From 745669ccbeb19c34daff866bf51c6535ade203bd Mon Sep 17 00:00:00 2001 From: "PFC(Ian)" Date: Wed, 15 Jun 2022 18:16:04 +1000 Subject: [PATCH 01/77] migrate fn. ability to handle multi-chain accept fees use eris-protocol's checked Decimal ops --- .gitignore | 2 +- Cargo.lock | 5 +- contracts/hub/Cargo.toml | 3 +- contracts/hub/src/contract.rs | 126 ++++++--- contracts/hub/src/execute.rs | 246 ++++++++++++------ contracts/hub/src/math.rs | 142 ++++++----- contracts/hub/src/queries.rs | 13 +- contracts/hub/src/state.rs | 15 +- contracts/hub/src/testing/tests.rs | 394 +++++++++++++++++++++-------- packages/steak/Cargo.toml | 8 +- packages/steak/src/hub.rs | 47 ++-- packages/steak/src/lib.rs | 61 +++++ 12 files changed, 737 insertions(+), 325 deletions(-) diff --git a/.gitignore b/.gitignore index 4b75762..31618ac 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,4 @@ scripts/data/ scripts/keys/ # Misc notes -notes.txt +/notes*.txt diff --git a/Cargo.lock b/Cargo.lock index b54699a..e4dcf74 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -567,7 +567,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "steak" -version = "2.0.0" +version = "2.1.0" dependencies = [ "cosmwasm-std", "cw20", @@ -577,10 +577,11 @@ dependencies = [ [[package]] name = "steak-hub" -version = "2.0.0" +version = "2.1.0" dependencies = [ "cosmwasm-std", "cw-storage-plus", + "cw2", "cw20", "cw20-base", "serde", diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index 84415c0..c0cf95c 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "steak-hub" -version = "2.0.0" +version = "2.1.0" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" @@ -14,6 +14,7 @@ backtraces = ["cosmwasm-std/backtraces"] [dependencies] cosmwasm-std = { version = "1.0", features = ["staking"] } +cw2="0.13" cw20 = "0.13" cw20-base = { version = "0.13", features = ["library"] } cw-storage-plus = "0.13" diff --git a/contracts/hub/src/contract.rs b/contracts/hub/src/contract.rs index 99c0943..236528d 100644 --- a/contracts/hub/src/contract.rs +++ b/contracts/hub/src/contract.rs @@ -1,14 +1,20 @@ use cosmwasm_std::{ - entry_point, from_binary, to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, - StdError, StdResult, + entry_point, from_binary, to_binary, Binary, Decimal, Deps, DepsMut, Env, MessageInfo, Reply, + Response, StdError, StdResult, }; use cw20::Cw20ReceiveMsg; use steak::hub::{CallbackMsg, ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, ReceiveMsg}; -use crate::helpers::{parse_received_fund, unwrap_reply}; +use crate::helpers::unwrap_reply; use crate::state::State; use crate::{execute, queries}; +use cw2::{get_contract_version, set_contract_version, ContractVersion}; + +/// Contract name that is used for migration. +const CONTRACT_NAME: &str = "steak-hub"; +/// Contract version that is used for migration. +const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); #[entry_point] pub fn instantiate( @@ -17,6 +23,7 @@ pub fn instantiate( _info: MessageInfo, msg: InstantiateMsg, ) -> StdResult { + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; execute::instantiate(deps, env, msg) } @@ -25,36 +32,42 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S let api = deps.api; match msg { ExecuteMsg::Receive(cw20_msg) => receive(deps, env, info, cw20_msg), - ExecuteMsg::Bond { - receiver, - } => execute::bond( + ExecuteMsg::Bond { receiver } => execute::bond( deps, env, - receiver.map(|s| api.addr_validate(&s)).transpose()?.unwrap_or(info.sender), - parse_received_fund(&info.funds, "uluna")?, + receiver + .map(|s| api.addr_validate(&s)) + .transpose()? + .unwrap_or(info.sender), + info.funds, ), - ExecuteMsg::WithdrawUnbonded { - receiver, - } => execute::withdraw_unbonded( + ExecuteMsg::WithdrawUnbonded { receiver } => execute::withdraw_unbonded( deps, env, info.sender.clone(), - receiver.map(|s| api.addr_validate(&s)).transpose()?.unwrap_or(info.sender), + receiver + .map(|s| api.addr_validate(&s)) + .transpose()? + .unwrap_or(info.sender), ), - ExecuteMsg::AddValidator { - validator, - } => execute::add_validator(deps, info.sender, validator), - ExecuteMsg::RemoveValidator { - validator, - } => execute::remove_validator(deps, env, info.sender, validator), - ExecuteMsg::TransferOwnership { - new_owner, - } => execute::transfer_ownership(deps, info.sender, new_owner), + ExecuteMsg::AddValidator { validator } => { + execute::add_validator(deps, info.sender, validator) + } + ExecuteMsg::RemoveValidator { validator } => { + execute::remove_validator(deps, env, info.sender, validator) + } + ExecuteMsg::TransferOwnership { new_owner } => { + execute::transfer_ownership(deps, info.sender, new_owner) + } ExecuteMsg::AcceptOwnership {} => execute::accept_ownership(deps, info.sender), ExecuteMsg::Harvest {} => execute::harvest(deps, env), ExecuteMsg::Rebalance {} => execute::rebalance(deps, env), ExecuteMsg::Reconcile {} => execute::reconcile(deps, env), ExecuteMsg::SubmitBatch {} => execute::submit_batch(deps, env), + ExecuteMsg::TransferFeeAccount { new_fee_account } => { + execute::transfer_fee_account(deps, info.sender, new_fee_account) + } + ExecuteMsg::UpdateFee { new_fee } => execute::update_fee(deps, info.sender, new_fee), ExecuteMsg::Callback(callback_msg) => callback(deps, env, info, callback_msg), } } @@ -67,16 +80,15 @@ fn receive( ) -> StdResult { let api = deps.api; match from_binary(&cw20_msg.msg)? { - ReceiveMsg::QueueUnbond { - receiver, - } => { + ReceiveMsg::QueueUnbond { receiver } => { let state = State::default(); let steak_token = state.steak_token.load(deps.storage)?; if info.sender != steak_token { - return Err(StdError::generic_err( - format!("expecting Steak token, received {}", info.sender), - )); + return Err(StdError::generic_err(format!( + "expecting Steak token, received {}", + info.sender + ))); } execute::queue_unbond( @@ -85,7 +97,7 @@ fn receive( api.addr_validate(&receiver.unwrap_or(cw20_msg.sender))?, cw20_msg.amount, ) - }, + } } } @@ -96,7 +108,9 @@ fn callback( callback_msg: CallbackMsg, ) -> StdResult { if env.contract.address != info.sender { - return Err(StdError::generic_err("callbacks can only be invoked by the contract itself")); + return Err(StdError::generic_err( + "callbacks can only be invoked by the contract itself", + )); } match callback_msg { @@ -109,7 +123,10 @@ pub fn reply(deps: DepsMut, env: Env, reply: Reply) -> StdResult { match reply.id { 1 => execute::register_steak_token(deps, unwrap_reply(reply)?), 2 => execute::register_received_coins(deps, env, unwrap_reply(reply)?.events), - id => Err(StdError::generic_err(format!("invalid reply id: {}; must be 1-2", id))), + id => Err(StdError::generic_err(format!( + "invalid reply id: {}; must be 1-2", + id + ))), } } @@ -120,24 +137,61 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { QueryMsg::State {} => to_binary(&queries::state(deps, env)?), QueryMsg::PendingBatch {} => to_binary(&queries::pending_batch(deps)?), QueryMsg::PreviousBatch(id) => to_binary(&queries::previous_batch(deps, id)?), - QueryMsg::PreviousBatches { + QueryMsg::PreviousBatches { start_after, limit } => { + to_binary(&queries::previous_batches(deps, start_after, limit)?) + } + QueryMsg::UnbondRequestsByBatch { + id, start_after, limit, - } => to_binary(&queries::previous_batches(deps, start_after, limit)?), - QueryMsg::UnbondRequestsByBatch { + } => to_binary(&queries::unbond_requests_by_batch( + deps, id, start_after, limit, - } => to_binary(&queries::unbond_requests_by_batch(deps, id, start_after, limit)?), + )?), QueryMsg::UnbondRequestsByUser { user, start_after, limit, - } => to_binary(&queries::unbond_requests_by_user(deps, user, start_after, limit)?), + } => to_binary(&queries::unbond_requests_by_user( + deps, + user, + start_after, + limit, + )?), } } #[entry_point] -pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult { +pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult { + let contract_version = match get_contract_version(deps.storage) { + Ok(version) => version, + Err(_) => ContractVersion { + contract: "steak-hub".to_string(), + version: "0".to_string(), + }, + }; + match contract_version.contract.as_ref() { + #[allow(clippy::single_match)] + "steak-hub" => match contract_version.version.as_ref() { + #[allow(clippy::single_match)] + "0" => { + let state = State::default(); + let owner = state.owner.load(deps.storage)?; + state.denom.save(deps.storage, &"uluna".to_string())?; + state.fee_account.save(deps.storage, &owner)?; + state.max_fee_rate.save(deps.storage, &Decimal::zero())?; + state.fee_rate.save(deps.storage, &Decimal::zero())?; + } + _ => {} + }, + _ => { + return Err(StdError::generic_err( + "contract name is not the same. aborting {}", + )) + } + } + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; Ok(Response::new()) } diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index 482e7ac..b36f219 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -1,15 +1,18 @@ use std::str::FromStr; use cosmwasm_std::{ - to_binary, Addr, BankMsg, Coin, CosmosMsg, DepsMut, DistributionMsg, Env, Event, Order, - Response, StdError, StdResult, SubMsg, SubMsgResponse, Uint128, WasmMsg, + to_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, DepsMut, DistributionMsg, Env, Event, + Order, Response, StdError, StdResult, SubMsg, SubMsgResponse, Uint128, WasmMsg, }; use cw20::{Cw20ExecuteMsg, MinterResponse}; use cw20_base::msg::InstantiateMsg as Cw20InstantiateMsg; use steak::hub::{Batch, CallbackMsg, ExecuteMsg, InstantiateMsg, PendingBatch, UnbondRequest}; +use steak::DecimalCheckedOps; -use crate::helpers::{query_cw20_total_supply, query_delegation, query_delegations}; +use crate::helpers::{ + parse_received_fund, query_cw20_total_supply, query_delegation, query_delegations, +}; use crate::math::{ compute_mint_amount, compute_redelegations_for_rebalancing, compute_redelegations_for_removal, compute_unbond_amount, compute_undelegations, reconcile_batches, @@ -24,11 +27,27 @@ use crate::types::{Coins, Delegation}; pub fn instantiate(deps: DepsMut, env: Env, msg: InstantiateMsg) -> StdResult { let state = State::default(); - state.owner.save(deps.storage, &deps.api.addr_validate(&msg.owner)?)?; + if msg.max_fee_amount > Decimal::from_str("1.00")? { + return Err(StdError::generic_err("Max fee can not exceed 1/100%")); + } + + if msg.fee_amount > msg.max_fee_amount { + return Err(StdError::generic_err("fee can not exceed max fee")); + } + + state + .owner + .save(deps.storage, &deps.api.addr_validate(&msg.owner)?)?; state.epoch_period.save(deps.storage, &msg.epoch_period)?; state.unbond_period.save(deps.storage, &msg.unbond_period)?; state.validators.save(deps.storage, &msg.validators)?; state.unlocked_coins.save(deps.storage, &vec![])?; + state.denom.save(deps.storage, &msg.denom)?; + state.max_fee_rate.save(deps.storage, &msg.max_fee_amount)?; + state.fee_rate.save(deps.storage, &msg.fee_amount)?; + state + .fee_account + .save(deps.storage, &deps.api.addr_validate(&msg.fee_account)?)?; state.pending_batch.save( deps.storage, @@ -95,13 +114,10 @@ pub fn register_steak_token(deps: DepsMut, response: SubMsgResponse) -> StdResul /// smallest amount of delegation. If delegations become severely unbalance as a result of this /// (e.g. when a single user makes a very big deposit), anyone can invoke `ExecuteMsg::Rebalance` /// to balance the delegations. -pub fn bond( - deps: DepsMut, - env: Env, - receiver: Addr, - uluna_to_bond: Uint128, -) -> StdResult { +pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec) -> StdResult { let state = State::default(); + let denom = state.denom.load(deps.storage)?; + let amount_to_bond = parse_received_fund(&funds, &denom)?; let steak_token = state.steak_token.load(deps.storage)?; let validators = state.validators.load(deps.storage)?; @@ -119,12 +135,12 @@ pub fn bond( } let new_delegation = Delegation { validator: validator.clone(), - amount: uluna_to_bond.u128(), + amount: amount_to_bond.u128(), }; // Query the current supply of Steak and compute the amount to mint let usteak_supply = query_cw20_total_supply(&deps.querier, &steak_token)?; - let usteak_to_mint = compute_mint_amount(usteak_supply, uluna_to_bond, &delegations); + let usteak_to_mint = compute_mint_amount(usteak_supply, amount_to_bond, &delegations); let delegate_submsg = SubMsg::reply_on_success(new_delegation.to_cosmos_msg(), 2); @@ -141,7 +157,8 @@ pub fn bond( .add_attribute("time", env.block.time.seconds().to_string()) .add_attribute("height", env.block.height.to_string()) .add_attribute("receiver", receiver) - .add_attribute("uluna_bonded", uluna_to_bond) + .add_attribute("denom_bonded", denom) + .add_attribute("denom_amount", amount_to_bond) .add_attribute("usteak_minted", usteak_to_mint); Ok(Response::new() @@ -175,20 +192,23 @@ pub fn harvest(deps: DepsMut, env: Env) -> StdResult { } /// NOTE: -/// 1. When delegation Luna here, we don't need to use a `SubMsg` to handle the received coins, +/// 1. When delegation Native denom here, we don't need to use a `SubMsg` to handle the received coins, /// because we have already withdrawn all claimable staking rewards previously in the same atomic /// execution. /// 2. Same as with `bond`, in the latest implementation we only delegate staking rewards with the /// validator that has the smallest delegation amount. pub fn reinvest(deps: DepsMut, env: Env) -> StdResult { let state = State::default(); + let denom = state.denom.load(deps.storage)?; + let fee = state.fee_rate.load(deps.storage)?; + let validators = state.validators.load(deps.storage)?; let mut unlocked_coins = state.unlocked_coins.load(deps.storage)?; - let uluna_to_bond = unlocked_coins + let amount_to_bond = unlocked_coins .iter() - .find(|coin| coin.denom == "uluna") - .ok_or_else(|| StdError::generic_err("no uluna available to be bonded"))? + .find(|coin| coin.denom == denom) + .ok_or_else(|| StdError::generic_err("no native amount available to be bonded"))? .amount; let delegations = query_delegations(&deps.querier, &validators, &env.contract.address)?; @@ -200,20 +220,43 @@ pub fn reinvest(deps: DepsMut, env: Env) -> StdResult { amount = d.amount; } } - let new_delegation = Delegation::new(validator, uluna_to_bond.u128()); + let fee_amount = if fee.is_zero() { + Uint128::zero() + } else { + fee.checked_mul_uint(amount_to_bond)? + }; + let amount_to_bond_minus_fees = amount_to_bond.saturating_sub(fee_amount); + + let new_delegation = Delegation::new(validator, amount_to_bond_minus_fees.u128()); - unlocked_coins.retain(|coin| coin.denom != "uluna"); + unlocked_coins.retain(|coin| coin.denom != denom); state.unlocked_coins.save(deps.storage, &unlocked_coins)?; let event = Event::new("steakhub/harvested") .add_attribute("time", env.block.time.seconds().to_string()) .add_attribute("height", env.block.height.to_string()) - .add_attribute("uluna_bonded", uluna_to_bond); - - Ok(Response::new() - .add_message(new_delegation.to_cosmos_msg()) - .add_event(event) - .add_attribute("action", "steakhub/reinvest")) + .add_attribute("denom", &denom) + .add_attribute("fees_deducted", fee_amount) + .add_attribute("denom_bonded", amount_to_bond_minus_fees); + + if fee_amount > Uint128::zero() { + let fee_account = state.fee_account.load(deps.storage)?; + + let send_msg = BankMsg::Send { + to_address: fee_account.to_string(), + amount: vec![Coin::new(fee_amount.into(), &denom)], + }; + Ok(Response::new() + .add_message(new_delegation.to_cosmos_msg()) + .add_message(CosmosMsg::Bank(send_msg)) + .add_event(event) + .add_attribute("action", "steakhub/reinvest")) + } else { + Ok(Response::new() + .add_message(new_delegation.to_cosmos_msg()) + .add_event(event) + .add_attribute("action", "steakhub/reinvest")) + } } /// NOTE: a `SubMsgResponse` may contain multiple coin-receiving events, must handle them individually @@ -233,14 +276,15 @@ pub fn register_received_coins( } let state = State::default(); - state.unlocked_coins.update(deps.storage, |coins| -> StdResult<_> { - let mut coins = Coins(coins); - coins.add_many(&received_coins)?; - Ok(coins.0) - })?; - - Ok(Response::new() - .add_attribute("action", "steakhub/register_received_coins")) + state + .unlocked_coins + .update(deps.storage, |coins| -> StdResult<_> { + let mut coins = Coins(coins); + coins.add_many(&received_coins)?; + Ok(coins.0) + })?; + + Ok(Response::new().add_attribute("action", "steakhub/register_received_coins")) } fn parse_coin_receiving_event(env: &Env, event: &Event) -> StdResult { @@ -328,21 +372,23 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { let current_time = env.block.time.seconds(); if current_time < pending_batch.est_unbond_start_time { - return Err(StdError::generic_err( - format!("batch can only be submitted for unbonding after {}", pending_batch.est_unbond_start_time), - )); + return Err(StdError::generic_err(format!( + "batch can only be submitted for unbonding after {}", + pending_batch.est_unbond_start_time + ))); } let delegations = query_delegations(&deps.querier, &validators, &env.contract.address)?; let usteak_supply = query_cw20_total_supply(&deps.querier, &steak_token)?; - let uluna_to_unbond = compute_unbond_amount(usteak_supply, pending_batch.usteak_to_burn, &delegations); - let new_undelegations = compute_undelegations(uluna_to_unbond, &delegations); + let amount_to_bond = + compute_unbond_amount(usteak_supply, pending_batch.usteak_to_burn, &delegations); + let new_undelegations = compute_undelegations(amount_to_bond, &delegations); - // NOTE: Regarding the `uluna_unclaimed` value + // NOTE: Regarding the `amount_unclaimed` value // // If validators misbehave and get slashed during the unbonding period, the contract can receive - // LESS Luna than `uluna_to_unbond` when unbonding finishes! + // LESS Luna than `amount_to_unbond` when unbonding finishes! // // In this case, users who invokes `withdraw_unbonded` will have their txs failed as the contract // does not have enough Luna balance. @@ -355,7 +401,7 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { id: pending_batch.id, reconciled: false, total_shares: pending_batch.usteak_to_burn, - uluna_unclaimed: uluna_to_unbond, + amount_unclaimed: amount_to_bond, est_unbond_end_time: current_time + unbond_period, }, )?; @@ -387,7 +433,7 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { .add_attribute("time", env.block.time.seconds().to_string()) .add_attribute("height", env.block.height.to_string()) .add_attribute("id", pending_batch.id.to_string()) - .add_attribute("uluna_unbonded", uluna_to_unbond) + .add_attribute("native_unbonded", amount_to_bond) .add_attribute("usteak_burned", pending_batch.usteak_to_burn); Ok(Response::new() @@ -419,20 +465,23 @@ pub fn reconcile(deps: DepsMut, env: Env) -> StdResult { .filter(|b| current_time > b.est_unbond_end_time) .collect::>(); - let uluna_expected_received: Uint128 = batches - .iter() - .map(|b| b.uluna_unclaimed) - .sum(); - + let native_expected_received: Uint128 = batches.iter().map(|b| b.amount_unclaimed).sum(); + let denom = state.denom.load(deps.storage)?; let unlocked_coins = state.unlocked_coins.load(deps.storage)?; - let uluna_expected_unlocked = Coins(unlocked_coins).find("uluna").amount; - let uluna_expected = uluna_expected_received + uluna_expected_unlocked; - let uluna_actual = deps.querier.query_balance(&env.contract.address, "uluna")?.amount; + let native_expected_unlocked = Coins(unlocked_coins).find(&denom).amount; + + let native_expected = native_expected_received + native_expected_unlocked; + let native_actual = deps + .querier + .query_balance(&env.contract.address, &denom)? + .amount; - let uluna_to_deduct = uluna_expected.checked_sub(uluna_actual).unwrap_or_else(|_| Uint128::zero()); - if !uluna_to_deduct.is_zero() { - reconcile_batches(&mut batches, uluna_expected - uluna_actual); + let native_to_deduct = native_expected + .checked_sub(native_actual) + .unwrap_or_else(|_| Uint128::zero()); + if !native_to_deduct.is_zero() { + reconcile_batches(&mut batches, native_expected - native_actual); } for batch in &batches { @@ -447,7 +496,7 @@ pub fn reconcile(deps: DepsMut, env: Env) -> StdResult { let event = Event::new("steakhub/reconciled") .add_attribute("ids", ids) - .add_attribute("uluna_deducted", uluna_to_deduct.to_string()); + .add_attribute("native_deducted", native_to_deduct.to_string()); Ok(Response::new() .add_event(event) @@ -461,6 +510,7 @@ pub fn withdraw_unbonded( receiver: Addr, ) -> StdResult { let state = State::default(); + let denom = state.denom.load(deps.storage)?; let current_time = env.block.time.seconds(); // NOTE: If the user has too many unclaimed requests, this may not fit in the WASM memory... @@ -478,45 +528,49 @@ pub fn withdraw_unbonded( }) .collect::>>()?; - // NOTE: Luna in the following batches are withdrawn it the batch: + // NOTE: Native in the following batches are withdrawn it the batch: // - is a _previous_ batch, not a _pending_ batch // - is reconciled // - has finished unbonding // If not sure whether the batches have been reconciled, the user should first invoke `ExecuteMsg::Reconcile` // before withdrawing. - let mut total_uluna_to_refund = Uint128::zero(); + let mut total_native_to_refund = Uint128::zero(); let mut ids: Vec = vec![]; for request in &requests { if let Ok(mut batch) = state.previous_batches.load(deps.storage, request.id) { if batch.reconciled && batch.est_unbond_end_time < current_time { - let uluna_to_refund = batch - .uluna_unclaimed + let native_to_refund = batch + .amount_unclaimed .multiply_ratio(request.shares, batch.total_shares); ids.push(request.id.to_string()); - total_uluna_to_refund += uluna_to_refund; + total_native_to_refund += native_to_refund; batch.total_shares -= request.shares; - batch.uluna_unclaimed -= uluna_to_refund; + batch.amount_unclaimed -= native_to_refund; if batch.total_shares.is_zero() { state.previous_batches.remove(deps.storage, request.id)?; } else { - state.previous_batches.save(deps.storage, batch.id, &batch)?; + state + .previous_batches + .save(deps.storage, batch.id, &batch)?; } - state.unbond_requests.remove(deps.storage, (request.id, &user))?; + state + .unbond_requests + .remove(deps.storage, (request.id, &user))?; } } } - if total_uluna_to_refund.is_zero() { + if total_native_to_refund.is_zero() { return Err(StdError::generic_err("withdrawable amount is zero")); } let refund_msg = CosmosMsg::Bank(BankMsg::Send { to_address: receiver.clone().into(), - amount: vec![Coin::new(total_uluna_to_refund.u128(), "uluna")], + amount: vec![Coin::new(total_native_to_refund.u128(), &denom)], }); let event = Event::new("steakhub/unbonded_withdrawn") @@ -525,7 +579,7 @@ pub fn withdraw_unbonded( .add_attribute("ids", ids.join(",")) .add_attribute("user", user) .add_attribute("receiver", receiver) - .add_attribute("uluna_refunded", total_uluna_to_refund); + .add_attribute("amount_refunded", total_native_to_refund); Ok(Response::new() .add_message(refund_msg) @@ -552,8 +606,7 @@ pub fn rebalance(deps: DepsMut, env: Env) -> StdResult { let amount: u128 = new_redelegations.iter().map(|rd| rd.amount).sum(); - let event = Event::new("steakhub/rebalanced") - .add_attribute("uluna_moved", amount.to_string()); + let event = Event::new("steakhub/rebalanced").add_attribute("amount_moved", amount.to_string()); Ok(Response::new() .add_submessages(redelegate_submsgs) @@ -574,8 +627,7 @@ pub fn add_validator(deps: DepsMut, sender: Addr, validator: String) -> StdResul Ok(validators) })?; - let event = Event::new("steakhub/validator_added") - .add_attribute("validator", validator); + let event = Event::new("steakhub/validator_added").add_attribute("validator", validator); Ok(Response::new() .add_event(event) @@ -594,7 +646,9 @@ pub fn remove_validator( let validators = state.validators.update(deps.storage, |mut validators| { if !validators.contains(&validator) { - return Err(StdError::generic_err("validator is not already whitelisted")); + return Err(StdError::generic_err( + "validator is not already whitelisted", + )); } validators.retain(|v| *v != validator); Ok(validators) @@ -609,8 +663,7 @@ pub fn remove_validator( .map(|d| SubMsg::reply_on_success(d.to_cosmos_msg(), 2)) .collect::>(); - let event = Event::new("steak/validator_removed") - .add_attribute("validator", validator); + let event = Event::new("steak/validator_removed").add_attribute("validator", validator); Ok(Response::new() .add_submessages(redelegate_submsgs) @@ -622,10 +675,11 @@ pub fn transfer_ownership(deps: DepsMut, sender: Addr, new_owner: String) -> Std let state = State::default(); state.assert_owner(deps.storage, &sender)?; - state.new_owner.save(deps.storage, &deps.api.addr_validate(&new_owner)?)?; + state + .new_owner + .save(deps.storage, &deps.api.addr_validate(&new_owner)?)?; - Ok(Response::new() - .add_attribute("action", "steakhub/transfer_ownership")) + Ok(Response::new().add_attribute("action", "steakhub/transfer_ownership")) } pub fn accept_ownership(deps: DepsMut, sender: Addr) -> StdResult { @@ -635,7 +689,9 @@ pub fn accept_ownership(deps: DepsMut, sender: Addr) -> StdResult { let new_owner = state.new_owner.load(deps.storage)?; if sender != new_owner { - return Err(StdError::generic_err("unauthorized: sender is not new owner")); + return Err(StdError::generic_err( + "unauthorized: sender is not new owner", + )); } state.owner.save(deps.storage, &sender)?; @@ -649,3 +705,41 @@ pub fn accept_ownership(deps: DepsMut, sender: Addr) -> StdResult { .add_event(event) .add_attribute("action", "steakhub/transfer_ownership")) } + +pub fn transfer_fee_account( + deps: DepsMut, + sender: Addr, + new_fee_account: String, +) -> StdResult { + let state = State::default(); + + state.assert_owner(deps.storage, &sender)?; + state + .fee_account + .save(deps.storage, &deps.api.addr_validate(&new_fee_account)?)?; + + Ok(Response::new().add_attribute("action", "steakhub/transfer_fee_account")) +} + +pub fn change_denom(deps: DepsMut, sender: Addr, new_denom: String) -> StdResult { + let state = State::default(); + + state.assert_owner(deps.storage, &sender)?; + state.denom.save(deps.storage, &new_denom)?; + + Ok(Response::new().add_attribute("action", "steakhub/change_denom")) +} + +pub fn update_fee(deps: DepsMut, sender: Addr, new_fee: Decimal) -> StdResult { + let state = State::default(); + + state.assert_owner(deps.storage, &sender)?; + if new_fee > state.max_fee_rate.load(deps.storage)? { + return Err(StdError::generic_err( + "refusing to set fee above maximum set", + )); + } + state.fee_rate.save(deps.storage, &new_fee)?; + + Ok(Response::new().add_attribute("action", "steakhub/update_fee")) +} diff --git a/contracts/hub/src/math.rs b/contracts/hub/src/math.rs index 83c29a5..4408902 100644 --- a/contracts/hub/src/math.rs +++ b/contracts/hub/src/math.rs @@ -11,22 +11,22 @@ use crate::types::{Delegation, Redelegation, Undelegation}; //-------------------------------------------------------------------------------------------------- /// Compute the amount of Steak token to mint for a specific Luna stake amount. If current total -/// staked amount is zero, we use 1 usteak = 1 uluna; otherwise, we calculate base on the current -/// uluna per ustake ratio. +/// staked amount is zero, we use 1 usteak = 1 native; otherwise, we calculate base on the current +/// native per ustake ratio. pub(crate) fn compute_mint_amount( usteak_supply: Uint128, - uluna_to_bond: Uint128, + native_to_bond: Uint128, current_delegations: &[Delegation], ) -> Uint128 { - let uluna_bonded: u128 = current_delegations.iter().map(|d| d.amount).sum(); - if uluna_bonded == 0 { - uluna_to_bond + let native_bonded: u128 = current_delegations.iter().map(|d| d.amount).sum(); + if native_bonded == 0 { + native_to_bond } else { - usteak_supply.multiply_ratio(uluna_to_bond, uluna_bonded) + usteak_supply.multiply_ratio(native_to_bond, native_bonded) } } -/// Compute the amount of `uluna` to unbond for a specific `usteak` burn amount +/// Compute the amount of `native` to unbond for a specific `usteak` burn amount /// /// There is no way `usteak` total supply is zero when the user is senting a non-zero amount of `usteak` /// to burn, so we don't need to handle division-by-zero here @@ -35,53 +35,51 @@ pub(crate) fn compute_unbond_amount( usteak_to_burn: Uint128, current_delegations: &[Delegation], ) -> Uint128 { - let uluna_bonded: u128 = current_delegations.iter().map(|d| d.amount).sum(); - Uint128::new(uluna_bonded).multiply_ratio(usteak_to_burn, usteak_supply) + let native_bonded: u128 = current_delegations.iter().map(|d| d.amount).sum(); + Uint128::new(native_bonded).multiply_ratio(usteak_to_burn, usteak_supply) } //-------------------------------------------------------------------------------------------------- // Delegation logics //-------------------------------------------------------------------------------------------------- -/// Given the current delegations made to validators, and a specific amount of `uluna` to unstake, +/// Given the current delegations made to validators, and a specific amount of `native` to unstake, /// compute the undelegations to make such that the delegated amount to each validator is as even /// as possible. /// /// This function is based on Lido's implementation: /// https://github.com/lidofinance/lido-terra-contracts/blob/v1.0.2/contracts/lido_terra_validators_registry/src/common.rs#L55-102 pub(crate) fn compute_undelegations( - uluna_to_unbond: Uint128, + native_to_unbond: Uint128, current_delegations: &[Delegation], ) -> Vec { - let uluna_staked: u128 = current_delegations.iter().map(|d| d.amount).sum(); + let native_staked: u128 = current_delegations.iter().map(|d| d.amount).sum(); let validator_count = current_delegations.len() as u128; - let uluna_to_distribute = uluna_staked - uluna_to_unbond.u128(); - let uluna_per_validator = uluna_to_distribute / validator_count; - let remainder = uluna_to_distribute % validator_count; + let native_to_distribute = native_staked - native_to_unbond.u128(); + let native_per_validator = native_to_distribute / validator_count; + let remainder = native_to_distribute % validator_count; let mut new_undelegations: Vec = vec![]; - let mut uluna_available = uluna_to_unbond.u128(); + let mut native_available = native_to_unbond.u128(); for (i, d) in current_delegations.iter().enumerate() { let remainder_for_validator: u128 = if (i + 1) as u128 <= remainder { 1 } else { 0 }; - let uluna_for_validator = uluna_per_validator + remainder_for_validator; + let native_for_validator = native_per_validator + remainder_for_validator; - let mut uluna_to_undelegate = if d.amount < uluna_for_validator { + let mut native_to_undelegate = if d.amount < native_for_validator { 0 } else { - d.amount - uluna_for_validator + d.amount - native_for_validator }; - uluna_to_undelegate = std::cmp::min(uluna_to_undelegate, uluna_available); - uluna_available -= uluna_to_undelegate; + native_to_undelegate = cmp::min(native_to_undelegate, native_available); + native_available -= native_to_undelegate; - if uluna_to_undelegate > 0 { - new_undelegations.push( - Undelegation::new(&d.validator, uluna_to_undelegate), - ); + if native_to_undelegate > 0 { + new_undelegations.push(Undelegation::new(&d.validator, native_to_undelegate)); } - if uluna_available == 0 { + if native_available == 0 { break; } } @@ -99,35 +97,37 @@ pub(crate) fn compute_redelegations_for_removal( delegation_to_remove: &Delegation, current_delegations: &[Delegation], ) -> Vec { - let uluna_staked: u128 = current_delegations.iter().map(|d| d.amount).sum(); + let native_staked: u128 = current_delegations.iter().map(|d| d.amount).sum(); let validator_count = current_delegations.len() as u128; - let uluna_to_distribute = uluna_staked + delegation_to_remove.amount; - let uluna_per_validator = uluna_to_distribute / validator_count; - let remainder = uluna_to_distribute % validator_count; + let native_to_distribute = native_staked + delegation_to_remove.amount; + let native_per_validator = native_to_distribute / validator_count; + let remainder = native_to_distribute % validator_count; let mut new_redelegations: Vec = vec![]; - let mut uluna_available = delegation_to_remove.amount; + let mut native_available = delegation_to_remove.amount; for (i, d) in current_delegations.iter().enumerate() { let remainder_for_validator: u128 = if (i + 1) as u128 <= remainder { 1 } else { 0 }; - let uluna_for_validator = uluna_per_validator + remainder_for_validator; + let native_for_validator = native_per_validator + remainder_for_validator; - let mut uluna_to_redelegate = if d.amount > uluna_for_validator { + let mut native_to_redelegate = if d.amount > native_for_validator { 0 } else { - uluna_for_validator - d.amount + native_for_validator - d.amount }; - uluna_to_redelegate = std::cmp::min(uluna_to_redelegate, uluna_available); - uluna_available -= uluna_to_redelegate; + native_to_redelegate = cmp::min(native_to_redelegate, native_available); + native_available -= native_to_redelegate; - if uluna_to_redelegate > 0 { - new_redelegations.push( - Redelegation::new(&delegation_to_remove.validator, &d.validator, uluna_to_redelegate), - ); + if native_to_redelegate > 0 { + new_redelegations.push(Redelegation::new( + &delegation_to_remove.validator, + &d.validator, + native_to_redelegate, + )); } - if uluna_available == 0 { + if native_available == 0 { break; } } @@ -142,29 +142,35 @@ pub(crate) fn compute_redelegations_for_removal( pub(crate) fn compute_redelegations_for_rebalancing( current_delegations: &[Delegation], ) -> Vec { - let uluna_staked: u128 = current_delegations.iter().map(|d| d.amount).sum(); + let native_staked: u128 = current_delegations.iter().map(|d| d.amount).sum(); let validator_count = current_delegations.len() as u128; - let uluna_per_validator = uluna_staked / validator_count; - let remainder = uluna_staked % validator_count; + let native_per_validator = native_staked / validator_count; + let remainder = native_staked % validator_count; - // If a validator's current delegated amount is greater than the target amount, Luna will be + // If a validator's current delegated amount is greater than the target amount, native will be // redelegated _from_ them. They will be put in `src_validators` vector - // If a validator's current delegated amount is smaller than the target amount, Luna will be + // If a validator's current delegated amount is smaller than the target amount, native will be // redelegated _to_ them. They will be put in `dst_validators` vector let mut src_delegations: Vec = vec![]; let mut dst_delegations: Vec = vec![]; for (i, d) in current_delegations.iter().enumerate() { let remainder_for_validator: u128 = if (i + 1) as u128 <= remainder { 1 } else { 0 }; - let uluna_for_validator = uluna_per_validator + remainder_for_validator; + let native_for_validator = native_per_validator + remainder_for_validator; - match d.amount.cmp(&uluna_for_validator) { + match d.amount.cmp(&native_for_validator) { Ordering::Greater => { - src_delegations.push(Delegation::new(&d.validator, d.amount - uluna_for_validator)); - }, + src_delegations.push(Delegation::new( + &d.validator, + d.amount - native_for_validator, + )); + } Ordering::Less => { - dst_delegations.push(Delegation::new(&d.validator, uluna_for_validator - d.amount)); - }, + dst_delegations.push(Delegation::new( + &d.validator, + native_for_validator - d.amount, + )); + } Ordering::Equal => (), } } @@ -173,23 +179,25 @@ pub(crate) fn compute_redelegations_for_rebalancing( while !src_delegations.is_empty() && !dst_delegations.is_empty() { let src_delegation = src_delegations[0].clone(); let dst_delegation = dst_delegations[0].clone(); - let uluna_to_redelegate = cmp::min(src_delegation.amount, dst_delegation.amount); + let native_to_redelegate = cmp::min(src_delegation.amount, dst_delegation.amount); - if src_delegation.amount == uluna_to_redelegate { + if src_delegation.amount == native_to_redelegate { src_delegations.remove(0); } else { - src_delegations[0].amount -= uluna_to_redelegate; + src_delegations[0].amount -= native_to_redelegate; } - if dst_delegation.amount == uluna_to_redelegate { + if dst_delegation.amount == native_to_redelegate { dst_delegations.remove(0); } else { - dst_delegations[0].amount -= uluna_to_redelegate; + dst_delegations[0].amount -= native_to_redelegate; } - new_redelegations.push( - Redelegation::new(&src_delegation.validator, &dst_delegation.validator, uluna_to_redelegate), - ); + new_redelegations.push(Redelegation::new( + &src_delegation.validator, + &dst_delegation.validator, + native_to_redelegate, + )); } new_redelegations @@ -199,22 +207,22 @@ pub(crate) fn compute_redelegations_for_rebalancing( // Batch logics //-------------------------------------------------------------------------------------------------- -/// If the received uluna amount after the unbonding period is less than expected, e.g. due to rounding +/// If the received native amount after the unbonding period is less than expected, e.g. due to rounding /// error or the validator(s) being slashed, then deduct the difference in amount evenly from each /// unreconciled batch. /// /// The idea of "reconciling" is based on Stader's implementation: /// https://github.com/stader-labs/stader-liquid-token/blob/v0.2.1/contracts/staking/src/contract.rs#L968-L1048 -pub(crate) fn reconcile_batches(batches: &mut [Batch], uluna_to_deduct: Uint128) { +pub(crate) fn reconcile_batches(batches: &mut [Batch], native_to_deduct: Uint128) { let batch_count = batches.len() as u128; - let uluna_per_batch = uluna_to_deduct.u128() / batch_count; - let remainder = uluna_to_deduct.u128() % batch_count; + let native_per_batch = native_to_deduct.u128() / batch_count; + let remainder = native_to_deduct.u128() % batch_count; for (i, batch) in batches.iter_mut().enumerate() { let remainder_for_batch: u128 = if (i + 1) as u128 <= remainder { 1 } else { 0 }; - let uluna_for_batch = uluna_per_batch + remainder_for_batch; + let native_for_batch = native_per_batch + remainder_for_batch; - batch.uluna_unclaimed -= Uint128::new(uluna_for_batch); + batch.amount_unclaimed -= Uint128::new(native_for_batch); batch.reconciled = true; } } diff --git a/contracts/hub/src/queries.rs b/contracts/hub/src/queries.rs index 55f73de..005d4de 100644 --- a/contracts/hub/src/queries.rs +++ b/contracts/hub/src/queries.rs @@ -16,7 +16,10 @@ pub fn config(deps: Deps) -> StdResult { let state = State::default(); Ok(ConfigResponse { owner: state.owner.load(deps.storage)?.into(), - new_owner: state.new_owner.may_load(deps.storage)?.map(|addr| addr.into()), + new_owner: state + .new_owner + .may_load(deps.storage)? + .map(|addr| addr.into()), steak_token: state.steak_token.load(deps.storage)?.into(), epoch_period: state.epoch_period.load(deps.storage)?, unbond_period: state.unbond_period.load(deps.storage)?, @@ -32,17 +35,17 @@ pub fn state(deps: Deps, env: Env) -> StdResult { let validators = state.validators.load(deps.storage)?; let delegations = query_delegations(&deps.querier, &validators, &env.contract.address)?; - let total_uluna: u128 = delegations.iter().map(|d| d.amount).sum(); + let total_native: u128 = delegations.iter().map(|d| d.amount).sum(); let exchange_rate = if total_usteak.is_zero() { Decimal::one() } else { - Decimal::from_ratio(total_uluna, total_usteak) + Decimal::from_ratio(total_native, total_usteak) }; Ok(StateResponse { total_usteak, - total_uluna: Uint128::new(total_uluna), + total_native: Uint128::new(total_native), exchange_rate, unlocked_coins: state.unlocked_coins.load(deps.storage)?, }) @@ -93,7 +96,7 @@ pub fn unbond_requests_by_batch( Some(addr_str) => { addr = deps.api.addr_validate(&addr_str)?; Some(Bound::exclusive(&addr)) - }, + } }; let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize; diff --git a/contracts/hub/src/state.rs b/contracts/hub/src/state.rs index 8e251a5..8f88f78 100644 --- a/contracts/hub/src/state.rs +++ b/contracts/hub/src/state.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{Addr, Coin, StdError, StdResult, Storage}; +use cosmwasm_std::{Addr, Coin, Decimal, StdError, StdResult, Storage}; use cw_storage_plus::{Index, IndexList, IndexedMap, Item, MultiIndex}; use steak::hub::{Batch, PendingBatch, UnbondRequest}; @@ -10,6 +10,14 @@ pub(crate) struct State<'a> { pub owner: Item<'a, Addr>, /// Pending ownership transfer, awaiting acceptance by the new owner pub new_owner: Item<'a, Addr>, + /// Account to send fees to + pub fee_account: Item<'a, Addr>, + /// Current fee rate + pub fee_rate: Item<'a, Decimal>, + /// Maximum fee rate + pub max_fee_rate: Item<'a, Decimal>, + /// denom to accept + pub denom: Item<'a, String>, /// Address of the Steak token pub steak_token: Item<'a, Addr>, /// How often the unbonding queue is to be executed @@ -22,6 +30,7 @@ pub(crate) struct State<'a> { pub unlocked_coins: Item<'a, Vec>, /// The current batch of unbonding requests queded to be executed pub pending_batch: Item<'a, PendingBatch>, + /// Previous batches that have started unbonding but not yet finished pub previous_batches: IndexedMap<'a, u64, Batch, PreviousBatchesIndexes<'a>>, /// Users' shares in unbonding batches @@ -47,6 +56,10 @@ impl Default for State<'static> { Self { owner: Item::new("owner"), new_owner: Item::new("new_owner"), + fee_account: Item::new("fee_account"), + fee_rate: Item::new("fee_rate"), + max_fee_rate: Item::new("max_fee_rate"), + denom: Item::new("denom"), steak_token: Item::new("steak_token"), epoch_period: Item::new("epoch_period"), unbond_period: Item::new("unbond_period"), diff --git a/contracts/hub/src/testing/tests.rs b/contracts/hub/src/testing/tests.rs index dc1824a..07eeeeb 100644 --- a/contracts/hub/src/testing/tests.rs +++ b/contracts/hub/src/testing/tests.rs @@ -41,10 +41,18 @@ fn setup_test() -> OwnedDeps { owner: "larry".to_string(), name: "Steak Token".to_string(), symbol: "STEAK".to_string(), + denom: "uxyz".to_string(), + fee_account: "the_fee_man".to_string(), + fee_amount: Decimal::from_ratio(10_u128, 100_u128), //10% + max_fee_amount: Decimal::from_ratio(20_u128, 100_u128), //20% decimals: 6, epoch_period: 259200, // 3 * 24 * 60 * 60 = 3 days unbond_period: 1814400, // 21 * 24 * 60 * 60 = 21 days - validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string()], + validators: vec![ + "alice".to_string(), + "bob".to_string(), + "charlie".to_string(), + ], }, ) .unwrap(); @@ -115,7 +123,11 @@ fn proper_instantiation() { steak_token: "steak_token".to_string(), epoch_period: 259200, unbond_period: 1814400, - validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string()] + validators: vec![ + "alice".to_string(), + "bob".to_string(), + "charlie".to_string() + ] } ); @@ -124,7 +136,7 @@ fn proper_instantiation() { res, StateResponse { total_usteak: Uint128::zero(), - total_uluna: Uint128::zero(), + total_native: Uint128::zero(), exchange_rate: Decimal::one(), unlocked_coins: vec![], }, @@ -150,10 +162,8 @@ fn bonding() { let res = execute( deps.as_mut(), mock_env(), - mock_info("user_1", &[Coin::new(1000000, "uluna")]), - ExecuteMsg::Bond { - receiver: None, - }, + mock_info("user_1", &[Coin::new(1000000, "uxyz")]), + ExecuteMsg::Bond { receiver: None }, ) .unwrap(); @@ -193,7 +203,7 @@ fn bonding() { let res = execute( deps.as_mut(), mock_env(), - mock_info("user_2", &[Coin::new(12345, "uluna")]), + mock_info("user_2", &[Coin::new(12345, "uxyz")]), ExecuteMsg::Bond { receiver: Some("user_3".to_string()), }, @@ -236,7 +246,7 @@ fn bonding() { res, StateResponse { total_usteak: Uint128::new(1012043), - total_uluna: Uint128::new(1037345), + total_native: Uint128::new(1037345), exchange_rate: Decimal::from_ratio(1037345u128, 1012043u128), unlocked_coins: vec![], } @@ -259,7 +269,7 @@ fn harvesting() { deps.as_mut(), mock_env(), mock_info("worker", &[]), - ExecuteMsg::Harvest {} + ExecuteMsg::Harvest {}, ) .unwrap(); @@ -314,7 +324,7 @@ fn registering_unlocked_coins() { // After withdrawing staking rewards, we parse the `coin_received` event to find the received amounts let event = Event::new("coin_received") .add_attribute("receiver", MOCK_CONTRACT_ADDR.to_string()) - .add_attribute("amount", "123ukrw,234uluna,345uusd,69420ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B"); + .add_attribute("amount", "123ukrw,234uxyz,345uusd,69420ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B"); reply( deps.as_mut(), @@ -335,9 +345,12 @@ fn registering_unlocked_coins() { unlocked_coins, vec![ Coin::new(123, "ukrw"), - Coin::new(234, "uluna"), + Coin::new(234, "uxyz"), Coin::new(345, "uusd"), - Coin::new(69420, "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B"), + Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B" + ), ] ); } @@ -359,8 +372,11 @@ fn reinvesting() { .save( deps.as_mut().storage, &vec![ - Coin::new(234, "uluna"), - Coin::new(69420, "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B"), + Coin::new(234, "uxyz"), + Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + ), ], ) .unwrap(); @@ -374,12 +390,25 @@ fn reinvesting() { ) .unwrap(); - assert_eq!(res.messages.len(), 1); + assert_eq!(res.messages.len(), 2); assert_eq!( res.messages[0], SubMsg { id: 0, - msg: Delegation::new("bob", 234).to_cosmos_msg(), + msg: Delegation::new("bob", 234 - 23).to_cosmos_msg(), + gas_limit: None, + reply_on: ReplyOn::Never + } + ); + let send_msg = BankMsg::Send { + to_address: "the_fee_man".into(), + amount: vec![Coin::new(23u128, "uxyz")], + }; + assert_eq!( + res.messages[1], + SubMsg { + id: 0, + msg: CosmosMsg::Bank(send_msg), gas_limit: None, reply_on: ReplyOn::Never } @@ -389,7 +418,10 @@ fn reinvesting() { let unlocked_coins = state.unlocked_coins.load(deps.as_ref().storage).unwrap(); assert_eq!( unlocked_coins, - vec![Coin::new(69420, "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B")], + vec![Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B" + )], ); } @@ -406,15 +438,15 @@ fn queuing_unbond() { ExecuteMsg::Receive(cw20::Cw20ReceiveMsg { sender: "hacker".to_string(), amount: Uint128::new(69420), - msg: to_binary(&ReceiveMsg::QueueUnbond { - receiver: None, - }) - .unwrap(), + msg: to_binary(&ReceiveMsg::QueueUnbond { receiver: None }).unwrap(), }), ) .unwrap_err(); - assert_eq!(err, StdError::generic_err("expecting Steak token, received random_token")); + assert_eq!( + err, + StdError::generic_err("expecting Steak token, received random_token") + ); // User 1 creates an unbonding request before `est_unbond_start_time` is reached. The unbond // request is saved, but not the pending batch is not submitted for unbonding @@ -425,10 +457,7 @@ fn queuing_unbond() { ExecuteMsg::Receive(cw20::Cw20ReceiveMsg { sender: "user_1".to_string(), amount: Uint128::new(23456), - msg: to_binary(&ReceiveMsg::QueueUnbond { - receiver: None, - }) - .unwrap(), + msg: to_binary(&ReceiveMsg::QueueUnbond { receiver: None }).unwrap(), }), ) .unwrap(); @@ -470,11 +499,17 @@ fn queuing_unbond() { // The users' unbonding requests should have been saved let ubr1 = state .unbond_requests - .load(deps.as_ref().storage, (1u64.into(), &Addr::unchecked("user_1"))) + .load( + deps.as_ref().storage, + (1u64.into(), &Addr::unchecked("user_1")), + ) .unwrap(); let ubr2 = state .unbond_requests - .load(deps.as_ref().storage, (1u64.into(), &Addr::unchecked("user_3"))) + .load( + deps.as_ref().storage, + (1u64.into(), &Addr::unchecked("user_3")), + ) .unwrap(); assert_eq!( @@ -540,7 +575,10 @@ fn submitting_batch() { .unbond_requests .save( deps.as_mut().storage, - (unbond_request.id.into(), &Addr::unchecked(unbond_request.user.clone())), + ( + unbond_request.id.into(), + &Addr::unchecked(unbond_request.user.clone()), + ), unbond_request, ) .unwrap(); @@ -619,14 +657,17 @@ fn submitting_batch() { ); // Previous batch should have been updated - let previous_batch = state.previous_batches.load(deps.as_ref().storage, 1u64.into()).unwrap(); + let previous_batch = state + .previous_batches + .load(deps.as_ref().storage, 1u64.into()) + .unwrap(); assert_eq!( previous_batch, Batch { id: 1, reconciled: false, total_shares: Uint128::new(92876), - uluna_unclaimed: Uint128::new(95197), + amount_unclaimed: Uint128::new(95197), est_unbond_end_time: 2083601 // 269,201 + 1,814,400 } ); @@ -642,36 +683,40 @@ fn reconciling() { id: 1, reconciled: true, total_shares: Uint128::new(92876), - uluna_unclaimed: Uint128::new(95197), // 1.025 Luna per Steak + amount_unclaimed: Uint128::new(95197), // 1.025 Luna per Steak est_unbond_end_time: 10000, }, Batch { id: 2, reconciled: false, total_shares: Uint128::new(1345), - uluna_unclaimed: Uint128::new(1385), // 1.030 Luna per Steak + amount_unclaimed: Uint128::new(1385), // 1.030 Luna per Steak est_unbond_end_time: 20000, }, Batch { id: 3, reconciled: false, total_shares: Uint128::new(1456), - uluna_unclaimed: Uint128::new(1506), // 1.035 Luna per Steak + amount_unclaimed: Uint128::new(1506), // 1.035 Luna per Steak est_unbond_end_time: 30000, }, Batch { id: 4, reconciled: false, total_shares: Uint128::new(1567), - uluna_unclaimed: Uint128::new(1629), // 1.040 Luna per Steak - est_unbond_end_time: 40000, // not yet finished unbonding, ignored + amount_unclaimed: Uint128::new(1629), // 1.040 Luna per Steak + est_unbond_end_time: 40000, // not yet finished unbonding, ignored }, ]; for previous_batch in &previous_batches { state .previous_batches - .save(deps.as_mut().storage, previous_batch.id.into(), previous_batch) + .save( + deps.as_mut().storage, + previous_batch.id.into(), + previous_batch, + ) .unwrap(); } @@ -680,19 +725,25 @@ fn reconciling() { .save( deps.as_mut().storage, &vec![ - Coin::new(10000, "uluna"), + Coin::new(10000, "uxyz"), Coin::new(234, "ukrw"), Coin::new(345, "uusd"), - Coin::new(69420, "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B"), + Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + ), ], ) .unwrap(); deps.querier.set_bank_balances(&[ - Coin::new(12345, "uluna"), + Coin::new(12345, "uxyz"), Coin::new(234, "ukrw"), Coin::new(345, "uusd"), - Coin::new(69420, "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B"), + Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + ), ]); execute( @@ -713,35 +764,47 @@ fn reconciling() { // remainder: 0 // batch 2: 1385 - 273 = 1112 // batch 3: 1506 - 273 = 1233 - let batch = state.previous_batches.load(deps.as_ref().storage, 2u64.into()).unwrap(); + let batch = state + .previous_batches + .load(deps.as_ref().storage, 2u64.into()) + .unwrap(); assert_eq!( batch, Batch { id: 2, reconciled: true, total_shares: Uint128::new(1345), - uluna_unclaimed: Uint128::new(1112), // 1385 - 273 + amount_unclaimed: Uint128::new(1112), // 1385 - 273 est_unbond_end_time: 20000, } ); - let batch = state.previous_batches.load(deps.as_ref().storage, 3u64.into()).unwrap(); + let batch = state + .previous_batches + .load(deps.as_ref().storage, 3u64.into()) + .unwrap(); assert_eq!( batch, Batch { id: 3, reconciled: true, total_shares: Uint128::new(1456), - uluna_unclaimed: Uint128::new(1233), // 1506 - 273 + amount_unclaimed: Uint128::new(1233), // 1506 - 273 est_unbond_end_time: 30000, } ); // Batches 1 and 4 should not have changed - let batch = state.previous_batches.load(deps.as_ref().storage, 1u64.into()).unwrap(); + let batch = state + .previous_batches + .load(deps.as_ref().storage, 1u64.into()) + .unwrap(); assert_eq!(batch, previous_batches[0]); - let batch = state.previous_batches.load(deps.as_ref().storage, 4u64.into()).unwrap(); + let batch = state + .previous_batches + .load(deps.as_ref().storage, 4u64.into()) + .unwrap(); assert_eq!(batch, previous_batches[3]); } @@ -787,7 +850,10 @@ fn withdrawing_unbonded() { .unbond_requests .save( deps.as_mut().storage, - (unbond_request.id.into(), &Addr::unchecked(unbond_request.user.clone())), + ( + unbond_request.id.into(), + &Addr::unchecked(unbond_request.user.clone()), + ), unbond_request, ) .unwrap(); @@ -798,28 +864,28 @@ fn withdrawing_unbonded() { id: 1, reconciled: true, total_shares: Uint128::new(92876), - uluna_unclaimed: Uint128::new(95197), // 1.025 Luna per Steak + amount_unclaimed: Uint128::new(95197), // 1.025 Luna per Steak est_unbond_end_time: 10000, }, Batch { id: 2, reconciled: true, total_shares: Uint128::new(34567), - uluna_unclaimed: Uint128::new(35604), // 1.030 Luna per Steak + amount_unclaimed: Uint128::new(35604), // 1.030 Luna per Steak est_unbond_end_time: 20000, }, Batch { id: 3, reconciled: false, // finished unbonding, but not reconciled; ignored total_shares: Uint128::new(45678), - uluna_unclaimed: Uint128::new(47276), // 1.035 Luna per Steak + amount_unclaimed: Uint128::new(47276), // 1.035 Luna per Steak est_unbond_end_time: 20000, }, Batch { id: 4, reconciled: true, total_shares: Uint128::new(56789), - uluna_unclaimed: Uint128::new(59060), // 1.040 Luna per Steak + amount_unclaimed: Uint128::new(59060), // 1.040 Luna per Steak est_unbond_end_time: 30000, // reconciled, but not yet finished unbonding; ignored }, ]; @@ -827,7 +893,11 @@ fn withdrawing_unbonded() { for previous_batch in &previous_batches { state .previous_batches - .save(deps.as_mut().storage, previous_batch.id.into(), previous_batch) + .save( + deps.as_mut().storage, + previous_batch.id.into(), + previous_batch, + ) .unwrap(); } @@ -848,9 +918,7 @@ fn withdrawing_unbonded() { deps.as_mut(), mock_env_at_timestamp(5000), mock_info("user_1", &[]), - ExecuteMsg::WithdrawUnbonded { - receiver: None, - }, + ExecuteMsg::WithdrawUnbonded { receiver: None }, ) .unwrap_err(); @@ -871,9 +939,7 @@ fn withdrawing_unbonded() { deps.as_mut(), mock_env_at_timestamp(25000), mock_info("user_1", &[]), - ExecuteMsg::WithdrawUnbonded { - receiver: None, - }, + ExecuteMsg::WithdrawUnbonded { receiver: None }, ) .unwrap(); @@ -884,7 +950,7 @@ fn withdrawing_unbonded() { id: 0, msg: CosmosMsg::Bank(BankMsg::Send { to_address: "user_1".to_string(), - amount: vec![Coin::new(59646, "uluna")] + amount: vec![Coin::new(59646, "uxyz")] }), gas_limit: None, reply_on: ReplyOn::Never @@ -892,41 +958,59 @@ fn withdrawing_unbonded() { ); // Previous batches should have been updated - let batch = state.previous_batches.load(deps.as_ref().storage, 1u64.into()).unwrap(); + let batch = state + .previous_batches + .load(deps.as_ref().storage, 1u64.into()) + .unwrap(); assert_eq!( batch, Batch { id: 1, reconciled: true, total_shares: Uint128::new(69420), - uluna_unclaimed: Uint128::new(71155), + amount_unclaimed: Uint128::new(71155), est_unbond_end_time: 10000, } ); - let err = state.previous_batches.load(deps.as_ref().storage, 2u64.into()).unwrap_err(); + let err = state + .previous_batches + .load(deps.as_ref().storage, 2u64.into()) + .unwrap_err(); assert_eq!( err, - StdError::NotFound { kind: "steak::hub::Batch".to_string() } + StdError::NotFound { + kind: "steak::hub::Batch".to_string() + } ); // User 1's unbond requests in batches 1 and 2 should have been deleted let err1 = state .unbond_requests - .load(deps.as_ref().storage, (1u64.into(), &Addr::unchecked("user_1"))) + .load( + deps.as_ref().storage, + (1u64.into(), &Addr::unchecked("user_1")), + ) .unwrap_err(); let err2 = state .unbond_requests - .load(deps.as_ref().storage, (1u64.into(), &Addr::unchecked("user_1"))) + .load( + deps.as_ref().storage, + (1u64.into(), &Addr::unchecked("user_1")), + ) .unwrap_err(); assert_eq!( err1, - StdError::NotFound { kind: "steak::hub::UnbondRequest".to_string() } + StdError::NotFound { + kind: "steak::hub::UnbondRequest".to_string() + } ); assert_eq!( err2, - StdError::NotFound { kind: "steak::hub::UnbondRequest".to_string() } + StdError::NotFound { + kind: "steak::hub::UnbondRequest".to_string() + } ); // User 3 attempt to withdraw; also specifying a receiver @@ -947,7 +1031,7 @@ fn withdrawing_unbonded() { id: 0, msg: CosmosMsg::Bank(BankMsg::Send { to_address: "user_2".to_string(), - amount: vec![Coin::new(71155, "uluna")] + amount: vec![Coin::new(71155, "uxyz")] }), gas_limit: None, reply_on: ReplyOn::Never @@ -955,7 +1039,10 @@ fn withdrawing_unbonded() { ); // Batch 1 and user 2's unbonding request should have been purged from storage - let err = state.previous_batches.load(deps.as_ref().storage, 1u64.into()).unwrap_err(); + let err = state + .previous_batches + .load(deps.as_ref().storage, 1u64.into()) + .unwrap_err(); assert_eq!( err, StdError::NotFound { @@ -965,12 +1052,17 @@ fn withdrawing_unbonded() { let err = state .unbond_requests - .load(deps.as_ref().storage, (1u64.into(), &Addr::unchecked("user_3"))) + .load( + deps.as_ref().storage, + (1u64.into(), &Addr::unchecked("user_3")), + ) .unwrap_err(); assert_eq!( err, - StdError::NotFound { kind: "steak::hub::UnbondRequest".to_string() } + StdError::NotFound { + kind: "steak::hub::UnbondRequest".to_string() + } ); } @@ -989,7 +1081,10 @@ fn adding_validator() { ) .unwrap_err(); - assert_eq!(err, StdError::generic_err("unauthorized: sender is not owner")); + assert_eq!( + err, + StdError::generic_err("unauthorized: sender is not owner") + ); let err = execute( deps.as_mut(), @@ -1001,7 +1096,10 @@ fn adding_validator() { ) .unwrap_err(); - assert_eq!(err, StdError::generic_err("validator is already whitelisted")); + assert_eq!( + err, + StdError::generic_err("validator is already whitelisted") + ); let res = execute( deps.as_mut(), @@ -1048,7 +1146,10 @@ fn removing_validator() { ) .unwrap_err(); - assert_eq!(err, StdError::generic_err("unauthorized: sender is not owner")); + assert_eq!( + err, + StdError::generic_err("unauthorized: sender is not owner") + ); let err = execute( deps.as_mut(), @@ -1060,7 +1161,10 @@ fn removing_validator() { ) .unwrap_err(); - assert_eq!(err, StdError::generic_err("validator is not already whitelisted")); + assert_eq!( + err, + StdError::generic_err("validator is not already whitelisted") + ); // Target: (341667 + 341667 + 341666) / 2 = 512500 // Remainder: 0 @@ -1079,11 +1183,17 @@ fn removing_validator() { assert_eq!(res.messages.len(), 2); assert_eq!( res.messages[0], - SubMsg::reply_on_success(Redelegation::new("charlie", "alice", 170833).to_cosmos_msg(), 2), + SubMsg::reply_on_success( + Redelegation::new("charlie", "alice", 170833).to_cosmos_msg(), + 2 + ), ); assert_eq!( res.messages[1], - SubMsg::reply_on_success(Redelegation::new("charlie", "bob", 170833).to_cosmos_msg(), 2), + SubMsg::reply_on_success( + Redelegation::new("charlie", "bob", 170833).to_cosmos_msg(), + 2 + ), ); let validators = state.validators.load(deps.as_ref().storage).unwrap(); @@ -1105,7 +1215,10 @@ fn transferring_ownership() { ) .unwrap_err(); - assert_eq!(err, StdError::generic_err("unauthorized: sender is not owner")); + assert_eq!( + err, + StdError::generic_err("unauthorized: sender is not owner") + ); let res = execute( deps.as_mut(), @@ -1130,13 +1243,16 @@ fn transferring_ownership() { ) .unwrap_err(); - assert_eq!(err, StdError::generic_err("unauthorized: sender is not new owner")); + assert_eq!( + err, + StdError::generic_err("unauthorized: sender is not new owner") + ); let res = execute( deps.as_mut(), mock_env(), mock_info("jake", &[]), - ExecuteMsg::AcceptOwnership {} + ExecuteMsg::AcceptOwnership {}, ) .unwrap(); @@ -1159,35 +1275,38 @@ fn querying_previous_batches() { id: 1, reconciled: false, total_shares: Uint128::new(123), - uluna_unclaimed: Uint128::new(678), + amount_unclaimed: Uint128::new(678), est_unbond_end_time: 10000, }, Batch { id: 2, reconciled: true, total_shares: Uint128::new(234), - uluna_unclaimed: Uint128::new(789), + amount_unclaimed: Uint128::new(789), est_unbond_end_time: 15000, }, Batch { id: 3, reconciled: false, total_shares: Uint128::new(345), - uluna_unclaimed: Uint128::new(890), + amount_unclaimed: Uint128::new(890), est_unbond_end_time: 20000, }, Batch { id: 4, reconciled: true, total_shares: Uint128::new(456), - uluna_unclaimed: Uint128::new(999), + amount_unclaimed: Uint128::new(999), est_unbond_end_time: 25000, }, ]; let state = State::default(); for batch in &batches { - state.previous_batches.save(deps.as_mut().storage, batch.id.into(), batch).unwrap(); + state + .previous_batches + .save(deps.as_mut().storage, batch.id.into(), batch) + .unwrap(); } // Querying a single batch @@ -1214,7 +1333,10 @@ fn querying_previous_batches() { limit: None, }, ); - assert_eq!(res, vec![batches[1].clone(), batches[2].clone(), batches[3].clone()]); + assert_eq!( + res, + vec![batches[1].clone(), batches[2].clone(), batches[3].clone()] + ); let res: Vec = query_helper( deps.as_ref(), @@ -1288,7 +1410,10 @@ fn querying_unbond_requests() { .unbond_requests .save( deps.as_mut().storage, - (unbond_request.id.into(), &Addr::unchecked(unbond_request.user.clone())), + ( + unbond_request.id.into(), + &Addr::unchecked(unbond_request.user.clone()), + ), unbond_request, ) .unwrap(); @@ -1329,7 +1454,13 @@ fn querying_unbond_requests() { limit: None, }, ); - assert_eq!(res, vec![unbond_requests[0].clone().into(), unbond_requests[3].clone().into()]); + assert_eq!( + res, + vec![ + unbond_requests[0].clone().into(), + unbond_requests[3].clone().into() + ] + ); let res: Vec = query_helper( deps.as_ref(), @@ -1435,7 +1566,10 @@ fn computing_redelegations_for_rebalancing() { Redelegation::new("charlie", "evan", 38126), ]; - assert_eq!(compute_redelegations_for_rebalancing(¤t_delegations), expected,); + assert_eq!( + compute_redelegations_for_rebalancing(¤t_delegations), + expected, + ); } //-------------------------------------------------------------------------------------------------- @@ -1447,14 +1581,25 @@ fn parsing_coin() { let coin = parse_coin("12345uatom").unwrap(); assert_eq!(coin, Coin::new(12345, "uatom")); - let coin = parse_coin("23456ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B").unwrap(); - assert_eq!(coin, Coin::new(23456, "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B")); + let coin = + parse_coin("23456ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B") + .unwrap(); + assert_eq!( + coin, + Coin::new( + 23456, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B" + ) + ); let err = parse_coin("69420").unwrap_err(); assert_eq!(err, StdError::generic_err("failed to parse coin: 69420")); let err = parse_coin("ngmi").unwrap_err(); - assert_eq!(err, StdError::generic_err("Parsing u128: cannot parse integer from empty string")); + assert_eq!( + err, + StdError::generic_err("Parsing u128: cannot parse integer from empty string") + ); } #[test] @@ -1465,8 +1610,11 @@ fn parsing_coins() { let coins = Coins::from_str("12345uatom").unwrap(); assert_eq!(coins.0, vec![Coin::new(12345, "uatom")]); - let coins = Coins::from_str("12345uatom,23456uluna").unwrap(); - assert_eq!(coins.0, vec![Coin::new(12345, "uatom"), Coin::new(23456, "uluna")]); + let coins = Coins::from_str("12345uatom,23456uxyz").unwrap(); + assert_eq!( + coins.0, + vec![Coin::new(12345, "uatom"), Coin::new(23456, "uxyz")] + ); } #[test] @@ -1476,27 +1624,55 @@ fn adding_coins() { coins.add(&Coin::new(12345, "uatom")).unwrap(); assert_eq!(coins.0, vec![Coin::new(12345, "uatom")]); - coins.add(&Coin::new(23456, "uluna")).unwrap(); - assert_eq!(coins.0, vec![Coin::new(12345, "uatom"), Coin::new(23456, "uluna")]); + coins.add(&Coin::new(23456, "uxyz")).unwrap(); + assert_eq!( + coins.0, + vec![Coin::new(12345, "uatom"), Coin::new(23456, "uxyz")] + ); - coins.add_many(&Coins::from_str("76543uatom,69420uusd").unwrap()).unwrap(); - assert_eq!(coins.0, vec![Coin::new(88888, "uatom"), Coin::new(23456, "uluna"), Coin::new(69420, "uusd")]); + coins + .add_many(&Coins::from_str("76543uatom,69420uusd").unwrap()) + .unwrap(); + assert_eq!( + coins.0, + vec![ + Coin::new(88888, "uatom"), + Coin::new(23456, "uxyz"), + Coin::new(69420, "uusd") + ] + ); } #[test] fn receiving_funds() { - let err = parse_received_fund(&[], "uluna").unwrap_err(); - assert_eq!(err, StdError::generic_err("must deposit exactly one coin; received 0")); + let err = parse_received_fund(&[], "uxyz").unwrap_err(); + assert_eq!( + err, + StdError::generic_err("must deposit exactly one coin; received 0") + ); - let err = parse_received_fund(&[Coin::new(12345, "uatom"), Coin::new(23456, "uluna")], "uluna").unwrap_err(); - assert_eq!(err, StdError::generic_err("must deposit exactly one coin; received 2")); + let err = parse_received_fund( + &[Coin::new(12345, "uatom"), Coin::new(23456, "uxyz")], + "uxyz", + ) + .unwrap_err(); + assert_eq!( + err, + StdError::generic_err("must deposit exactly one coin; received 2") + ); - let err = parse_received_fund(&[Coin::new(12345, "uatom")], "uluna").unwrap_err(); - assert_eq!(err, StdError::generic_err("expected uluna deposit, received uatom")); + let err = parse_received_fund(&[Coin::new(12345, "uatom")], "uxyz").unwrap_err(); + assert_eq!( + err, + StdError::generic_err("expected uxyz deposit, received uatom") + ); - let err = parse_received_fund(&[Coin::new(0, "uluna")], "uluna").unwrap_err(); - assert_eq!(err, StdError::generic_err("deposit amount must be non-zero")); + let err = parse_received_fund(&[Coin::new(0, "uxyz")], "uxyz").unwrap_err(); + assert_eq!( + err, + StdError::generic_err("deposit amount must be non-zero") + ); - let amount = parse_received_fund(&[Coin::new(69420, "uluna")], "uluna").unwrap(); + let amount = parse_received_fund(&[Coin::new(69420, "uxyz")], "uxyz").unwrap(); assert_eq!(amount, Uint128::new(69420)); } diff --git a/packages/steak/Cargo.toml b/packages/steak/Cargo.toml index bbdd500..dc1842d 100644 --- a/packages/steak/Cargo.toml +++ b/packages/steak/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "steak" -version = "2.0.0" +version = "2.1.0" authors = ["larry ", "PFC "] edition = "2018" -description = "Liquid staking protocol for the cosmos" +description = "Liquid steaking protocol for the cosmos" license = "GPL-3.0-or-later" -homepage = "https://steak.club" -repository = "https://github.com/st4k3h0us3/steak-contracts" +homepage = "https://liquidsteaking.app" +repository = "https://github.com/PFC-developer/steak-contracts" [dependencies] cosmwasm-std = "1.0" diff --git a/packages/steak/src/hub.rs b/packages/steak/src/hub.rs index b793be1..cba006c 100644 --- a/packages/steak/src/hub.rs +++ b/packages/steak/src/hub.rs @@ -21,6 +21,14 @@ pub struct InstantiateMsg { pub unbond_period: u64, /// Initial set of validators who will receive the delegations pub validators: Vec, + /// denomination of coins to steak (uXXXX) + pub denom: String, + /// Fee Account to send fees too + pub fee_account: String, + /// Fee "1.00 = 100%" + pub fee_amount: Decimal, + /// Max Fee "1.00 = 100%" + pub max_fee_amount: Decimal, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] @@ -29,25 +37,15 @@ pub enum ExecuteMsg { /// Implements the Cw20 receiver interface Receive(Cw20ReceiveMsg), /// Bond specified amount of Luna - Bond { - receiver: Option, - }, + Bond { receiver: Option }, /// Withdraw Luna that have finished unbonding in previous batches - WithdrawUnbonded { - receiver: Option, - }, + WithdrawUnbonded { receiver: Option }, /// Add a validator to the whitelist; callable by the owner - AddValidator { - validator: String, - }, + AddValidator { validator: String }, /// Remove a validator from the whitelist; callable by the owner - RemoveValidator { - validator: String, - }, + RemoveValidator { validator: String }, /// Transfer ownership to another account; will not take effect unless the new owner accepts - TransferOwnership { - new_owner: String, - }, + TransferOwnership { new_owner: String }, /// Accept an ownership transfer AcceptOwnership {}, /// Claim staking rewards, swap all for Luna, and restake @@ -58,6 +56,11 @@ pub enum ExecuteMsg { Reconcile {}, /// Submit the current pending batch of unbonding requests to be unbonded SubmitBatch {}, + /// Transfer Fee collection account to another account + TransferFeeAccount { new_fee_account: String }, + /// Update fee collection amount + UpdateFee { new_fee: Decimal }, + /// Callbacks; can only be invoked by the contract itself Callback(CallbackMsg), } @@ -67,9 +70,7 @@ pub enum ExecuteMsg { pub enum ReceiveMsg { /// Submit an unbonding request to the current unbonding queue; automatically invokes `unbond` /// if `epoch_time` has elapsed since when the last unbonding queue was executed. - QueueUnbond { - receiver: Option, - }, + QueueUnbond { receiver: Option }, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] @@ -141,9 +142,9 @@ pub struct ConfigResponse { pub struct StateResponse { /// Total supply to the Steak token pub total_usteak: Uint128, - /// Total amount of uluna staked - pub total_uluna: Uint128, - /// The exchange rate between usteak and uluna, in terms of uluna per usteak + /// Total amount of native staked + pub total_native: Uint128, + /// The exchange rate between usteak and native, in terms of native per usteak pub exchange_rate: Decimal, /// Staking rewards currently held by the contract that are ready to be reinvested pub unlocked_coins: Vec, @@ -167,8 +168,8 @@ pub struct Batch { pub reconciled: bool, /// Total amount of shares remaining this batch. Each `usteak` burned = 1 share pub total_shares: Uint128, - /// Amount of `uluna` in this batch that have not been claimed - pub uluna_unclaimed: Uint128, + /// Amount of `denom` in this batch that have not been claimed + pub amount_unclaimed: Uint128, /// Estimated time when this batch will finish unbonding pub est_unbond_end_time: u64, } diff --git a/packages/steak/src/lib.rs b/packages/steak/src/lib.rs index 2dbca3b..b1f29ab 100644 --- a/packages/steak/src/lib.rs +++ b/packages/steak/src/lib.rs @@ -1 +1,62 @@ pub mod hub; + +// this was copied from eris-staking's branch of STEAK. +// +mod decimal_checked_ops { + use cosmwasm_std::{Decimal, Decimal256, Fraction, OverflowError, StdError, Uint128, Uint256}; + use std::{convert::TryInto, str::FromStr}; + + // pub trait Decimal256CheckedOps { + // fn to_decimal(self) -> Result; + // } + + // impl Decimal256CheckedOps for Decimal256 { + // fn to_decimal(self) -> Result { + // let U256(ref arr) = self.0; + // if arr[2] == 0u64 || arr[3] == 0u64 { + // return Err(StdError::generic_err( + // "overflow error by casting decimal256 to decimal", + // )); + // } + // Decimal::from_str(&self.to_string()) + // } + // } + + pub trait DecimalCheckedOps { + fn checked_add(self, other: Decimal) -> Result; + fn checked_mul_uint(self, other: Uint128) -> Result; + fn to_decimal256(self) -> Decimal256; + } + + impl DecimalCheckedOps for Decimal { + fn checked_add(self, other: Decimal) -> Result { + self.numerator() + .checked_add(other.numerator()) + .map(|_| self + other) + .map_err(StdError::overflow) + } + + fn checked_mul_uint(self, other: Uint128) -> Result { + if self.is_zero() || other.is_zero() { + return Ok(Uint128::zero()); + } + let multiply_ratio = + other.full_mul(self.numerator()) / Uint256::from(self.denominator()); + if multiply_ratio > Uint256::from(Uint128::MAX) { + Err(StdError::overflow(OverflowError::new( + cosmwasm_std::OverflowOperation::Mul, + self, + other, + ))) + } else { + Ok(multiply_ratio.try_into().unwrap()) + } + } + + fn to_decimal256(self) -> Decimal256 { + Decimal256::from_str(&self.to_string()).unwrap() + } + } +} + +pub use decimal_checked_ops::DecimalCheckedOps; From a957ab665b131dd80cdcab1845112a6a0d02aa0a Mon Sep 17 00:00:00 2001 From: "PFC(Ian)" Date: Wed, 15 Jun 2022 18:38:44 +1000 Subject: [PATCH 02/77] fix config query --- Cargo.lock | 4 ++-- contracts/hub/Cargo.toml | 2 +- contracts/hub/src/queries.rs | 4 ++++ contracts/hub/src/testing/tests.rs | 4 ++++ packages/steak/Cargo.toml | 2 +- packages/steak/src/hub.rs | 8 ++++++++ 6 files changed, 20 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e4dcf74..e653df6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -567,7 +567,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "steak" -version = "2.1.0" +version = "2.1.1" dependencies = [ "cosmwasm-std", "cw20", @@ -577,7 +577,7 @@ dependencies = [ [[package]] name = "steak-hub" -version = "2.1.0" +version = "2.1.1" dependencies = [ "cosmwasm-std", "cw-storage-plus", diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index c0cf95c..d156a69 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "steak-hub" -version = "2.1.0" +version = "2.1.1" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub/src/queries.rs b/contracts/hub/src/queries.rs index 005d4de..0cf69a7 100644 --- a/contracts/hub/src/queries.rs +++ b/contracts/hub/src/queries.rs @@ -23,6 +23,10 @@ pub fn config(deps: Deps) -> StdResult { steak_token: state.steak_token.load(deps.storage)?.into(), epoch_period: state.epoch_period.load(deps.storage)?, unbond_period: state.unbond_period.load(deps.storage)?, + denom: state.denom.load(deps.storage)?.to_string(), + fee_account: state.fee_account.load(deps.storage)?.to_string(), + fee_rate: state.fee_rate.load(deps.storage)?, + max_fee_rate: state.max_fee_rate.load(deps.storage)?, validators: state.validators.load(deps.storage)?, }) } diff --git a/contracts/hub/src/testing/tests.rs b/contracts/hub/src/testing/tests.rs index 07eeeeb..83efdbd 100644 --- a/contracts/hub/src/testing/tests.rs +++ b/contracts/hub/src/testing/tests.rs @@ -123,6 +123,10 @@ fn proper_instantiation() { steak_token: "steak_token".to_string(), epoch_period: 259200, unbond_period: 1814400, + denom: "uxyz".to_string(), + fee_account: "the_fee_man".to_string(), + fee_rate: Decimal::from_ratio(10_u128, 100_u128), + max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), validators: vec![ "alice".to_string(), "bob".to_string(), diff --git a/packages/steak/Cargo.toml b/packages/steak/Cargo.toml index dc1842d..2134f91 100644 --- a/packages/steak/Cargo.toml +++ b/packages/steak/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "steak" -version = "2.1.0" +version = "2.1.1" authors = ["larry ", "PFC "] edition = "2018" description = "Liquid steaking protocol for the cosmos" diff --git a/packages/steak/src/hub.rs b/packages/steak/src/hub.rs index cba006c..4c97370 100644 --- a/packages/steak/src/hub.rs +++ b/packages/steak/src/hub.rs @@ -134,6 +134,14 @@ pub struct ConfigResponse { pub epoch_period: u64, /// The staking module's unbonding time, in seconds pub unbond_period: u64, + /// denomination of coins to steak (uXXXX) + pub denom: String, + /// Fee Account to send fees too + pub fee_account: String, + /// Fee "1.00 = 100%" + pub fee_rate: Decimal, + /// Max Fee "1.00 = 100%" + pub max_fee_rate: Decimal, /// Initial set of validators who will receive the delegations pub validators: Vec, } From 6288ce16a9304c2ad5bcbe2b846edf8cfc16a642 Mon Sep 17 00:00:00 2001 From: "PFC(Ian)" Date: Wed, 15 Jun 2022 19:44:41 +1000 Subject: [PATCH 03/77] fixed types, to specify token --- contracts/hub/src/execute.rs | 24 ++++--- contracts/hub/src/helpers.rs | 32 ++++++--- contracts/hub/src/math.rs | 8 ++- contracts/hub/src/queries.rs | 5 +- contracts/hub/src/testing/tests.rs | 105 +++++++++++++++-------------- contracts/hub/src/types/staking.rs | 18 +++-- 6 files changed, 115 insertions(+), 77 deletions(-) diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index b36f219..e5c159f 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -124,7 +124,7 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec) -> StdRes // Query the current delegations made to validators, and find the validator with the smallest // delegated amount through a linear search // The code for linear search is a bit uglier than using `sort_by` but cheaper: O(n) vs O(n * log(n)) - let delegations = query_delegations(&deps.querier, &validators, &env.contract.address)?; + let delegations = query_delegations(&deps.querier, &validators, &env.contract.address, &denom)?; let mut validator = &delegations[0].validator; let mut amount = delegations[0].amount; for d in &delegations[1..] { @@ -136,6 +136,7 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec) -> StdRes let new_delegation = Delegation { validator: validator.clone(), amount: amount_to_bond.u128(), + denom: denom.clone(), }; // Query the current supply of Steak and compute the amount to mint @@ -211,7 +212,7 @@ pub fn reinvest(deps: DepsMut, env: Env) -> StdResult { .ok_or_else(|| StdError::generic_err("no native amount available to be bonded"))? .amount; - let delegations = query_delegations(&deps.querier, &validators, &env.contract.address)?; + let delegations = query_delegations(&deps.querier, &validators, &env.contract.address, &denom)?; let mut validator = &delegations[0].validator; let mut amount = delegations[0].amount; for d in &delegations[1..] { @@ -227,7 +228,7 @@ pub fn reinvest(deps: DepsMut, env: Env) -> StdResult { }; let amount_to_bond_minus_fees = amount_to_bond.saturating_sub(fee_amount); - let new_delegation = Delegation::new(validator, amount_to_bond_minus_fees.u128()); + let new_delegation = Delegation::new(validator, amount_to_bond_minus_fees.u128(), &denom); unlocked_coins.retain(|coin| coin.denom != denom); state.unlocked_coins.save(deps.storage, &unlocked_coins)?; @@ -365,6 +366,7 @@ pub fn queue_unbond( pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { let state = State::default(); + let denom = state.denom.load(deps.storage)?; let steak_token = state.steak_token.load(deps.storage)?; let validators = state.validators.load(deps.storage)?; let unbond_period = state.unbond_period.load(deps.storage)?; @@ -378,12 +380,12 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { ))); } - let delegations = query_delegations(&deps.querier, &validators, &env.contract.address)?; + let delegations = query_delegations(&deps.querier, &validators, &env.contract.address, &denom)?; let usteak_supply = query_cw20_total_supply(&deps.querier, &steak_token)?; let amount_to_bond = compute_unbond_amount(usteak_supply, pending_batch.usteak_to_burn, &delegations); - let new_undelegations = compute_undelegations(amount_to_bond, &delegations); + let new_undelegations = compute_undelegations(amount_to_bond, &delegations, &denom); // NOTE: Regarding the `amount_unclaimed` value // @@ -593,9 +595,10 @@ pub fn withdraw_unbonded( pub fn rebalance(deps: DepsMut, env: Env) -> StdResult { let state = State::default(); + let denom = state.denom.load(deps.storage)?; let validators = state.validators.load(deps.storage)?; - let delegations = query_delegations(&deps.querier, &validators, &env.contract.address)?; + let delegations = query_delegations(&deps.querier, &validators, &env.contract.address, &denom)?; let new_redelegations = compute_redelegations_for_rebalancing(&delegations); @@ -643,6 +646,7 @@ pub fn remove_validator( let state = State::default(); state.assert_owner(deps.storage, &sender)?; + let denom = state.denom.load(deps.storage)?; let validators = state.validators.update(deps.storage, |mut validators| { if !validators.contains(&validator) { @@ -654,9 +658,11 @@ pub fn remove_validator( Ok(validators) })?; - let delegations = query_delegations(&deps.querier, &validators, &env.contract.address)?; - let delegation_to_remove = query_delegation(&deps.querier, &validator, &env.contract.address)?; - let new_redelegations = compute_redelegations_for_removal(&delegation_to_remove, &delegations); + let delegations = query_delegations(&deps.querier, &validators, &env.contract.address, &denom)?; + let delegation_to_remove = + query_delegation(&deps.querier, &validator, &env.contract.address, &denom)?; + let new_redelegations = + compute_redelegations_for_removal(&delegation_to_remove, &delegations, &denom); let redelegate_submsgs = new_redelegations .iter() diff --git a/contracts/hub/src/helpers.rs b/contracts/hub/src/helpers.rs index b47fb77..ee62a4f 100644 --- a/contracts/hub/src/helpers.rs +++ b/contracts/hub/src/helpers.rs @@ -17,7 +17,8 @@ pub(crate) fn query_cw20_total_supply( querier: &QuerierWrapper, token_addr: &Addr, ) -> StdResult { - let token_info: TokenInfoResponse = querier.query_wasm_smart(token_addr, &Cw20QueryMsg::TokenInfo {})?; + let token_info: TokenInfoResponse = + querier.query_wasm_smart(token_addr, &Cw20QueryMsg::TokenInfo {})?; Ok(token_info.total_supply) } @@ -26,10 +27,15 @@ pub(crate) fn query_delegation( querier: &QuerierWrapper, validator: &str, delegator_addr: &Addr, + denom: &str, ) -> StdResult { Ok(Delegation { validator: validator.to_string(), - amount: querier.query_delegation(delegator_addr, validator)?.map(|fd| fd.amount.amount.u128()).unwrap_or(0), + amount: querier + .query_delegation(delegator_addr, validator)? + .map(|fd| fd.amount.amount.u128()) + .unwrap_or(0), + denom: denom.into(), }) } @@ -38,10 +44,11 @@ pub(crate) fn query_delegations( querier: &QuerierWrapper, validators: &[String], delegator_addr: &Addr, + denom: &str, ) -> StdResult> { validators .iter() - .map(|validator| query_delegation(querier, validator, delegator_addr)) + .map(|validator| query_delegation(querier, validator, delegator_addr, denom)) .collect() } @@ -64,23 +71,28 @@ pub(crate) fn parse_coin(s: &str) -> StdResult { } } - Err(StdError::generic_err(format!("failed to parse coin: {}", s))) + Err(StdError::generic_err(format!( + "failed to parse coin: {}", + s + ))) } /// Find the amount of a denom sent along a message, assert it is non-zero, and no other denom were /// sent together pub(crate) fn parse_received_fund(funds: &[Coin], denom: &str) -> StdResult { if funds.len() != 1 { - return Err(StdError::generic_err( - format!("must deposit exactly one coin; received {}", funds.len()), - )); + return Err(StdError::generic_err(format!( + "must deposit exactly one coin; received {}", + funds.len() + ))); } let fund = &funds[0]; if fund.denom != denom { - return Err(StdError::generic_err( - format!("expected {} deposit, received {}", denom, fund.denom), - )); + return Err(StdError::generic_err(format!( + "expected {} deposit, received {}", + denom, fund.denom + ))); } if fund.amount.is_zero() { diff --git a/contracts/hub/src/math.rs b/contracts/hub/src/math.rs index 4408902..3013aab 100644 --- a/contracts/hub/src/math.rs +++ b/contracts/hub/src/math.rs @@ -52,6 +52,7 @@ pub(crate) fn compute_unbond_amount( pub(crate) fn compute_undelegations( native_to_unbond: Uint128, current_delegations: &[Delegation], + denom: &str, ) -> Vec { let native_staked: u128 = current_delegations.iter().map(|d| d.amount).sum(); let validator_count = current_delegations.len() as u128; @@ -76,7 +77,7 @@ pub(crate) fn compute_undelegations( native_available -= native_to_undelegate; if native_to_undelegate > 0 { - new_undelegations.push(Undelegation::new(&d.validator, native_to_undelegate)); + new_undelegations.push(Undelegation::new(&d.validator, native_to_undelegate, denom)); } if native_available == 0 { @@ -96,6 +97,7 @@ pub(crate) fn compute_undelegations( pub(crate) fn compute_redelegations_for_removal( delegation_to_remove: &Delegation, current_delegations: &[Delegation], + denom: &str, ) -> Vec { let native_staked: u128 = current_delegations.iter().map(|d| d.amount).sum(); let validator_count = current_delegations.len() as u128; @@ -124,6 +126,7 @@ pub(crate) fn compute_redelegations_for_removal( &delegation_to_remove.validator, &d.validator, native_to_redelegate, + denom, )); } @@ -163,12 +166,14 @@ pub(crate) fn compute_redelegations_for_rebalancing( src_delegations.push(Delegation::new( &d.validator, d.amount - native_for_validator, + &d.denom, )); } Ordering::Less => { dst_delegations.push(Delegation::new( &d.validator, native_for_validator - d.amount, + &d.denom, )); } Ordering::Equal => (), @@ -197,6 +202,7 @@ pub(crate) fn compute_redelegations_for_rebalancing( &src_delegation.validator, &dst_delegation.validator, native_to_redelegate, + &src_delegation.denom, )); } diff --git a/contracts/hub/src/queries.rs b/contracts/hub/src/queries.rs index 0cf69a7..75fd136 100644 --- a/contracts/hub/src/queries.rs +++ b/contracts/hub/src/queries.rs @@ -23,7 +23,7 @@ pub fn config(deps: Deps) -> StdResult { steak_token: state.steak_token.load(deps.storage)?.into(), epoch_period: state.epoch_period.load(deps.storage)?, unbond_period: state.unbond_period.load(deps.storage)?, - denom: state.denom.load(deps.storage)?.to_string(), + denom: state.denom.load(deps.storage)?, fee_account: state.fee_account.load(deps.storage)?.to_string(), fee_rate: state.fee_rate.load(deps.storage)?, max_fee_rate: state.max_fee_rate.load(deps.storage)?, @@ -34,11 +34,12 @@ pub fn config(deps: Deps) -> StdResult { pub fn state(deps: Deps, env: Env) -> StdResult { let state = State::default(); + let denom = state.denom.load(deps.storage)?; let steak_token = state.steak_token.load(deps.storage)?; let total_usteak = query_cw20_total_supply(&deps.querier, &steak_token)?; let validators = state.validators.load(deps.storage)?; - let delegations = query_delegations(&deps.querier, &validators, &env.contract.address)?; + let delegations = query_delegations(&deps.querier, &validators, &env.contract.address, &denom)?; let total_native: u128 = delegations.iter().map(|d| d.amount).sum(); let exchange_rate = if total_usteak.is_zero() { diff --git a/contracts/hub/src/testing/tests.rs b/contracts/hub/src/testing/tests.rs index 83efdbd..788894e 100644 --- a/contracts/hub/src/testing/tests.rs +++ b/contracts/hub/src/testing/tests.rs @@ -174,7 +174,7 @@ fn bonding() { assert_eq!(res.messages.len(), 2); assert_eq!( res.messages[0], - SubMsg::reply_on_success(Delegation::new("alice", 1000000).to_cosmos_msg(), 2) + SubMsg::reply_on_success(Delegation::new("alice", 1000000, "uxyz").to_cosmos_msg(), 2) ); assert_eq!( res.messages[1], @@ -197,9 +197,9 @@ fn bonding() { // Bond when there are existing delegations, and Luna:Steak exchange rate is >1 // Previously user 1 delegated 1,000,000 uluna. We assume we have accumulated 2.5% yield at 1025000 staked deps.querier.set_staking_delegations(&[ - Delegation::new("alice", 341667), - Delegation::new("bob", 341667), - Delegation::new("charlie", 341666), + Delegation::new("alice", 341667, "uxyz"), + Delegation::new("bob", 341667, "uxyz"), + Delegation::new("charlie", 341666, "uxyz"), ]); deps.querier.set_cw20_total_supply("steak_token", 1000000); @@ -217,7 +217,7 @@ fn bonding() { assert_eq!(res.messages.len(), 2); assert_eq!( res.messages[0], - SubMsg::reply_on_success(Delegation::new("charlie", 12345).to_cosmos_msg(), 2) + SubMsg::reply_on_success(Delegation::new("charlie", 12345, "uxyz").to_cosmos_msg(), 2) ); assert_eq!( res.messages[1], @@ -239,9 +239,9 @@ fn bonding() { // Check the state after bonding deps.querier.set_staking_delegations(&[ - Delegation::new("alice", 341667), - Delegation::new("bob", 341667), - Delegation::new("charlie", 354011), + Delegation::new("alice", 341667, "uxyz"), + Delegation::new("bob", 341667, "uxyz"), + Delegation::new("charlie", 354011, "uxyz"), ]); deps.querier.set_cw20_total_supply("steak_token", 1012043); @@ -263,9 +263,9 @@ fn harvesting() { // Assume users have bonded a total of 1,000,000 uluna and minted the same amount of usteak deps.querier.set_staking_delegations(&[ - Delegation::new("alice", 341667), - Delegation::new("bob", 341667), - Delegation::new("charlie", 341666), + Delegation::new("alice", 341667, "uxyz"), + Delegation::new("bob", 341667, "uxyz"), + Delegation::new("charlie", 341666, "uxyz"), ]); deps.querier.set_cw20_total_supply("steak_token", 1000000); @@ -365,9 +365,9 @@ fn reinvesting() { let state = State::default(); deps.querier.set_staking_delegations(&[ - Delegation::new("alice", 333334), - Delegation::new("bob", 333333), - Delegation::new("charlie", 333333), + Delegation::new("alice", 333334, "uxyz"), + Delegation::new("bob", 333333, "uxyz"), + Delegation::new("charlie", 333333, "uxyz"), ]); // After the swaps, `unlocked_coins` should contain only uluna and unknown denoms @@ -399,7 +399,7 @@ fn reinvesting() { res.messages[0], SubMsg { id: 0, - msg: Delegation::new("bob", 234 - 23).to_cosmos_msg(), + msg: Delegation::new("bob", 234 - 23, "uxyz").to_cosmos_msg(), gas_limit: None, reply_on: ReplyOn::Never } @@ -554,9 +554,9 @@ fn submitting_batch() { // usteak supply: 1,012,043 // uluna per ustake: 1.025 deps.querier.set_staking_delegations(&[ - Delegation::new("alice", 345782), - Delegation::new("bob", 345782), - Delegation::new("charlie", 345781), + Delegation::new("alice", 345782, "uxyz"), + Delegation::new("bob", 345782, "uxyz"), + Delegation::new("charlie", 345781, "uxyz"), ]); deps.querier.set_cw20_total_supply("steak_token", 1012043); @@ -622,15 +622,18 @@ fn submitting_batch() { assert_eq!(res.messages.len(), 4); assert_eq!( res.messages[0], - SubMsg::reply_on_success(Undelegation::new("alice", 31732).to_cosmos_msg(), 2) + SubMsg::reply_on_success(Undelegation::new("alice", 31732, "uxyz").to_cosmos_msg(), 2) ); assert_eq!( res.messages[1], - SubMsg::reply_on_success(Undelegation::new("bob", 31733).to_cosmos_msg(), 2) + SubMsg::reply_on_success(Undelegation::new("bob", 31733, "uxyz").to_cosmos_msg(), 2) ); assert_eq!( res.messages[2], - SubMsg::reply_on_success(Undelegation::new("charlie", 31732).to_cosmos_msg(), 2) + SubMsg::reply_on_success( + Undelegation::new("charlie", 31732, "uxyz").to_cosmos_msg(), + 2 + ) ); assert_eq!( res.messages[3], @@ -1135,9 +1138,9 @@ fn removing_validator() { let state = State::default(); deps.querier.set_staking_delegations(&[ - Delegation::new("alice", 341667), - Delegation::new("bob", 341667), - Delegation::new("charlie", 341666), + Delegation::new("alice", 341667, "uxyz"), + Delegation::new("bob", 341667, "uxyz"), + Delegation::new("charlie", 341666, "uxyz"), ]); let err = execute( @@ -1188,14 +1191,14 @@ fn removing_validator() { assert_eq!( res.messages[0], SubMsg::reply_on_success( - Redelegation::new("charlie", "alice", 170833).to_cosmos_msg(), + Redelegation::new("charlie", "alice", 170833, "uxyz").to_cosmos_msg(), 2 ), ); assert_eq!( res.messages[1], SubMsg::reply_on_success( - Redelegation::new("charlie", "bob", 170833).to_cosmos_msg(), + Redelegation::new("charlie", "bob", 170833, "uxyz").to_cosmos_msg(), 2 ), ); @@ -1484,9 +1487,9 @@ fn querying_unbond_requests() { #[test] fn computing_undelegations() { let current_delegations = vec![ - Delegation::new("alice", 400), - Delegation::new("bob", 300), - Delegation::new("charlie", 200), + Delegation::new("alice", 400, "uxyz"), + Delegation::new("bob", 300, "uxyz"), + Delegation::new("charlie", 200, "uxyz"), ]; // Target: (400 + 300 + 200 - 451) / 3 = 149 @@ -1494,11 +1497,11 @@ fn computing_undelegations() { // Alice: 400 - (149 + 1) = 250 // Bob: 300 - (149 + 1) = 150 // Charlie: 200 - (149 + 0) = 51 - let new_undelegations = compute_undelegations(Uint128::new(451), ¤t_delegations); + let new_undelegations = compute_undelegations(Uint128::new(451), ¤t_delegations, "uxyz"); let expected = vec![ - Undelegation::new("alice", 250), - Undelegation::new("bob", 150), - Undelegation::new("charlie", 51), + Undelegation::new("alice", 250, "uxyz"), + Undelegation::new("bob", 150, "uxyz"), + Undelegation::new("charlie", 51, "uxyz"), ]; assert_eq!(new_undelegations, expected); } @@ -1506,10 +1509,10 @@ fn computing_undelegations() { #[test] fn computing_redelegations_for_removal() { let current_delegations = vec![ - Delegation::new("alice", 13000), - Delegation::new("bob", 12000), - Delegation::new("charlie", 11000), - Delegation::new("dave", 10000), + Delegation::new("alice", 13000, "uxyz"), + Delegation::new("bob", 12000, "uxyz"), + Delegation::new("charlie", 11000, "uxyz"), + Delegation::new("dave", 10000, "uxyz"), ]; // Suppose Dave will be removed @@ -1519,13 +1522,17 @@ fn computing_redelegations_for_removal() { // to Bob: 15333 + 0 - 12000 = 3333 // to Charlie: 15333 + 0 - 11000 = 4333 let expected = vec![ - Redelegation::new("dave", "alice", 2334), - Redelegation::new("dave", "bob", 3333), - Redelegation::new("dave", "charlie", 4333), + Redelegation::new("dave", "alice", 2334, "uxyz"), + Redelegation::new("dave", "bob", 3333, "uxyz"), + Redelegation::new("dave", "charlie", 4333, "uxyz"), ]; assert_eq!( - compute_redelegations_for_removal(¤t_delegations[3], ¤t_delegations[..3]), + compute_redelegations_for_removal( + ¤t_delegations[3], + ¤t_delegations[..3], + "uxyz" + ), expected, ); } @@ -1533,11 +1540,11 @@ fn computing_redelegations_for_removal() { #[test] fn computing_redelegations_for_rebalancing() { let current_delegations = vec![ - Delegation::new("alice", 69420), - Delegation::new("bob", 1234), - Delegation::new("charlie", 88888), - Delegation::new("dave", 40471), - Delegation::new("evan", 2345), + Delegation::new("alice", 69420, "uxyz"), + Delegation::new("bob", 1234, "uxyz"), + Delegation::new("charlie", 88888, "uxyz"), + Delegation::new("dave", 40471, "uxyz"), + Delegation::new("evan", 2345, "uxyz"), ]; // uluna_per_validator = (69420 + 88888 + 1234 + 40471 + 2345) / 4 = 40471 @@ -1565,9 +1572,9 @@ fn computing_redelegations_for_rebalancing() { // Round 3: charlie --(38126)--> evan // Queues are emptied let expected = vec![ - Redelegation::new("alice", "bob", 28948), - Redelegation::new("charlie", "bob", 10290), - Redelegation::new("charlie", "evan", 38126), + Redelegation::new("alice", "bob", 28948, "uxyz"), + Redelegation::new("charlie", "bob", 10290, "uxyz"), + Redelegation::new("charlie", "evan", 38126, "uxyz"), ]; assert_eq!( diff --git a/contracts/hub/src/types/staking.rs b/contracts/hub/src/types/staking.rs index a4d4fa3..39cb599 100644 --- a/contracts/hub/src/types/staking.rs +++ b/contracts/hub/src/types/staking.rs @@ -5,20 +5,22 @@ use cosmwasm_std::{Coin, CosmosMsg, StakingMsg}; pub struct Delegation { pub validator: String, pub amount: u128, + pub denom: String, } impl Delegation { - pub fn new(validator: &str, amount: u128) -> Self { + pub fn new(validator: &str, amount: u128, denom: &str) -> Self { Self { validator: validator.to_string(), amount, + denom: denom.to_string(), } } pub fn to_cosmos_msg(&self) -> CosmosMsg { CosmosMsg::Staking(StakingMsg::Delegate { validator: self.validator.clone(), - amount: Coin::new(self.amount, "uluna"), + amount: Coin::new(self.amount, self.denom.to_string()), }) } } @@ -27,20 +29,22 @@ impl Delegation { pub struct Undelegation { pub validator: String, pub amount: u128, + pub denom: String, } impl Undelegation { - pub fn new(validator: &str, amount: u128) -> Self { + pub fn new(validator: &str, amount: u128, denom: &str) -> Self { Self { validator: validator.to_string(), amount, + denom: denom.to_string(), } } pub fn to_cosmos_msg(&self) -> CosmosMsg { CosmosMsg::Staking(StakingMsg::Undelegate { validator: self.validator.clone(), - amount: Coin::new(self.amount, "uluna"), + amount: Coin::new(self.amount, self.denom.to_string()), }) } } @@ -50,14 +54,16 @@ pub struct Redelegation { pub src: String, pub dst: String, pub amount: u128, + pub denom: String, } impl Redelegation { - pub fn new(src: &str, dst: &str, amount: u128) -> Self { + pub fn new(src: &str, dst: &str, amount: u128, denom: &str) -> Self { Self { src: src.to_string(), dst: dst.to_string(), amount, + denom: denom.into(), } } @@ -65,7 +71,7 @@ impl Redelegation { CosmosMsg::Staking(StakingMsg::Redelegate { src_validator: self.src.clone(), dst_validator: self.dst.clone(), - amount: Coin::new(self.amount, "uluna"), + amount: Coin::new(self.amount, &self.denom), }) } } From c3db5954216b1dcdaee7c0f2d0e2fd4f3969d279 Mon Sep 17 00:00:00 2001 From: "PFC(Ian)" Date: Mon, 27 Jun 2022 07:40:26 -0500 Subject: [PATCH 04/77] fix: emergency method to remove a validator with an invalid address. does not undelegate --- .gitignore | 2 +- Cargo.lock | 2 +- contracts/hub/Cargo.toml | 2 +- contracts/hub/src/contract.rs | 10 +++++-- contracts/hub/src/execute.rs | 56 ++++++++++++++++++++++------------- packages/steak/src/hub.rs | 4 +++ 6 files changed, 50 insertions(+), 26 deletions(-) diff --git a/.gitignore b/.gitignore index 4b75762..ffcf560 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,4 @@ scripts/data/ scripts/keys/ # Misc notes -notes.txt +/notes*txt diff --git a/Cargo.lock b/Cargo.lock index b54699a..2e4ee11 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -577,7 +577,7 @@ dependencies = [ [[package]] name = "steak-hub" -version = "2.0.0" +version = "2.0.1" dependencies = [ "cosmwasm-std", "cw-storage-plus", diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index 84415c0..3c12810 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "steak-hub" -version = "2.0.0" +version = "2.0.1" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub/src/contract.rs b/contracts/hub/src/contract.rs index 99c0943..ac07d0c 100644 --- a/contracts/hub/src/contract.rs +++ b/contracts/hub/src/contract.rs @@ -47,6 +47,9 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S ExecuteMsg::RemoveValidator { validator, } => execute::remove_validator(deps, env, info.sender, validator), + ExecuteMsg::RemoveValidatorEx { + validator, + } => execute::remove_validator_ex(deps, env, info.sender, validator), ExecuteMsg::TransferOwnership { new_owner, } => execute::transfer_ownership(deps, info.sender, new_owner), @@ -74,9 +77,10 @@ fn receive( let steak_token = state.steak_token.load(deps.storage)?; if info.sender != steak_token { - return Err(StdError::generic_err( - format!("expecting Steak token, received {}", info.sender), - )); + return Err(StdError::generic_err(format!( + "expecting Steak token, received {}", + info.sender + ))); } execute::queue_unbond( diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index 3964144..c6619fb 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -327,9 +327,10 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { let current_time = env.block.time.seconds(); if current_time < pending_batch.est_unbond_start_time { - return Err(StdError::generic_err( - format!("batch can only be submitted for unbonding after {}", pending_batch.est_unbond_start_time), - )); + return Err(StdError::generic_err(format!( + "batch can only be submitted for unbonding after {}", + pending_batch.est_unbond_start_time + ))); } let delegations = query_delegations(&deps.querier, &validators, &env.contract.address)?; @@ -431,7 +432,8 @@ pub fn reconcile(deps: DepsMut, env: Env) -> StdResult { let uluna_expected = uluna_expected_received + uluna_expected_unlocked; let uluna_actual = deps.querier.query_balance(&env.contract.address, "uluna")?.amount; - let uluna_to_deduct = uluna_expected.checked_sub(uluna_actual).unwrap_or_else(|_| Uint128::zero()); + let uluna_to_deduct = + uluna_expected.checked_sub(uluna_actual).unwrap_or_else(|_| Uint128::zero()); if !uluna_to_deduct.is_zero() { reconcile_batches(&mut batches, uluna_expected - uluna_actual); } @@ -440,18 +442,13 @@ pub fn reconcile(deps: DepsMut, env: Env) -> StdResult { state.previous_batches.save(deps.storage, batch.id, batch)?; } - let ids = batches - .iter() - .map(|b| b.id.to_string()) - .collect::>().join(","); + let ids = batches.iter().map(|b| b.id.to_string()).collect::>().join(","); let event = Event::new("steakhub/reconciled") .add_attribute("ids", ids) .add_attribute("uluna_deducted", uluna_to_deduct.to_string()); - Ok(Response::new() - .add_event(event) - .add_attribute("action", "steakhub/reconcile")) + Ok(Response::new().add_event(event).add_attribute("action", "steakhub/reconcile")) } pub fn withdraw_unbonded( @@ -489,9 +486,8 @@ pub fn withdraw_unbonded( for request in &requests { if let Ok(mut batch) = state.previous_batches.load(deps.storage, request.id) { if batch.reconciled && batch.est_unbond_end_time < current_time { - let uluna_to_refund = batch - .uluna_unclaimed - .multiply_ratio(request.shares, batch.total_shares); + let uluna_to_refund = + batch.uluna_unclaimed.multiply_ratio(request.shares, batch.total_shares); ids.push(request.id.to_string()); @@ -552,8 +548,7 @@ pub fn rebalance(deps: DepsMut, env: Env) -> StdResult { let amount: u128 = new_redelegations.iter().map(|rd| rd.amount).sum(); - let event = Event::new("steakhub/rebalanced") - .add_attribute("uluna_moved", amount.to_string()); + let event = Event::new("steakhub/rebalanced").add_attribute("uluna_moved", amount.to_string()); Ok(Response::new() .add_submessages(redelegate_submsgs) @@ -574,8 +569,7 @@ pub fn add_validator(deps: DepsMut, sender: Addr, validator: String) -> StdResul Ok(validators) })?; - let event = Event::new("steakhub/validator_added") - .add_attribute("validator", validator); + let event = Event::new("steakhub/validator_added").add_attribute("validator", validator); Ok(Response::new().add_event(event).add_attribute("action", "steakhub/add_validator")) } @@ -607,8 +601,7 @@ pub fn remove_validator( .map(|d| SubMsg::reply_on_success(d.to_cosmos_msg(), 2)) .collect::>(); - let event = Event::new("steak/validator_removed") - .add_attribute("validator", validator); + let event = Event::new("steak/validator_removed").add_attribute("validator", validator); Ok(Response::new() .add_submessages(redelegate_submsgs) @@ -616,6 +609,29 @@ pub fn remove_validator( .add_attribute("action", "steakhub/remove_validator")) } +pub fn remove_validator_ex( + deps: DepsMut, + _env: Env, + sender: Addr, + validator: String, +) -> StdResult { + let state = State::default(); + + state.assert_owner(deps.storage, &sender)?; + + let _validators = state.validators.update(deps.storage, |mut validators| { + if !validators.contains(&validator) { + return Err(StdError::generic_err("validator is not already whitelisted")); + } + validators.retain(|v| *v != validator); + Ok(validators) + })?; + + let event = Event::new("steak/validator_removed_ex").add_attribute("validator", validator); + + Ok(Response::new().add_event(event).add_attribute("action", "steakhub/remove_validator_ex")) +} + pub fn transfer_ownership(deps: DepsMut, sender: Addr, new_owner: String) -> StdResult { let state = State::default(); diff --git a/packages/steak/src/hub.rs b/packages/steak/src/hub.rs index b793be1..748809e 100644 --- a/packages/steak/src/hub.rs +++ b/packages/steak/src/hub.rs @@ -44,6 +44,10 @@ pub enum ExecuteMsg { RemoveValidator { validator: String, }, + /// Remove a validator from the whitelist; callable by the owner. does not check delegations. use with caution + RemoveValidatorEx { + validator: String, + }, /// Transfer ownership to another account; will not take effect unless the new owner accepts TransferOwnership { new_owner: String, From 50ba42741502f295cd3a7ca634fba8f3d6fc1ac8 Mon Sep 17 00:00:00 2001 From: "PFC(Ian)" Date: Mon, 27 Jun 2022 07:55:07 -0500 Subject: [PATCH 05/77] fix: emergency method to remove a validator with an invalid address. does not undelegate --- Cargo.lock | 5 +++-- contracts/hub/Cargo.toml | 2 +- contracts/hub/src/contract.rs | 3 +++ contracts/hub/src/execute.rs | 27 ++++++++++++++++++++++ packages/steak/Cargo.toml | 2 +- packages/steak/src/hub.rs | 42 +++++++---------------------------- 6 files changed, 43 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2e4ee11..4397a06 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -567,7 +567,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "steak" -version = "2.0.0" +version = "2.1.2" dependencies = [ "cosmwasm-std", "cw20", @@ -577,10 +577,11 @@ dependencies = [ [[package]] name = "steak-hub" -version = "2.0.1" +version = "2.1.2" dependencies = [ "cosmwasm-std", "cw-storage-plus", + "cw2", "cw20", "cw20-base", "serde", diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index d156a69..b121184 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "steak-hub" -version = "2.1.1" +version = "2.1.2" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub/src/contract.rs b/contracts/hub/src/contract.rs index 236528d..b868de3 100644 --- a/contracts/hub/src/contract.rs +++ b/contracts/hub/src/contract.rs @@ -56,6 +56,9 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S ExecuteMsg::RemoveValidator { validator } => { execute::remove_validator(deps, env, info.sender, validator) } + ExecuteMsg::RemoveValidatorEx { validator } => { + execute::remove_validator_ex(deps, env, info.sender, validator) + } ExecuteMsg::TransferOwnership { new_owner } => { execute::transfer_ownership(deps, info.sender, new_owner) } diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index e5c159f..d7f74e8 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -677,6 +677,33 @@ pub fn remove_validator( .add_attribute("action", "steakhub/remove_validator")) } +pub fn remove_validator_ex( + deps: DepsMut, + _env: Env, + sender: Addr, + validator: String, +) -> StdResult { + let state = State::default(); + + state.assert_owner(deps.storage, &sender)?; + + state.validators.update(deps.storage, |mut validators| { + if !validators.contains(&validator) { + return Err(StdError::generic_err( + "validator is not already whitelisted", + )); + } + validators.retain(|v| *v != validator); + Ok(validators) + })?; + + let event = Event::new("steak/validator_removed_ex").add_attribute("validator", validator); + + Ok(Response::new() + .add_event(event) + .add_attribute("action", "steakhub/remove_validator_ex")) +} + pub fn transfer_ownership(deps: DepsMut, sender: Addr, new_owner: String) -> StdResult { let state = State::default(); diff --git a/packages/steak/Cargo.toml b/packages/steak/Cargo.toml index 2134f91..441e610 100644 --- a/packages/steak/Cargo.toml +++ b/packages/steak/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "steak" -version = "2.1.1" +version = "2.1.2" authors = ["larry ", "PFC "] edition = "2018" description = "Liquid steaking protocol for the cosmos" diff --git a/packages/steak/src/hub.rs b/packages/steak/src/hub.rs index 0761d98..9a014fc 100644 --- a/packages/steak/src/hub.rs +++ b/packages/steak/src/hub.rs @@ -44,6 +44,8 @@ pub enum ExecuteMsg { AddValidator { validator: String }, /// Remove a validator from the whitelist; callable by the owner RemoveValidator { validator: String }, + /// Remove a validator from the whitelist; callable by the owner. Does not undelegate. use for typos + RemoveValidatorEx { validator: String }, /// Transfer ownership to another account; will not take effect unless the new owner accepts TransferOwnership { new_owner: String }, /// Accept an ownership transfer @@ -56,6 +58,11 @@ pub enum ExecuteMsg { Reconcile {}, /// Submit the current pending batch of unbonding requests to be unbonded SubmitBatch {}, + + /// Transfer Fee collection account to another account + TransferFeeAccount { new_fee_account: String }, + /// Update fee collection amount + UpdateFee { new_fee: Decimal }, /// Callbacks; can only be invoked by the contract itself Callback(CallbackMsg), } @@ -65,40 +72,7 @@ pub enum ExecuteMsg { pub enum ReceiveMsg { /// Submit an unbonding request to the current unbonding queue; automatically invokes `unbond` /// if `epoch_time` has elapsed since when the last unbonding queue was executed. - QueueUnbond { - receiver: Option, - }, - Bond { - receiver: Option, - }, - WithdrawUnbonded { - receiver: Option, - }, - AddValidator { - validator: String, - }, - RemoveValidator { - validator: String, - }, - /// Remove a validator from the whitelist; callable by the owner. does not check delegations. use with caution - RemoveValidatorEx { - validator: String, - }, - TransferOwnership { - new_owner: String, - }, - /// Transfer Fee collection account to another account - TransferFeeAccount { - new_fee_account: String, - }, - /// Update fee collection amount - UpdateFee { - new_fee: Decimal, - }, - - QueueUnbond { - receiver: Option, - }, + QueueUnbond { receiver: Option }, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] From 0f61d10b41aa5a42b83d746fd9e3d799447198af Mon Sep 17 00:00:00 2001 From: "PFC(Ian)" Date: Wed, 29 Jun 2022 08:05:47 -0500 Subject: [PATCH 06/77] admin fn: allow admin to withdraw luna for user --- .gitignore | 2 +- Cargo.lock | 4 ++-- contracts/hub/Cargo.toml | 2 +- contracts/hub/src/contract.rs | 8 ++++++++ contracts/hub/src/execute.rs | 11 +++++++++++ packages/steak/Cargo.toml | 2 +- packages/steak/src/hub.rs | 4 ++++ 7 files changed, 28 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 4b75762..31618ac 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,4 @@ scripts/data/ scripts/keys/ # Misc notes -notes.txt +/notes*.txt diff --git a/Cargo.lock b/Cargo.lock index b54699a..3d91a5c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -567,7 +567,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "steak" -version = "2.0.0" +version = "2.0.2" dependencies = [ "cosmwasm-std", "cw20", @@ -577,7 +577,7 @@ dependencies = [ [[package]] name = "steak-hub" -version = "2.0.0" +version = "2.0.2" dependencies = [ "cosmwasm-std", "cw-storage-plus", diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index 84415c0..d99421e 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "steak-hub" -version = "2.0.0" +version = "2.0.2" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub/src/contract.rs b/contracts/hub/src/contract.rs index 99c0943..f22d514 100644 --- a/contracts/hub/src/contract.rs +++ b/contracts/hub/src/contract.rs @@ -41,6 +41,14 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S info.sender.clone(), receiver.map(|s| api.addr_validate(&s)).transpose()?.unwrap_or(info.sender), ), + ExecuteMsg::WithdrawUnbondedAdmin { + address, + } => execute::withdraw_unbonded_admin( + deps, + env, + info.sender.clone(), + api.addr_validate(&address)?, + ), ExecuteMsg::AddValidator { validator, } => execute::add_validator(deps, info.sender, validator), diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index 482e7ac..d52640c 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -453,7 +453,18 @@ pub fn reconcile(deps: DepsMut, env: Env) -> StdResult { .add_event(event) .add_attribute("action", "steakhub/reconcile")) } +pub fn withdraw_unbonded_admin( + deps: DepsMut, + env: Env, + user: Addr, + receiver: Addr, +) -> StdResult { + let state = State::default(); + state.assert_owner(deps.storage, &user)?; + + withdraw_unbonded(deps,env,receiver.clone(),receiver) +} pub fn withdraw_unbonded( deps: DepsMut, env: Env, diff --git a/packages/steak/Cargo.toml b/packages/steak/Cargo.toml index bbdd500..94e8847 100644 --- a/packages/steak/Cargo.toml +++ b/packages/steak/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "steak" -version = "2.0.0" +version = "2.0.2" authors = ["larry ", "PFC "] edition = "2018" description = "Liquid staking protocol for the cosmos" diff --git a/packages/steak/src/hub.rs b/packages/steak/src/hub.rs index b793be1..46a0fc7 100644 --- a/packages/steak/src/hub.rs +++ b/packages/steak/src/hub.rs @@ -36,6 +36,10 @@ pub enum ExecuteMsg { WithdrawUnbonded { receiver: Option, }, + /// Withdraw Luna that has finished unbonding in previous batches, for given address + WithdrawUnbondedAdmin { + address: String, + }, /// Add a validator to the whitelist; callable by the owner AddValidator { validator: String, From d7c5ac80461fb653c59b0a29704a658ba5275adb Mon Sep 17 00:00:00 2001 From: "PFC(Ian)" Date: Wed, 29 Jun 2022 08:08:12 -0500 Subject: [PATCH 07/77] admin fn: allow admin to withdraw luna for user --- contracts/hub/src/execute.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index d52640c..9a004c9 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -530,7 +530,7 @@ pub fn withdraw_unbonded( amount: vec![Coin::new(total_uluna_to_refund.u128(), "uluna")], }); - let event = Event::new("steakhub/unbonded_withdrawn") + let event = Event::new("steakhub/unbonded_withdrawn_admin") .add_attribute("time", env.block.time.seconds().to_string()) .add_attribute("height", env.block.height.to_string()) .add_attribute("ids", ids.join(",")) @@ -541,7 +541,7 @@ pub fn withdraw_unbonded( Ok(Response::new() .add_message(refund_msg) .add_event(event) - .add_attribute("action", "steakhub/withdraw_unbonded")) + .add_attribute("action", "steakhub/withdraw_unbonded_admin")) } //-------------------------------------------------------------------------------------------------- From a060f7620c144ce9aedf56496149f7c07c68e727 Mon Sep 17 00:00:00 2001 From: "PFC(Ian)" Date: Wed, 29 Jun 2022 08:16:37 -0500 Subject: [PATCH 08/77] admin fn: allow admin to withdraw luna for user --- contracts/hub/src/execute.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index 9a004c9..d52640c 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -530,7 +530,7 @@ pub fn withdraw_unbonded( amount: vec![Coin::new(total_uluna_to_refund.u128(), "uluna")], }); - let event = Event::new("steakhub/unbonded_withdrawn_admin") + let event = Event::new("steakhub/unbonded_withdrawn") .add_attribute("time", env.block.time.seconds().to_string()) .add_attribute("height", env.block.height.to_string()) .add_attribute("ids", ids.join(",")) @@ -541,7 +541,7 @@ pub fn withdraw_unbonded( Ok(Response::new() .add_message(refund_msg) .add_event(event) - .add_attribute("action", "steakhub/withdraw_unbonded_admin")) + .add_attribute("action", "steakhub/withdraw_unbonded")) } //-------------------------------------------------------------------------------------------------- From cfe4dbc60b6107b1837f8d8789eb54b0db8b7871 Mon Sep 17 00:00:00 2001 From: "PFC(Ian)" Date: Wed, 29 Jun 2022 08:41:58 -0500 Subject: [PATCH 09/77] fix: batches should be reconciled regardless if the amounts need to be adjusted --- contracts/hub/src/execute.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index d52640c..a848b7d 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -435,7 +435,8 @@ pub fn reconcile(deps: DepsMut, env: Env) -> StdResult { reconcile_batches(&mut batches, uluna_expected - uluna_actual); } - for batch in &batches { + for batch in batches.iter_mut() { + batch.reconciled = true; state.previous_batches.save(deps.storage, batch.id, batch)?; } @@ -447,7 +448,10 @@ pub fn reconcile(deps: DepsMut, env: Env) -> StdResult { let event = Event::new("steakhub/reconciled") .add_attribute("ids", ids) - .add_attribute("uluna_deducted", uluna_to_deduct.to_string()); + .add_attribute("uluna_deducted", uluna_to_deduct.to_string()) + .add_attribute("uluna_actual", uluna_actual.to_string()) + .add_attribute("uluna_expected", uluna_expected.to_string()) + .add_attribute("uluna_expected_received", uluna_expected_received.to_string()); Ok(Response::new() .add_event(event) From 632623ec0c46f1d6e5c58003810a09c02677f18e Mon Sep 17 00:00:00 2001 From: "PFC(Ian)" Date: Wed, 29 Jun 2022 08:49:47 -0500 Subject: [PATCH 10/77] chore: add contracts to README --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index ac96c10..f2a5378 100644 --- a/README.md +++ b/README.md @@ -15,12 +15,12 @@ A previous version ([v1.0.0-rc0](https://github.com/st4k3h0us3/steak-contracts/r ### Mainnet (phoenix-1) -| Contract | Address | -| ------------------- | --------- | -| Steak Hub | [`tbd`]() | -| Steak Token | [`tbd`]() | -| STEAK-LUNA Pair | [`tbd`]() | -| STEAK-LUNA LP Token | [`tbd`]() | +| Contract | Address | +| ------------------- |-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Steak Hub | [`terra12e4v50xl33fnwkzltz9vu565snlmx65vdrk8e2644km09myewr8q538psc`](https://finder.terra.money/mainnet/address/terra12e4v50xl33fnwkzltz9vu565snlmx65vdrk8e2644km09myewr8q538psc) | +| Steak Token | [`terra1xumzh893lfa7ak5qvpwmnle5m5xp47t3suwwa9s0ydqa8d8s5faqn6x7al`](https://finder.terra.money/mainnet/address/terra1xumzh893lfa7ak5qvpwmnle5m5xp47t3suwwa9s0ydqa8d8s5faqn6x7al) | +| STEAK-LUNA Pair | [`terra1jynmf6gteg4rd03ztldan5j2dp78su4tc3hfvkve8dl068c2yppsk5uszc`](https://finder.terra.money/mainnet/address/terra1jynmf6gteg4rd03ztldan5j2dp78su4tc3hfvkve8dl068c2yppsk5uszc) | +| STEAK-LUNA LP Token | [`tbd`]() | ### Testnet (pisco-1) From 594326d5cb09b501fe6395e0d34b3db644ae1431 Mon Sep 17 00:00:00 2001 From: "PFC(Ian)" Date: Wed, 29 Jun 2022 09:00:16 -0500 Subject: [PATCH 11/77] fix: batches should be marked as reconciled regardless if they need to be adjusted or not --- contracts/hub/Cargo.toml | 2 +- contracts/hub/src/execute.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index b121184..68dfc36 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "steak-hub" -version = "2.1.2" +version = "2.1.3" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index d7f74e8..c6d50ec 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -486,7 +486,8 @@ pub fn reconcile(deps: DepsMut, env: Env) -> StdResult { reconcile_batches(&mut batches, native_expected - native_actual); } - for batch in &batches { + for batch in batches.iter_mut() { + batch.reconciled = true; state.previous_batches.save(deps.storage, batch.id, batch)?; } From d92973025010ccb13213f19c4dcb16ad65f8aa3a Mon Sep 17 00:00:00 2001 From: "PFC(Ian)" Date: Wed, 29 Jun 2022 09:30:38 -0500 Subject: [PATCH 12/77] feat: withdraw_unbonded_admin - allow admin to perform withdraw on behalf of a user --- Cargo.lock | 4 ++-- contracts/hub/src/contract.rs | 6 ++++++ contracts/hub/src/execute.rs | 12 ++++++++++++ packages/steak/Cargo.toml | 2 +- packages/steak/src/hub.rs | 2 ++ 5 files changed, 23 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4397a06..131cf8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -567,7 +567,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "steak" -version = "2.1.2" +version = "2.1.3" dependencies = [ "cosmwasm-std", "cw20", @@ -577,7 +577,7 @@ dependencies = [ [[package]] name = "steak-hub" -version = "2.1.2" +version = "2.1.3" dependencies = [ "cosmwasm-std", "cw-storage-plus", diff --git a/contracts/hub/src/contract.rs b/contracts/hub/src/contract.rs index b868de3..2202ec1 100644 --- a/contracts/hub/src/contract.rs +++ b/contracts/hub/src/contract.rs @@ -50,6 +50,12 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S .transpose()? .unwrap_or(info.sender), ), + ExecuteMsg::WithdrawUnbondedAdmin { address } => execute::withdraw_unbonded_admin( + deps, + env, + info.sender.clone(), + api.addr_validate(&address)?, + ), ExecuteMsg::AddValidator { validator } => { execute::add_validator(deps, info.sender, validator) } diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index c6d50ec..0617315 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -505,6 +505,18 @@ pub fn reconcile(deps: DepsMut, env: Env) -> StdResult { .add_event(event) .add_attribute("action", "steakhub/reconcile")) } +pub fn withdraw_unbonded_admin( + deps: DepsMut, + env: Env, + user: Addr, + receiver: Addr, +) -> StdResult { + let state = State::default(); + + state.assert_owner(deps.storage, &user)?; + + withdraw_unbonded(deps, env, receiver.clone(), receiver) +} pub fn withdraw_unbonded( deps: DepsMut, diff --git a/packages/steak/Cargo.toml b/packages/steak/Cargo.toml index 441e610..659967e 100644 --- a/packages/steak/Cargo.toml +++ b/packages/steak/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "steak" -version = "2.1.2" +version = "2.1.3" authors = ["larry ", "PFC "] edition = "2018" description = "Liquid steaking protocol for the cosmos" diff --git a/packages/steak/src/hub.rs b/packages/steak/src/hub.rs index 9a014fc..2826d0a 100644 --- a/packages/steak/src/hub.rs +++ b/packages/steak/src/hub.rs @@ -40,6 +40,8 @@ pub enum ExecuteMsg { Bond { receiver: Option }, /// Withdraw Luna that have finished unbonding in previous batches WithdrawUnbonded { receiver: Option }, + /// Withdraw Luna that has finished unbonding in previous batches, for given address + WithdrawUnbondedAdmin { address: String }, /// Add a validator to the whitelist; callable by the owner AddValidator { validator: String }, /// Remove a validator from the whitelist; callable by the owner From 985cacfe818b5c4076898f0223d8495d8b6b5d63 Mon Sep 17 00:00:00 2001 From: "PFC(Ian)" Date: Fri, 5 Aug 2022 14:27:59 -0500 Subject: [PATCH 13/77] bug: forgot to migrate 'batch' table. so revert to 'uluna' until migration --- Cargo.lock | 4 ++-- contracts/hub/Cargo.toml | 2 +- contracts/hub/src/contract.rs | 2 +- contracts/hub/src/execute.rs | 8 ++++---- contracts/hub/src/math.rs | 2 +- packages/steak/Cargo.toml | 2 +- packages/steak/src/hub.rs | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 131cf8f..98259b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -567,7 +567,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "steak" -version = "2.1.3" +version = "2.1.4" dependencies = [ "cosmwasm-std", "cw20", @@ -577,7 +577,7 @@ dependencies = [ [[package]] name = "steak-hub" -version = "2.1.3" +version = "2.1.4" dependencies = [ "cosmwasm-std", "cw-storage-plus", diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index 68dfc36..6766be8 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "steak-hub" -version = "2.1.3" +version = "2.1.4" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub/src/contract.rs b/contracts/hub/src/contract.rs index 2202ec1..a1b63c6 100644 --- a/contracts/hub/src/contract.rs +++ b/contracts/hub/src/contract.rs @@ -53,7 +53,7 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S ExecuteMsg::WithdrawUnbondedAdmin { address } => execute::withdraw_unbonded_admin( deps, env, - info.sender.clone(), + info.sender, api.addr_validate(&address)?, ), ExecuteMsg::AddValidator { validator } => { diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index 0617315..ac13609 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -403,7 +403,7 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { id: pending_batch.id, reconciled: false, total_shares: pending_batch.usteak_to_burn, - amount_unclaimed: amount_to_bond, + uluna_unclaimed: amount_to_bond, est_unbond_end_time: current_time + unbond_period, }, )?; @@ -467,7 +467,7 @@ pub fn reconcile(deps: DepsMut, env: Env) -> StdResult { .filter(|b| current_time > b.est_unbond_end_time) .collect::>(); - let native_expected_received: Uint128 = batches.iter().map(|b| b.amount_unclaimed).sum(); + let native_expected_received: Uint128 = batches.iter().map(|b| b.uluna_unclaimed).sum(); let denom = state.denom.load(deps.storage)?; let unlocked_coins = state.unlocked_coins.load(deps.storage)?; @@ -555,14 +555,14 @@ pub fn withdraw_unbonded( if let Ok(mut batch) = state.previous_batches.load(deps.storage, request.id) { if batch.reconciled && batch.est_unbond_end_time < current_time { let native_to_refund = batch - .amount_unclaimed + .uluna_unclaimed .multiply_ratio(request.shares, batch.total_shares); ids.push(request.id.to_string()); total_native_to_refund += native_to_refund; batch.total_shares -= request.shares; - batch.amount_unclaimed -= native_to_refund; + batch.uluna_unclaimed -= native_to_refund; if batch.total_shares.is_zero() { state.previous_batches.remove(deps.storage, request.id)?; diff --git a/contracts/hub/src/math.rs b/contracts/hub/src/math.rs index 3013aab..4e66753 100644 --- a/contracts/hub/src/math.rs +++ b/contracts/hub/src/math.rs @@ -228,7 +228,7 @@ pub(crate) fn reconcile_batches(batches: &mut [Batch], native_to_deduct: Uint128 let remainder_for_batch: u128 = if (i + 1) as u128 <= remainder { 1 } else { 0 }; let native_for_batch = native_per_batch + remainder_for_batch; - batch.amount_unclaimed -= Uint128::new(native_for_batch); + batch.uluna_unclaimed -= Uint128::new(native_for_batch); batch.reconciled = true; } } diff --git a/packages/steak/Cargo.toml b/packages/steak/Cargo.toml index 659967e..807e579 100644 --- a/packages/steak/Cargo.toml +++ b/packages/steak/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "steak" -version = "2.1.3" +version = "2.1.4" authors = ["larry ", "PFC "] edition = "2018" description = "Liquid steaking protocol for the cosmos" diff --git a/packages/steak/src/hub.rs b/packages/steak/src/hub.rs index 2826d0a..5bdcef7 100644 --- a/packages/steak/src/hub.rs +++ b/packages/steak/src/hub.rs @@ -181,7 +181,7 @@ pub struct Batch { /// Total amount of shares remaining this batch. Each `usteak` burned = 1 share pub total_shares: Uint128, /// Amount of `denom` in this batch that have not been claimed - pub amount_unclaimed: Uint128, + pub uluna_unclaimed: Uint128, /// Estimated time when this batch will finish unbonding pub est_unbond_end_time: u64, } From 15c5b1e216235f567b3cf964e7c808655feba5f3 Mon Sep 17 00:00:00 2001 From: "PFC(Ian)" Date: Fri, 12 Aug 2022 12:18:52 -0500 Subject: [PATCH 14/77] feat: Batch now has 'amount' not uluna. rebalance takes a minimum amount --- Cargo.lock | 4 +- contracts/hub/Cargo.toml | 7 ++- contracts/hub/src/contract.rs | 23 ++++++-- contracts/hub/src/execute.rs | 78 +++++++++++++++++++++++--- contracts/hub/src/lib.rs | 1 + contracts/hub/src/math.rs | 60 +++++++++++--------- contracts/hub/src/migrations.rs | 89 ++++++++++++++++++++++++++++++ contracts/hub/src/state.rs | 11 +++- contracts/hub/src/testing/tests.rs | 32 ++++++++++- packages/steak/Cargo.toml | 2 +- packages/steak/src/hub.rs | 9 ++- 11 files changed, 266 insertions(+), 50 deletions(-) create mode 100644 contracts/hub/src/migrations.rs diff --git a/Cargo.lock b/Cargo.lock index 98259b4..9fb5afb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -567,7 +567,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "steak" -version = "2.1.4" +version = "2.1.5" dependencies = [ "cosmwasm-std", "cw20", @@ -577,7 +577,7 @@ dependencies = [ [[package]] name = "steak-hub" -version = "2.1.4" +version = "2.1.6" dependencies = [ "cosmwasm-std", "cw-storage-plus", diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index 6766be8..d92204d 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "steak-hub" -version = "2.1.4" +version = "2.1.6" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" @@ -19,6 +19,7 @@ cw20 = "0.13" cw20-base = { version = "0.13", features = ["library"] } cw-storage-plus = "0.13" steak = { path = "../../packages/steak" } - -[dev-dependencies] serde = { version = "1.0.103", default-features = false, features = ["derive"] } + +#[dev-dependencies] +#serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/contracts/hub/src/contract.rs b/contracts/hub/src/contract.rs index a1b63c6..deef276 100644 --- a/contracts/hub/src/contract.rs +++ b/contracts/hub/src/contract.rs @@ -10,11 +10,12 @@ use crate::helpers::unwrap_reply; use crate::state::State; use crate::{execute, queries}; use cw2::{get_contract_version, set_contract_version, ContractVersion}; +use crate::migrations::ConfigV100; /// Contract name that is used for migration. -const CONTRACT_NAME: &str = "steak-hub"; +pub const CONTRACT_NAME: &str = "steak-hub"; /// Contract version that is used for migration. -const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); +pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); #[entry_point] pub fn instantiate( @@ -70,7 +71,7 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S } ExecuteMsg::AcceptOwnership {} => execute::accept_ownership(deps, info.sender), ExecuteMsg::Harvest {} => execute::harvest(deps, env), - ExecuteMsg::Rebalance {} => execute::rebalance(deps, env), + ExecuteMsg::Rebalance { minimum } => execute::rebalance(deps, env,minimum), ExecuteMsg::Reconcile {} => execute::reconcile(deps, env), ExecuteMsg::SubmitBatch {} => execute::submit_batch(deps, env), ExecuteMsg::TransferFeeAccount { new_fee_account } => { @@ -78,6 +79,8 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S } ExecuteMsg::UpdateFee { new_fee } => execute::update_fee(deps, info.sender, new_fee), ExecuteMsg::Callback(callback_msg) => callback(deps, env, info, callback_msg), + ExecuteMsg::PauseValidator { validator } => execute::pause_validator(deps,env, info.sender, validator), + ExecuteMsg::UnPauseValidator { validator } => execute::unpause_validator(deps, env,info.sender, validator), } } @@ -192,6 +195,13 @@ pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult { + ConfigV100::upgrade_stores(deps.storage)?; + } + "2.1.5" => { + ConfigV100::upgrade_stores(deps.storage)?; } _ => {} }, @@ -202,5 +212,10 @@ pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult StdResult) -> StdRes let denom = state.denom.load(deps.storage)?; let amount_to_bond = parse_received_fund(&funds, &denom)?; let steak_token = state.steak_token.load(deps.storage)?; - let validators = state.validators.load(deps.storage)?; + let validators = state.validators_active.load(deps.storage)?; // Query the current delegations made to validators, and find the validator with the smallest // delegated amount through a linear search @@ -203,7 +204,7 @@ pub fn reinvest(deps: DepsMut, env: Env) -> StdResult { let denom = state.denom.load(deps.storage)?; let fee = state.fee_rate.load(deps.storage)?; - let validators = state.validators.load(deps.storage)?; + let validators = state.validators_active.load(deps.storage)?; let mut unlocked_coins = state.unlocked_coins.load(deps.storage)?; let amount_to_bond = unlocked_coins @@ -403,7 +404,7 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { id: pending_batch.id, reconciled: false, total_shares: pending_batch.usteak_to_burn, - uluna_unclaimed: amount_to_bond, + amount_unclaimed: amount_to_bond, est_unbond_end_time: current_time + unbond_period, }, )?; @@ -467,7 +468,7 @@ pub fn reconcile(deps: DepsMut, env: Env) -> StdResult { .filter(|b| current_time > b.est_unbond_end_time) .collect::>(); - let native_expected_received: Uint128 = batches.iter().map(|b| b.uluna_unclaimed).sum(); + let native_expected_received: Uint128 = batches.iter().map(|b| b.amount_unclaimed).sum(); let denom = state.denom.load(deps.storage)?; let unlocked_coins = state.unlocked_coins.load(deps.storage)?; @@ -555,14 +556,14 @@ pub fn withdraw_unbonded( if let Ok(mut batch) = state.previous_batches.load(deps.storage, request.id) { if batch.reconciled && batch.est_unbond_end_time < current_time { let native_to_refund = batch - .uluna_unclaimed + .amount_unclaimed .multiply_ratio(request.shares, batch.total_shares); ids.push(request.id.to_string()); total_native_to_refund += native_to_refund; batch.total_shares -= request.shares; - batch.uluna_unclaimed -= native_to_refund; + batch.amount_unclaimed -= native_to_refund; if batch.total_shares.is_zero() { state.previous_batches.remove(deps.storage, request.id)?; @@ -606,14 +607,15 @@ pub fn withdraw_unbonded( // Ownership and management logics //-------------------------------------------------------------------------------------------------- -pub fn rebalance(deps: DepsMut, env: Env) -> StdResult { +pub fn rebalance(deps: DepsMut, env: Env,minimum:Uint128) -> StdResult { let state = State::default(); let denom = state.denom.load(deps.storage)?; let validators = state.validators.load(deps.storage)?; + let validators_active = state.validators_active.load(deps.storage)?; let delegations = query_delegations(&deps.querier, &validators, &env.contract.address, &denom)?; - let new_redelegations = compute_redelegations_for_rebalancing(&delegations); + let new_redelegations = compute_redelegations_for_rebalancing(validators_active,&delegations,minimum); let redelegate_submsgs = new_redelegations .iter() @@ -643,6 +645,12 @@ pub fn add_validator(deps: DepsMut, sender: Addr, validator: String) -> StdResul Ok(validators) })?; + let mut validators_active = state.validators_active.load(deps.storage)?; + if !validators_active.contains(&validator) { + validators_active.push(validator.clone()); + + } + state.validators_active.save(deps.storage, &validators_active)?; let event = Event::new("steakhub/validator_added").add_attribute("validator", validator); Ok(Response::new() @@ -670,6 +678,12 @@ pub fn remove_validator( validators.retain(|v| *v != validator); Ok(validators) })?; + let mut validators_active = state.validators_active.load(deps.storage)?; + if !validators_active.contains(&validator) { + validators_active.push(validator.clone()); + + } + state.validators_active.save(deps.storage, &validators_active)?; let delegations = query_delegations(&deps.querier, &validators, &env.contract.address, &denom)?; let delegation_to_remove = @@ -716,6 +730,54 @@ pub fn remove_validator_ex( .add_event(event) .add_attribute("action", "steakhub/remove_validator_ex")) } +pub fn pause_validator( + deps: DepsMut, + _env: Env, + sender: Addr, + validator: String, +) -> StdResult { + let state = State::default(); + + state.assert_owner(deps.storage, &sender)?; + + state.validators_active.update(deps.storage, |mut validators| { + if !validators.contains(&validator) { + return Err(StdError::generic_err( + "validator is not already whitelisted", + )); + } + validators.retain(|v| *v != validator); + Ok(validators) + })?; + + let event = Event::new("steak/pause_validator").add_attribute("validator", validator); + + Ok(Response::new() + .add_event(event) + .add_attribute("action", "steakhub/pause_validator")) +} + +pub fn unpause_validator( + deps: DepsMut, + _env: Env, + sender: Addr, + validator: String, +) -> StdResult { + let state = State::default(); + + state.assert_owner(deps.storage, &sender)?; + let mut validators_active = state.validators_active.load(deps.storage)?; + if !validators_active.contains(&validator) { + validators_active.push(validator.clone()); + } + state.validators_active.save(deps.storage, &validators_active)?; + + let event = Event::new("steak/unpause_validator").add_attribute("validator", validator); + + Ok(Response::new() + .add_event(event) + .add_attribute("action", "steakhub/unpause_validator")) +} pub fn transfer_ownership(deps: DepsMut, sender: Addr, new_owner: String) -> StdResult { let state = State::default(); diff --git a/contracts/hub/src/lib.rs b/contracts/hub/src/lib.rs index fa16077..f2c0ace 100644 --- a/contracts/hub/src/lib.rs +++ b/contracts/hub/src/lib.rs @@ -10,3 +10,4 @@ pub mod types; #[cfg(test)] mod testing; +mod migrations; diff --git a/contracts/hub/src/math.rs b/contracts/hub/src/math.rs index 4e66753..6020e5d 100644 --- a/contracts/hub/src/math.rs +++ b/contracts/hub/src/math.rs @@ -143,10 +143,12 @@ pub(crate) fn compute_redelegations_for_removal( /// /// This algorithm does not guarantee the minimal number of moves, but is the best I can some up with... pub(crate) fn compute_redelegations_for_rebalancing( + validators_active:Vec, current_delegations: &[Delegation], + min_difference: Uint128 ) -> Vec { let native_staked: u128 = current_delegations.iter().map(|d| d.amount).sum(); - let validator_count = current_delegations.len() as u128; + let validator_count = validators_active.len() as u128; let native_per_validator = native_staked / validator_count; let remainder = native_staked % validator_count; @@ -160,24 +162,31 @@ pub(crate) fn compute_redelegations_for_rebalancing( for (i, d) in current_delegations.iter().enumerate() { let remainder_for_validator: u128 = if (i + 1) as u128 <= remainder { 1 } else { 0 }; let native_for_validator = native_per_validator + remainder_for_validator; - - match d.amount.cmp(&native_for_validator) { - Ordering::Greater => { - src_delegations.push(Delegation::new( - &d.validator, - d.amount - native_for_validator, - &d.denom, - )); + // eprintln!("{} amount ={} native={} min={}", d.validator, d.amount, native_for_validator, min_difference); + match d.amount.cmp(&native_for_validator) { + Ordering::Greater => { + if d.amount - native_for_validator > min_difference.u128() { + src_delegations.push(Delegation::new( + &d.validator, + d.amount - native_for_validator, + &d.denom, + )); + } + } + Ordering::Less => { + if validators_active.contains(&d.validator) { + if native_for_validator - d.amount > min_difference.u128() { + dst_delegations.push(Delegation::new( + &d.validator, + native_for_validator - d.amount, + &d.denom, + )); + } + } + } + Ordering::Equal => (), } - Ordering::Less => { - dst_delegations.push(Delegation::new( - &d.validator, - native_for_validator - d.amount, - &d.denom, - )); - } - Ordering::Equal => (), - } + } let mut new_redelegations: Vec = vec![]; @@ -197,14 +206,15 @@ pub(crate) fn compute_redelegations_for_rebalancing( } else { dst_delegations[0].amount -= native_to_redelegate; } + new_redelegations.push(Redelegation::new( + &src_delegation.validator, + &dst_delegation.validator, + native_to_redelegate, + &src_delegation.denom, + )); - new_redelegations.push(Redelegation::new( - &src_delegation.validator, - &dst_delegation.validator, - native_to_redelegate, - &src_delegation.denom, - )); } + // eprintln!("new redelegations ={:?}", new_redelegations); new_redelegations } @@ -228,7 +238,7 @@ pub(crate) fn reconcile_batches(batches: &mut [Batch], native_to_deduct: Uint128 let remainder_for_batch: u128 = if (i + 1) as u128 <= remainder { 1 } else { 0 }; let native_for_batch = native_per_batch + remainder_for_batch; - batch.uluna_unclaimed -= Uint128::new(native_for_batch); + batch.amount_unclaimed -= Uint128::new(native_for_batch); batch.reconciled = true; } } diff --git a/contracts/hub/src/migrations.rs b/contracts/hub/src/migrations.rs new file mode 100644 index 0000000..6f18943 --- /dev/null +++ b/contracts/hub/src/migrations.rs @@ -0,0 +1,89 @@ +use crate::state::{State, BATCH_KEY_V101}; +use crate::types::BooleanKey; +use cosmwasm_std::{Order, StdError, StdResult, Storage, Uint128}; +use cw_storage_plus::{Index, IndexList, IndexedMap, MultiIndex}; +use steak::hub::Batch; + +use serde::{Deserialize, Serialize}; + +const BATCH_KEY_V100: &str = "previous_batches"; +const BATCH_KEY_RECONCILED_V100: &str = "previous_batches__reconciled"; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct BatchV100 { + /// ID of this batch + pub id: u64, + /// Whether this batch has already been reconciled + pub reconciled: bool, + /// Total amount of shares remaining this batch. Each `usteak` burned = 1 share + pub total_shares: Uint128, + /// Amount of `denom` in this batch that have not been claimed + pub uluna_unclaimed: Uint128, + /// Estimated time when this batch will finish unbonding + pub est_unbond_end_time: u64, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct ConfigV100 {} + +impl ConfigV100 { + pub fn upgrade_stores(storage: &mut dyn Storage) -> StdResult { + if BATCH_KEY_V101 == BATCH_KEY_V100 { + Err(StdError::generic_err( + "STEAK: Migration Failed. Config keys are the same", + )) + } else { + let pb_indexes_v100 = PreviousBatchesIndexesV100 { + reconciled: MultiIndex::new( + |d: &BatchV100| d.reconciled.into(), + BATCH_KEY_V100, + BATCH_KEY_RECONCILED_V100, + ), + }; + + let old: IndexedMap<'_, u64, BatchV100, PreviousBatchesIndexesV100<'_>> = + IndexedMap::new(BATCH_KEY_V100, pb_indexes_v100); + let state = State::default(); + + let old_batches = old + .range(storage, None, None, Order::Ascending) + .map(|item| { + let (_, v) = item?; + Ok(v) + // Ok(v) + }) + .collect::>>()?; + + { + old_batches.into_iter().for_each(|v| { + { + let batch: Batch = Batch { + id: v.id, + reconciled: v.reconciled, + total_shares: v.total_shares, + amount_unclaimed: v.uluna_unclaimed, + est_unbond_end_time: v.est_unbond_end_time, + }; + state.previous_batches.save(storage, v.id, &batch).unwrap(); + } + // Ok(v) + }); + let validators = state.validators.load(storage)?; + state.validators_active.save(storage, &validators)?; + Ok(ConfigV100 {}) + } + } + } +} + +pub(crate) struct PreviousBatchesIndexesV100<'a> { + // pk goes to second tuple element + pub reconciled: MultiIndex<'a, BooleanKey, BatchV100, Vec>, +} + +impl<'a> IndexList for PreviousBatchesIndexesV100<'a> { + fn get_indexes(&'_ self) -> Box> + '_> { + let v: Vec<&dyn Index> = vec![&self.reconciled]; + Box::new(v.into_iter()) + } +} diff --git a/contracts/hub/src/state.rs b/contracts/hub/src/state.rs index 8f88f78..b1a011d 100644 --- a/contracts/hub/src/state.rs +++ b/contracts/hub/src/state.rs @@ -4,6 +4,8 @@ use cw_storage_plus::{Index, IndexList, IndexedMap, Item, MultiIndex}; use steak::hub::{Batch, PendingBatch, UnbondRequest}; use crate::types::BooleanKey; +pub(crate) const BATCH_KEY_V101: &str = "previous_batches_101"; +pub(crate) const BATCH_KEY_RECONCILED_V101: &str = "previous_batches__reconciled_101"; pub(crate) struct State<'a> { /// Account who can call certain privileged functions @@ -26,6 +28,7 @@ pub(crate) struct State<'a> { pub unbond_period: Item<'a, u64>, /// Validators who will receive the delegations pub validators: Item<'a, Vec>, + /// Coins that can be reinvested pub unlocked_coins: Item<'a, Vec>, /// The current batch of unbonding requests queded to be executed @@ -35,6 +38,7 @@ pub(crate) struct State<'a> { pub previous_batches: IndexedMap<'a, u64, Batch, PreviousBatchesIndexes<'a>>, /// Users' shares in unbonding batches pub unbond_requests: IndexedMap<'a, (u64, &'a Addr), UnbondRequest, UnbondRequestsIndexes<'a>>, + pub validators_active: Item<'a, Vec>, } impl Default for State<'static> { @@ -42,8 +46,8 @@ impl Default for State<'static> { let pb_indexes = PreviousBatchesIndexes { reconciled: MultiIndex::new( |d: &Batch| d.reconciled.into(), - "previous_batches", - "previous_batches__reconciled", + BATCH_KEY_V101, + BATCH_KEY_RECONCILED_V101, ), }; let ubr_indexes = UnbondRequestsIndexes { @@ -66,8 +70,9 @@ impl Default for State<'static> { validators: Item::new("validators"), unlocked_coins: Item::new("unlocked_coins"), pending_batch: Item::new("pending_batch"), - previous_batches: IndexedMap::new("previous_batches", pb_indexes), + previous_batches: IndexedMap::new(BATCH_KEY_V101, pb_indexes), unbond_requests: IndexedMap::new("unbond_requests", ubr_indexes), + validators_active: Item::new("validators_active"), } } } diff --git a/contracts/hub/src/testing/tests.rs b/contracts/hub/src/testing/tests.rs index 788894e..19ecaa6 100644 --- a/contracts/hub/src/testing/tests.rs +++ b/contracts/hub/src/testing/tests.rs @@ -53,6 +53,7 @@ fn setup_test() -> OwnedDeps { "bob".to_string(), "charlie".to_string(), ], + }, ) .unwrap(); @@ -1546,7 +1547,9 @@ fn computing_redelegations_for_rebalancing() { Delegation::new("dave", 40471, "uxyz"), Delegation::new("evan", 2345, "uxyz"), ]; - + let active_validators:Vec = vec!["alice".to_string(),"bob".to_string(), + "charlie".to_string(), "dave".to_string(), + "evan".to_string()]; // uluna_per_validator = (69420 + 88888 + 1234 + 40471 + 2345) / 4 = 40471 // remainer = 3 // src_delegations: @@ -1578,9 +1581,34 @@ fn computing_redelegations_for_rebalancing() { ]; assert_eq!( - compute_redelegations_for_rebalancing(¤t_delegations), + compute_redelegations_for_rebalancing(active_validators,¤t_delegations,Uint128::from(10 as u64)), expected, ); + + + let partially_active = vec![ "alice".to_string(), + "charlie".to_string(), + "dave".to_string(), + "evan".to_string()]; + + let partially_expected = vec![ + Redelegation::new("alice", "dave", 10118, "uxyz"), + Redelegation::new("alice", "evan", 8712, "uxyz"), + Redelegation::new("charlie", "evan", 38299, "uxyz"), + ]; + assert_eq!( + compute_redelegations_for_rebalancing(partially_active.clone(),¤t_delegations,Uint128::from(10 as u64)), + partially_expected, + ); + + let partially_expected_minimums = vec![ + Redelegation::new("alice", "evan", 18830, "uxyz"), + Redelegation::new("charlie", "evan", 29414, "uxyz"), + ]; + assert_eq!( + compute_redelegations_for_rebalancing(partially_active,¤t_delegations,Uint128::from(15_000 as u64)), + partially_expected_minimums, + ); } //-------------------------------------------------------------------------------------------------- diff --git a/packages/steak/Cargo.toml b/packages/steak/Cargo.toml index 807e579..9aa500c 100644 --- a/packages/steak/Cargo.toml +++ b/packages/steak/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "steak" -version = "2.1.4" +version = "2.1.5" authors = ["larry ", "PFC "] edition = "2018" description = "Liquid steaking protocol for the cosmos" diff --git a/packages/steak/src/hub.rs b/packages/steak/src/hub.rs index 5bdcef7..1c300bf 100644 --- a/packages/steak/src/hub.rs +++ b/packages/steak/src/hub.rs @@ -48,6 +48,11 @@ pub enum ExecuteMsg { RemoveValidator { validator: String }, /// Remove a validator from the whitelist; callable by the owner. Does not undelegate. use for typos RemoveValidatorEx { validator: String }, + /// Pause a validator from accepting new delegations + PauseValidator { validator: String }, + /// Unpause a validator from accepting new delegations + UnPauseValidator { validator: String }, + /// Transfer ownership to another account; will not take effect unless the new owner accepts TransferOwnership { new_owner: String }, /// Accept an ownership transfer @@ -55,7 +60,7 @@ pub enum ExecuteMsg { /// Claim staking rewards, swap all for Luna, and restake Harvest {}, /// Use redelegations to balance the amounts of Luna delegated to validators - Rebalance {}, + Rebalance { minimum: Uint128 }, /// Update Luna amounts in unbonding batches to reflect any slashing or rounding errors Reconcile {}, /// Submit the current pending batch of unbonding requests to be unbonded @@ -181,7 +186,7 @@ pub struct Batch { /// Total amount of shares remaining this batch. Each `usteak` burned = 1 share pub total_shares: Uint128, /// Amount of `denom` in this batch that have not been claimed - pub uluna_unclaimed: Uint128, + pub amount_unclaimed: Uint128, /// Estimated time when this batch will finish unbonding pub est_unbond_end_time: u64, } From 2c689e579ac93a9d2af97c35ae3ba3855527887a Mon Sep 17 00:00:00 2001 From: "PFC(Ian)" Date: Fri, 12 Aug 2022 12:44:40 -0500 Subject: [PATCH 15/77] merge multichain into main. --- Cargo.lock | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3d91a5c..9fb5afb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -567,7 +567,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "steak" -version = "2.0.2" +version = "2.1.5" dependencies = [ "cosmwasm-std", "cw20", @@ -577,10 +577,11 @@ dependencies = [ [[package]] name = "steak-hub" -version = "2.0.2" +version = "2.1.6" dependencies = [ "cosmwasm-std", "cw-storage-plus", + "cw2", "cw20", "cw20-base", "serde", From 9fac919687f86c0ef44d3a122e456a1da6882184 Mon Sep 17 00:00:00 2001 From: "PFC(Ian)" Date: Tue, 23 Aug 2022 15:19:29 -0500 Subject: [PATCH 16/77] ignore notes dir --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 31618ac..848aa62 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,4 @@ scripts/keys/ # Misc notes /notes*.txt +/notes From 54403ba57ba0b0b5f42a6170641fe9aad2b967f2 Mon Sep 17 00:00:00 2001 From: "PFC(Ian)" Date: Fri, 26 Aug 2022 11:19:18 -0500 Subject: [PATCH 17/77] fix: change error message to say 'no rewards' explictly. --- Cargo.lock | 2 +- contracts/hub/Cargo.toml | 2 +- contracts/hub/src/execute.rs | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9fb5afb..3752d54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -577,7 +577,7 @@ dependencies = [ [[package]] name = "steak-hub" -version = "2.1.6" +version = "2.1.7" dependencies = [ "cosmwasm-std", "cw-storage-plus", diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index d92204d..032a3ac 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "steak-hub" -version = "2.1.6" +version = "2.1.7" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index 32cb1b0..96c34fa 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -207,6 +207,9 @@ pub fn reinvest(deps: DepsMut, env: Env) -> StdResult { let validators = state.validators_active.load(deps.storage)?; let mut unlocked_coins = state.unlocked_coins.load(deps.storage)?; + if unlocked_coins.is_empty() { + return Err( StdError::generic_err("no rewards")) + } let amount_to_bond = unlocked_coins .iter() .find(|coin| coin.denom == denom) From a3066ba87c2714331b9e959c1be0919f402fd6d4 Mon Sep 17 00:00:00 2001 From: "PFC(Ian)" Date: Sun, 28 Aug 2022 11:09:43 -0500 Subject: [PATCH 18/77] feat: 2.1.8. change method of determining how much staking rewards were generated. --- Cargo.lock | 2 +- contracts/hub/Cargo.toml | 2 +- contracts/hub/src/contract.rs | 52 +++++++++++------- contracts/hub/src/execute.rs | 87 ++++++++++++++++++++---------- contracts/hub/src/helpers.rs | 14 +++-- contracts/hub/src/math.rs | 6 +-- contracts/hub/src/migrations.rs | 9 +++- contracts/hub/src/state.rs | 5 +- contracts/hub/src/testing/tests.rs | 30 ++++++----- 9 files changed, 134 insertions(+), 73 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3752d54..d1c326e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -577,7 +577,7 @@ dependencies = [ [[package]] name = "steak-hub" -version = "2.1.7" +version = "2.1.8" dependencies = [ "cosmwasm-std", "cw-storage-plus", diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index 032a3ac..f552acb 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "steak-hub" -version = "2.1.7" +version = "2.1.8" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub/src/contract.rs b/contracts/hub/src/contract.rs index deef276..13c33a5 100644 --- a/contracts/hub/src/contract.rs +++ b/contracts/hub/src/contract.rs @@ -6,16 +6,18 @@ use cw20::Cw20ReceiveMsg; use steak::hub::{CallbackMsg, ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, ReceiveMsg}; -use crate::helpers::unwrap_reply; +use crate::helpers::{get_denom_balance, unwrap_reply}; +use crate::migrations::ConfigV100; use crate::state::State; use crate::{execute, queries}; use cw2::{get_contract_version, set_contract_version, ContractVersion}; -use crate::migrations::ConfigV100; /// Contract name that is used for migration. pub const CONTRACT_NAME: &str = "steak-hub"; /// Contract version that is used for migration. pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); +pub const REPLY_INSTANTIATE_TOKEN: u64 = 1; +pub const REPLY_REGISTER_RECEIVED_COINS: u64 = 2; #[entry_point] pub fn instantiate( @@ -51,12 +53,9 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S .transpose()? .unwrap_or(info.sender), ), - ExecuteMsg::WithdrawUnbondedAdmin { address } => execute::withdraw_unbonded_admin( - deps, - env, - info.sender, - api.addr_validate(&address)?, - ), + ExecuteMsg::WithdrawUnbondedAdmin { address } => { + execute::withdraw_unbonded_admin(deps, env, info.sender, api.addr_validate(&address)?) + } ExecuteMsg::AddValidator { validator } => { execute::add_validator(deps, info.sender, validator) } @@ -71,7 +70,7 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S } ExecuteMsg::AcceptOwnership {} => execute::accept_ownership(deps, info.sender), ExecuteMsg::Harvest {} => execute::harvest(deps, env), - ExecuteMsg::Rebalance { minimum } => execute::rebalance(deps, env,minimum), + ExecuteMsg::Rebalance { minimum } => execute::rebalance(deps, env, minimum), ExecuteMsg::Reconcile {} => execute::reconcile(deps, env), ExecuteMsg::SubmitBatch {} => execute::submit_batch(deps, env), ExecuteMsg::TransferFeeAccount { new_fee_account } => { @@ -79,8 +78,12 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S } ExecuteMsg::UpdateFee { new_fee } => execute::update_fee(deps, info.sender, new_fee), ExecuteMsg::Callback(callback_msg) => callback(deps, env, info, callback_msg), - ExecuteMsg::PauseValidator { validator } => execute::pause_validator(deps,env, info.sender, validator), - ExecuteMsg::UnPauseValidator { validator } => execute::unpause_validator(deps, env,info.sender, validator), + ExecuteMsg::PauseValidator { validator } => { + execute::pause_validator(deps, env, info.sender, validator) + } + ExecuteMsg::UnPauseValidator { validator } => { + execute::unpause_validator(deps, env, info.sender, validator) + } } } @@ -134,7 +137,9 @@ fn callback( pub fn reply(deps: DepsMut, env: Env, reply: Reply) -> StdResult { match reply.id { 1 => execute::register_steak_token(deps, unwrap_reply(reply)?), - 2 => execute::register_received_coins(deps, env, unwrap_reply(reply)?.events), + REPLY_REGISTER_RECEIVED_COINS => { + execute::register_received_coins(deps, env, unwrap_reply(reply)?.events) + } id => Err(StdError::generic_err(format!( "invalid reply id: {}; must be 1-2", id @@ -176,7 +181,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { } #[entry_point] -pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult { +pub fn migrate(deps: DepsMut, env: Env, _msg: MigrateMsg) -> StdResult { let contract_version = match get_contract_version(deps.storage) { Ok(version) => version, Err(_) => ContractVersion { @@ -195,13 +200,22 @@ pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult { - ConfigV100::upgrade_stores(deps.storage)?; + ConfigV100::upgrade_stores(deps.storage,&deps.querier, env.contract.address)?; } - "2.1.5" => { - ConfigV100::upgrade_stores(deps.storage)?; + "2.1.4" => { + ConfigV100::upgrade_stores(deps.storage, &deps.querier,env.contract.address)?; + } + "2.1.5" => { + ConfigV100::upgrade_stores(deps.storage, &deps.querier,env.contract.address)?; + } + "2.1.6" | "2.1.7" => { + let state = State::default(); + // note: this is also done in ConfigV100::upgrade + let denom = state.denom.load(deps.storage)?; + state.prev_denom.save( + deps.storage, + &get_denom_balance(&deps.querier, env.contract.address, denom)?, + )?; } _ => {} }, diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index 96c34fa..6f26246 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -7,12 +7,11 @@ use cosmwasm_std::{ use cw20::{Cw20ExecuteMsg, MinterResponse}; use cw20_base::msg::InstantiateMsg as Cw20InstantiateMsg; +use crate::contract::{REPLY_INSTANTIATE_TOKEN, REPLY_REGISTER_RECEIVED_COINS}; use steak::hub::{Batch, CallbackMsg, ExecuteMsg, InstantiateMsg, PendingBatch, UnbondRequest}; use steak::DecimalCheckedOps; -use crate::helpers::{ - parse_received_fund, query_cw20_total_supply, query_delegation, query_delegations, -}; +use crate::helpers::{get_denom_balance, parse_received_fund, query_cw20_total_supply, query_delegation, query_delegations}; use crate::math::{ compute_mint_amount, compute_redelegations_for_rebalancing, compute_redelegations_for_removal, compute_unbond_amount, compute_undelegations, reconcile_batches, @@ -42,6 +41,7 @@ pub fn instantiate(deps: DepsMut, env: Env, msg: InstantiateMsg) -> StdResult StdResult StdResult) -> StdRes // Query the current supply of Steak and compute the amount to mint let usteak_supply = query_cw20_total_supply(&deps.querier, &steak_token)?; let usteak_to_mint = compute_mint_amount(usteak_supply, amount_to_bond, &delegations); + state.prev_denom.save(deps.storage, &get_denom_balance(&deps.querier,env.contract.address, denom.clone())?)?; - let delegate_submsg = SubMsg::reply_on_success(new_delegation.to_cosmos_msg(), 2); + let delegate_submsg = SubMsg::reply_on_success( + new_delegation.to_cosmos_msg(), + REPLY_REGISTER_RECEIVED_COINS, + ); let mint_msg: CosmosMsg = CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: steak_token.into(), @@ -171,6 +177,10 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec) -> StdRes } pub fn harvest(deps: DepsMut, env: Env) -> StdResult { + let state = State::default(); + let denom = state.denom.load(deps.storage)?; + state.prev_denom.save(deps.storage, &get_denom_balance(&deps.querier,env.contract.address.clone(), denom)?)?; + let withdraw_submsgs = deps .querier .query_all_delegations(&env.contract.address)? @@ -180,7 +190,7 @@ pub fn harvest(deps: DepsMut, env: Env) -> StdResult { CosmosMsg::Distribution(DistributionMsg::WithdrawDelegatorReward { validator: d.validator, }), - 2, + REPLY_REGISTER_RECEIVED_COINS, ) }) .collect::>(); @@ -205,17 +215,26 @@ pub fn reinvest(deps: DepsMut, env: Env) -> StdResult { let fee = state.fee_rate.load(deps.storage)?; let validators = state.validators_active.load(deps.storage)?; + let prev_coin = state.prev_denom.load(deps.storage)?; + let current_coin = get_denom_balance(&deps.querier,env.contract.address.clone(), denom.clone())?; + + if current_coin <= prev_coin { + return Err(StdError::generic_err("no rewards")); + } + let amount_to_bond = current_coin.saturating_sub(prev_coin); let mut unlocked_coins = state.unlocked_coins.load(deps.storage)?; + /* + if unlocked_coins.is_empty() { - return Err( StdError::generic_err("no rewards")) + return Err(StdError::generic_err("no rewards")); } let amount_to_bond = unlocked_coins .iter() .find(|coin| coin.denom == denom) .ok_or_else(|| StdError::generic_err("no native amount available to be bonded"))? .amount; - +*/ let delegations = query_delegations(&deps.querier, &validators, &env.contract.address, &denom)?; let mut validator = &delegations[0].validator; let mut amount = delegations[0].amount; @@ -421,10 +440,11 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { est_unbond_start_time: current_time + epoch_period, }, )?; + state.prev_denom.save(deps.storage, &get_denom_balance(&deps.querier,env.contract.address, denom)?)?; let undelegate_submsgs = new_undelegations .iter() - .map(|d| SubMsg::reply_on_success(d.to_cosmos_msg(), 2)) + .map(|d| SubMsg::reply_on_success(d.to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS)) .collect::>(); let burn_msg = CosmosMsg::Wasm(WasmMsg::Execute { @@ -610,7 +630,7 @@ pub fn withdraw_unbonded( // Ownership and management logics //-------------------------------------------------------------------------------------------------- -pub fn rebalance(deps: DepsMut, env: Env,minimum:Uint128) -> StdResult { +pub fn rebalance(deps: DepsMut, env: Env, minimum: Uint128) -> StdResult { let state = State::default(); let denom = state.denom.load(deps.storage)?; let validators = state.validators.load(deps.storage)?; @@ -618,11 +638,14 @@ pub fn rebalance(deps: DepsMut, env: Env,minimum:Uint128) -> StdResult let delegations = query_delegations(&deps.querier, &validators, &env.contract.address, &denom)?; - let new_redelegations = compute_redelegations_for_rebalancing(validators_active,&delegations,minimum); + let new_redelegations = + compute_redelegations_for_rebalancing(validators_active, &delegations, minimum); + + state.prev_denom.save(deps.storage, &get_denom_balance(&deps.querier,env.contract.address, denom)?)?; let redelegate_submsgs = new_redelegations .iter() - .map(|rd| SubMsg::reply_on_success(rd.to_cosmos_msg(), 2)) + .map(|rd| SubMsg::reply_on_success(rd.to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS)) .collect::>(); let amount: u128 = new_redelegations.iter().map(|rd| rd.amount).sum(); @@ -651,9 +674,10 @@ pub fn add_validator(deps: DepsMut, sender: Addr, validator: String) -> StdResul let mut validators_active = state.validators_active.load(deps.storage)?; if !validators_active.contains(&validator) { validators_active.push(validator.clone()); - } - state.validators_active.save(deps.storage, &validators_active)?; + state + .validators_active + .save(deps.storage, &validators_active)?; let event = Event::new("steakhub/validator_added").add_attribute("validator", validator); Ok(Response::new() @@ -684,9 +708,10 @@ pub fn remove_validator( let mut validators_active = state.validators_active.load(deps.storage)?; if !validators_active.contains(&validator) { validators_active.push(validator.clone()); - } - state.validators_active.save(deps.storage, &validators_active)?; + state + .validators_active + .save(deps.storage, &validators_active)?; let delegations = query_delegations(&deps.querier, &validators, &env.contract.address, &denom)?; let delegation_to_remove = @@ -694,9 +719,11 @@ pub fn remove_validator( let new_redelegations = compute_redelegations_for_removal(&delegation_to_remove, &delegations, &denom); + state.prev_denom.save(deps.storage, &get_denom_balance(&deps.querier,env.contract.address, denom)?)?; + let redelegate_submsgs = new_redelegations .iter() - .map(|d| SubMsg::reply_on_success(d.to_cosmos_msg(), 2)) + .map(|d| SubMsg::reply_on_success(d.to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS)) .collect::>(); let event = Event::new("steak/validator_removed").add_attribute("validator", validator); @@ -743,15 +770,17 @@ pub fn pause_validator( state.assert_owner(deps.storage, &sender)?; - state.validators_active.update(deps.storage, |mut validators| { - if !validators.contains(&validator) { - return Err(StdError::generic_err( - "validator is not already whitelisted", - )); - } - validators.retain(|v| *v != validator); - Ok(validators) - })?; + state + .validators_active + .update(deps.storage, |mut validators| { + if !validators.contains(&validator) { + return Err(StdError::generic_err( + "validator is not already whitelisted", + )); + } + validators.retain(|v| *v != validator); + Ok(validators) + })?; let event = Event::new("steak/pause_validator").add_attribute("validator", validator); @@ -773,7 +802,9 @@ pub fn unpause_validator( if !validators_active.contains(&validator) { validators_active.push(validator.clone()); } - state.validators_active.save(deps.storage, &validators_active)?; + state + .validators_active + .save(deps.storage, &validators_active)?; let event = Event::new("steak/unpause_validator").add_attribute("validator", validator); diff --git a/contracts/hub/src/helpers.rs b/contracts/hub/src/helpers.rs index ee62a4f..7e83105 100644 --- a/contracts/hub/src/helpers.rs +++ b/contracts/hub/src/helpers.rs @@ -1,9 +1,7 @@ use std::str::FromStr; -use cosmwasm_std::{ - Addr, Coin, QuerierWrapper, Reply, StdError, StdResult, SubMsgResponse, Uint128, -}; -use cw20::{Cw20QueryMsg, TokenInfoResponse}; +use cosmwasm_std::{Addr, BalanceResponse, BankQuery, Coin, QuerierWrapper, QueryRequest, Reply, StdError, StdResult, SubMsgResponse, Uint128}; +use cw20::{ Cw20QueryMsg, TokenInfoResponse}; use crate::types::Delegation; @@ -101,3 +99,11 @@ pub(crate) fn parse_received_fund(funds: &[Coin], denom: &str) -> StdResult StdResult { + let balance: BalanceResponse = querier.query(&QueryRequest::Bank(BankQuery::Balance { + address: account_addr.to_string(), + denom, + }))?; + Ok(balance.amount.amount) +} diff --git a/contracts/hub/src/math.rs b/contracts/hub/src/math.rs index 6020e5d..1d0860f 100644 --- a/contracts/hub/src/math.rs +++ b/contracts/hub/src/math.rs @@ -174,14 +174,14 @@ pub(crate) fn compute_redelegations_for_rebalancing( } } Ordering::Less => { - if validators_active.contains(&d.validator) { - if native_for_validator - d.amount > min_difference.u128() { + if validators_active.contains(&d.validator) && + native_for_validator - d.amount > min_difference.u128() { dst_delegations.push(Delegation::new( &d.validator, native_for_validator - d.amount, &d.denom, )); - } + } } Ordering::Equal => (), diff --git a/contracts/hub/src/migrations.rs b/contracts/hub/src/migrations.rs index 6f18943..8d19c95 100644 --- a/contracts/hub/src/migrations.rs +++ b/contracts/hub/src/migrations.rs @@ -1,10 +1,11 @@ use crate::state::{State, BATCH_KEY_V101}; use crate::types::BooleanKey; -use cosmwasm_std::{Order, StdError, StdResult, Storage, Uint128}; +use cosmwasm_std::{Addr, Order, QuerierWrapper, StdError, StdResult, Storage, Uint128}; use cw_storage_plus::{Index, IndexList, IndexedMap, MultiIndex}; use steak::hub::Batch; use serde::{Deserialize, Serialize}; +use crate::helpers::get_denom_balance; const BATCH_KEY_V100: &str = "previous_batches"; const BATCH_KEY_RECONCILED_V100: &str = "previous_batches__reconciled"; @@ -27,7 +28,7 @@ pub struct BatchV100 { pub struct ConfigV100 {} impl ConfigV100 { - pub fn upgrade_stores(storage: &mut dyn Storage) -> StdResult { + pub fn upgrade_stores(storage: &mut dyn Storage, querier: &QuerierWrapper, contract_addr: Addr,) -> StdResult { if BATCH_KEY_V101 == BATCH_KEY_V100 { Err(StdError::generic_err( "STEAK: Migration Failed. Config keys are the same", @@ -44,6 +45,10 @@ impl ConfigV100 { let old: IndexedMap<'_, u64, BatchV100, PreviousBatchesIndexesV100<'_>> = IndexedMap::new(BATCH_KEY_V100, pb_indexes_v100); let state = State::default(); + let denom = state.denom.load(storage)?; + state.prev_denom.save(storage, &get_denom_balance(querier,contract_addr, denom)?)?; + + let old_batches = old .range(storage, None, None, Order::Ascending) diff --git a/contracts/hub/src/state.rs b/contracts/hub/src/state.rs index b1a011d..2605522 100644 --- a/contracts/hub/src/state.rs +++ b/contracts/hub/src/state.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{Addr, Coin, Decimal, StdError, StdResult, Storage}; +use cosmwasm_std::{Addr, Coin, Decimal, StdError, StdResult, Storage, Uint128}; use cw_storage_plus::{Index, IndexList, IndexedMap, Item, MultiIndex}; use steak::hub::{Batch, PendingBatch, UnbondRequest}; @@ -39,6 +39,8 @@ pub(crate) struct State<'a> { /// Users' shares in unbonding batches pub unbond_requests: IndexedMap<'a, (u64, &'a Addr), UnbondRequest, UnbondRequestsIndexes<'a>>, pub validators_active: Item<'a, Vec>, + /// coins in 'denom' held before reinvest was called. + pub prev_denom: Item<'a, Uint128>, } impl Default for State<'static> { @@ -73,6 +75,7 @@ impl Default for State<'static> { previous_batches: IndexedMap::new(BATCH_KEY_V101, pb_indexes), unbond_requests: IndexedMap::new("unbond_requests", ubr_indexes), validators_active: Item::new("validators_active"), + prev_denom: Item::new("prev_denom"), } } } diff --git a/contracts/hub/src/testing/tests.rs b/contracts/hub/src/testing/tests.rs index 19ecaa6..52bee9c 100644 --- a/contracts/hub/src/testing/tests.rs +++ b/contracts/hub/src/testing/tests.rs @@ -14,7 +14,7 @@ use steak::hub::{ UnbondRequestsByUserResponseItem, }; -use crate::contract::{execute, instantiate, reply}; +use crate::contract::{execute, instantiate, reply, REPLY_INSTANTIATE_TOKEN, REPLY_REGISTER_RECEIVED_COINS}; use crate::helpers::{parse_coin, parse_received_fund}; use crate::math::{ compute_redelegations_for_rebalancing, compute_redelegations_for_removal, compute_undelegations, @@ -80,7 +80,7 @@ fn setup_test() -> OwnedDeps { funds: vec![], label: "steak_token".to_string(), }), - 1 + REPLY_INSTANTIATE_TOKEN ) ); @@ -92,7 +92,7 @@ fn setup_test() -> OwnedDeps { deps.as_mut(), mock_env_at_timestamp(10000), Reply { - id: 1, + id: REPLY_INSTANTIATE_TOKEN, result: cosmwasm_std::SubMsgResult::Ok(SubMsgResponse { events: vec![event], data: None, @@ -175,7 +175,7 @@ fn bonding() { assert_eq!(res.messages.len(), 2); assert_eq!( res.messages[0], - SubMsg::reply_on_success(Delegation::new("alice", 1000000, "uxyz").to_cosmos_msg(), 2) + SubMsg::reply_on_success(Delegation::new("alice", 1000000, "uxyz").to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS) ); assert_eq!( res.messages[1], @@ -218,7 +218,7 @@ fn bonding() { assert_eq!(res.messages.len(), 2); assert_eq!( res.messages[0], - SubMsg::reply_on_success(Delegation::new("charlie", 12345, "uxyz").to_cosmos_msg(), 2) + SubMsg::reply_on_success(Delegation::new("charlie", 12345, "uxyz").to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS) ); assert_eq!( res.messages[1], @@ -285,7 +285,7 @@ fn harvesting() { CosmosMsg::Distribution(DistributionMsg::WithdrawDelegatorReward { validator: "alice".to_string(), }), - 2, + REPLY_REGISTER_RECEIVED_COINS, ) ); assert_eq!( @@ -294,7 +294,7 @@ fn harvesting() { CosmosMsg::Distribution(DistributionMsg::WithdrawDelegatorReward { validator: "bob".to_string(), }), - 2, + REPLY_REGISTER_RECEIVED_COINS, ) ); assert_eq!( @@ -303,7 +303,7 @@ fn harvesting() { CosmosMsg::Distribution(DistributionMsg::WithdrawDelegatorReward { validator: "charlie".to_string(), }), - 2, + REPLY_REGISTER_RECEIVED_COINS, ) ); assert_eq!( @@ -370,8 +370,10 @@ fn reinvesting() { Delegation::new("bob", 333333, "uxyz"), Delegation::new("charlie", 333333, "uxyz"), ]); + state.prev_denom.save(deps.as_mut().storage,&Uint128::from(0 as u32)).unwrap(); + deps.querier.set_bank_balances(&[Coin::new(234u128,"uxyz")]); - // After the swaps, `unlocked_coins` should contain only uluna and unknown denoms + // After the swaps, `unlocked_coins` should contain only uxyz and unknown denoms state .unlocked_coins .save( @@ -623,17 +625,17 @@ fn submitting_batch() { assert_eq!(res.messages.len(), 4); assert_eq!( res.messages[0], - SubMsg::reply_on_success(Undelegation::new("alice", 31732, "uxyz").to_cosmos_msg(), 2) + SubMsg::reply_on_success(Undelegation::new("alice", 31732, "uxyz").to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS) ); assert_eq!( res.messages[1], - SubMsg::reply_on_success(Undelegation::new("bob", 31733, "uxyz").to_cosmos_msg(), 2) + SubMsg::reply_on_success(Undelegation::new("bob", 31733, "uxyz").to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS) ); assert_eq!( res.messages[2], SubMsg::reply_on_success( Undelegation::new("charlie", 31732, "uxyz").to_cosmos_msg(), - 2 + REPLY_REGISTER_RECEIVED_COINS ) ); assert_eq!( @@ -1193,14 +1195,14 @@ fn removing_validator() { res.messages[0], SubMsg::reply_on_success( Redelegation::new("charlie", "alice", 170833, "uxyz").to_cosmos_msg(), - 2 + REPLY_REGISTER_RECEIVED_COINS ), ); assert_eq!( res.messages[1], SubMsg::reply_on_success( Redelegation::new("charlie", "bob", 170833, "uxyz").to_cosmos_msg(), - 2 + REPLY_REGISTER_RECEIVED_COINS ), ); From ed3546cfedee03ce3076dc8640f9f496a004fc35 Mon Sep 17 00:00:00 2001 From: "PFC(Ian)" Date: Thu, 20 Oct 2022 08:53:33 -0500 Subject: [PATCH 19/77] feat: 2.1.9. add ability to send fees to a smart contract (fee_split for now) --- Cargo.lock | 280 +++++++++++++++++++++-------- contracts/hub/Cargo.toml | 4 +- contracts/hub/src/contract.rs | 21 ++- contracts/hub/src/execute.rs | 102 ++++++++--- contracts/hub/src/migrations.rs | 4 +- contracts/hub/src/queries.rs | 1 + contracts/hub/src/state.rs | 4 +- contracts/hub/src/testing/tests.rs | 277 ++++++++++++++++++++++++++++ packages/steak/Cargo.toml | 2 +- packages/steak/src/hub.rs | 59 ++++-- 10 files changed, 641 insertions(+), 113 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d1c326e..ffdd478 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,6 +29,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + [[package]] name = "byteorder" version = "1.4.3" @@ -43,17 +52,17 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "const-oid" -version = "0.7.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" +checksum = "722e23542a15cea1f65d4a1419c4cfd7a26706c70871a13a04238ca3f40f1661" [[package]] name = "cosmwasm-crypto" -version = "1.0.0" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb0afef2325df81aadbf9be1233f522ed8f6e91df870c764bc44cca2b1415bd" +checksum = "28376836c7677e1ea6d6656a754582e88b91e544ce22fae42956d5fe5549a958" dependencies = [ - "digest", + "digest 0.10.5", "ed25519-zebra", "k256", "rand_core 0.6.3", @@ -62,23 +71,49 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.0.0" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b36e527620a2a3e00e46b6e731ab6c9b68d11069c986f7d7be8eba79ef081a4" +checksum = "8eb69f4f7a8a4bce68c8fbd3646238fede1e77056e4ea31c5b6bfc37b709eec3" dependencies = [ "syn", ] +[[package]] +name = "cosmwasm-schema" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a227cfeb9a7152b26a354b1c990e930e962f75fd68f57ab5ae2ef888c8524292" +dependencies = [ + "cosmwasm-schema-derive", + "schemars", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cosmwasm-schema-derive" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3626cb42eef870de67f791e873711255325224d86f281bf628c42abd295f3a14" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "cosmwasm-std" -version = "1.0.0" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875994993c2082a6fcd406937bf0fca21c349e4a624f3810253a14fa83a3a195" +checksum = "46bf9157d060abbc55152aeadcace799d03dc630575daa66604079a1206cb060" dependencies = [ "base64", "cosmwasm-crypto", "cosmwasm-derive", + "derivative", "forward_ref", + "hex", "schemars", "serde", "serde-json-wasm", @@ -103,9 +138,9 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-bigint" -version = "0.3.2" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" dependencies = [ "generic-array", "rand_core 0.6.3", @@ -114,13 +149,13 @@ dependencies = [ ] [[package]] -name = "crypto-mac" -version = "0.11.1" +name = "crypto-common" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", - "subtle", + "typenum", ] [[package]] @@ -130,7 +165,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" dependencies = [ "byteorder", - "digest", + "digest 0.9.0", "rand_core 0.5.1", "subtle", "zeroize", @@ -147,6 +182,17 @@ dependencies = [ "serde", ] +[[package]] +name = "cw-storage-plus" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6cf70ef7686e2da9ad7b067c5942cd3e88dd9453f7af42f54557f8af300fb0" +dependencies = [ + "cosmwasm-std", + "schemars", + "serde", +] + [[package]] name = "cw-utils" version = "0.13.4" @@ -159,6 +205,21 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cw-utils" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae0b69fa7679de78825b4edeeec045066aa2b2c4b6e063d80042e565bb4da5c" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw2 0.15.1", + "schemars", + "semver", + "serde", + "thiserror", +] + [[package]] name = "cw2" version = "0.13.4" @@ -166,7 +227,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04cf4639517490dd36b333bbd6c4fbd92e325fd0acf4683b41753bc5eb63bfc1" dependencies = [ "cosmwasm-std", - "cw-storage-plus", + "cw-storage-plus 0.13.4", + "schemars", + "serde", +] + +[[package]] +name = "cw2" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5abb8ecea72e09afff830252963cb60faf945ce6cef2c20a43814516082653da" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 0.15.1", "schemars", "serde", ] @@ -178,7 +252,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cb782b8f110819a4eb5dbbcfed25ffba49ec16bbe32b4ad8da50a5ce68fec05" dependencies = [ "cosmwasm-std", - "cw-utils", + "cw-utils 0.13.4", + "schemars", + "serde", +] + +[[package]] +name = "cw20" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6025276fb6e603e974c21f3e4606982cdc646080e8fba3198816605505e1d9a" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils 0.15.1", "schemars", "serde", ] @@ -190,10 +277,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0306e606581f4fb45e82bcbb7f0333179ed53dd949c6523f01a99b4bfc1475a0" dependencies = [ "cosmwasm-std", - "cw-storage-plus", - "cw-utils", - "cw2", - "cw20", + "cw-storage-plus 0.13.4", + "cw-utils 0.13.4", + "cw2 0.13.4", + "cw20 0.13.4", "schemars", "serde", "thiserror", @@ -201,11 +288,23 @@ dependencies = [ [[package]] name = "der" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" +checksum = "13dd2ae565c0a381dde7fade45fce95984c568bdcb4700a4fdbe3175e0380b2f" dependencies = [ "const-oid", + "zeroize", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -217,6 +316,17 @@ dependencies = [ "generic-array", ] +[[package]] +name = "digest" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" +dependencies = [ + "block-buffer 0.10.3", + "crypto-common", + "subtle", +] + [[package]] name = "dyn-clone" version = "1.0.5" @@ -225,9 +335,9 @@ checksum = "21e50f3adc76d6a43f5ed73b698a87d0760ca74617f60f7c3b879003536fdd28" [[package]] name = "ecdsa" -version = "0.13.4" +version = "0.14.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0d69ae62e0ce582d56380743515fefaf1a8c70cec685d9677636d7e30ae9dc9" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" dependencies = [ "der", "elliptic-curve", @@ -245,23 +355,25 @@ dependencies = [ "hex", "rand_core 0.6.3", "serde", - "sha2", + "sha2 0.9.9", "thiserror", "zeroize", ] [[package]] name = "elliptic-curve" -version = "0.11.12" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b477563c2bfed38a3b7a60964c49e058b2510ad3f12ba3483fd8f62c2306d6" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" dependencies = [ "base16ct", "crypto-bigint", "der", + "digest 0.10.5", "ff", "generic-array", "group", + "pkcs8", "rand_core 0.6.3", "sec1", "subtle", @@ -270,9 +382,9 @@ dependencies = [ [[package]] name = "ff" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "131655483be284720a17d74ff97592b8e76576dc25563148601df2d7c9080924" +checksum = "df689201f395c6b90dfe87127685f8dbfc083a5e779e613575d8bd7314300c3e" dependencies = [ "rand_core 0.6.3", "subtle", @@ -318,9 +430,9 @@ dependencies = [ [[package]] name = "group" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5ac374b108929de78460075f3dc439fa66df9d8fc77e8f12caa5165fcf0c89" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" dependencies = [ "ff", "rand_core 0.6.3", @@ -335,12 +447,11 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hmac" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "crypto-mac", - "digest", + "digest 0.10.5", ] [[package]] @@ -351,15 +462,14 @@ checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" [[package]] name = "k256" -version = "0.10.4" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19c3a5e0a0b8450278feda242592512e09f61c72e018b8cd5c859482802daf2d" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" dependencies = [ "cfg-if", "ecdsa", "elliptic-curve", - "sec1", - "sha2", + "sha2 0.10.6", ] [[package]] @@ -374,15 +484,26 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "pfc-fee-split" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320b2bc8ca3b95a29a2aa7c9805efaaae5faedb8bb5bff384f8a659416d45515" +dependencies = [ + "cosmwasm-std", + "cw20 0.15.1", + "schemars", + "serde", +] + [[package]] name = "pkcs8" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" dependencies = [ "der", "spki", - "zeroize", ] [[package]] @@ -423,9 +544,9 @@ dependencies = [ [[package]] name = "rfc6979" -version = "0.1.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ef608575f6392792f9ecf7890c00086591d29a83910939d430753f7c050525" +checksum = "88c86280f057430a52f4861551b092a01b419b8eacefc7c995eacb9dc132fe32" dependencies = [ "crypto-bigint", "hmac", @@ -440,9 +561,9 @@ checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" [[package]] name = "schemars" -version = "0.8.8" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6b5a3c80cea1ab61f4260238409510e814e38b4b563c06044edf91e7dc070e3" +checksum = "2a5fb6c61f29e723026dc8e923d94c694313212abbecbbe5f55a7748eec5b307" dependencies = [ "dyn-clone", "schemars_derive", @@ -452,9 +573,9 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.8" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ae4dce13e8614c46ac3c38ef1c0d668b101df6ac39817aebdaa26642ddae9b" +checksum = "f188d036977451159430f3b8dc82ec76364a42b7e289c2b18a9a18f4470058e9" dependencies = [ "proc-macro2", "quote", @@ -464,10 +585,11 @@ dependencies = [ [[package]] name = "sec1" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ + "base16ct", "der", "generic-array", "pkcs8", @@ -475,6 +597,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "semver" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" + [[package]] name = "serde" version = "1.0.136" @@ -506,9 +634,9 @@ dependencies = [ [[package]] name = "serde_derive_internals" -version = "0.25.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dbab34ca63057a1f15280bdf3c39f2b1eb1b54c17e98360e511637aef7418c6" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" dependencies = [ "proc-macro2", "quote", @@ -532,28 +660,39 @@ version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ - "block-buffer", + "block-buffer 0.9.0", "cfg-if", "cpufeatures", - "digest", + "digest 0.9.0", "opaque-debug", ] +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.5", +] + [[package]] name = "signature" -version = "1.3.2" +version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2807892cfa58e081aa1f1111391c7a0649d4fa127a4ffbe34bcbfb35a1171a4" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" dependencies = [ - "digest", + "digest 0.10.5", "rand_core 0.6.3", ] [[package]] name = "spki" -version = "0.5.4" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" dependencies = [ "base64ct", "der", @@ -567,23 +706,24 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "steak" -version = "2.1.5" +version = "2.1.6" dependencies = [ "cosmwasm-std", - "cw20", + "cw20 0.13.4", "schemars", "serde", ] [[package]] name = "steak-hub" -version = "2.1.8" +version = "2.1.9" dependencies = [ "cosmwasm-std", - "cw-storage-plus", - "cw2", - "cw20", + "cw-storage-plus 0.13.4", + "cw2 0.13.4", + "cw20 0.13.4", "cw20-base", + "pfc-fee-split", "serde", "steak", ] @@ -593,7 +733,7 @@ name = "steak-token" version = "2.0.0" dependencies = [ "cosmwasm-std", - "cw20", + "cw20 0.13.4", "cw20-base", ] @@ -678,6 +818,6 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "zeroize" -version = "1.4.3" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d68d9dcec5f9b43a30d38c49f91dfedfaac384cb8f085faca366c26207dd1619" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index f552acb..99a3983 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "steak-hub" -version = "2.1.8" +version = "2.1.9" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" @@ -20,6 +20,6 @@ cw20-base = { version = "0.13", features = ["library"] } cw-storage-plus = "0.13" steak = { path = "../../packages/steak" } serde = { version = "1.0.103", default-features = false, features = ["derive"] } - +pfc-fee-split = "0.1.0" #[dev-dependencies] #serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/contracts/hub/src/contract.rs b/contracts/hub/src/contract.rs index 13c33a5..34bf961 100644 --- a/contracts/hub/src/contract.rs +++ b/contracts/hub/src/contract.rs @@ -4,7 +4,7 @@ use cosmwasm_std::{ }; use cw20::Cw20ReceiveMsg; -use steak::hub::{CallbackMsg, ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, ReceiveMsg}; +use steak::hub::{CallbackMsg, ExecuteMsg, FeeType, InstantiateMsg, MigrateMsg, QueryMsg, ReceiveMsg}; use crate::helpers::{get_denom_balance, unwrap_reply}; use crate::migrations::ConfigV100; @@ -65,6 +65,7 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S ExecuteMsg::RemoveValidatorEx { validator } => { execute::remove_validator_ex(deps, env, info.sender, validator) } + ExecuteMsg::TransferOwnership { new_owner } => { execute::transfer_ownership(deps, info.sender, new_owner) } @@ -73,8 +74,8 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S ExecuteMsg::Rebalance { minimum } => execute::rebalance(deps, env, minimum), ExecuteMsg::Reconcile {} => execute::reconcile(deps, env), ExecuteMsg::SubmitBatch {} => execute::submit_batch(deps, env), - ExecuteMsg::TransferFeeAccount { new_fee_account } => { - execute::transfer_fee_account(deps, info.sender, new_fee_account) + ExecuteMsg::TransferFeeAccount { fee_account_type,new_fee_account } => { + execute::transfer_fee_account(deps, info.sender, fee_account_type, new_fee_account) } ExecuteMsg::UpdateFee { new_fee } => execute::update_fee(deps, info.sender, new_fee), ExecuteMsg::Callback(callback_msg) => callback(deps, env, info, callback_msg), @@ -84,6 +85,8 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S ExecuteMsg::UnPauseValidator { validator } => { execute::unpause_validator(deps, env, info.sender, validator) } + ExecuteMsg::SetUnbondPeriod { unbond_period } => execute::set_unbond_period(deps, env, info.sender, unbond_period), + } } @@ -200,13 +203,18 @@ pub fn migrate(deps: DepsMut, env: Env, _msg: MigrateMsg) -> StdResult state.fee_account.save(deps.storage, &owner)?; state.max_fee_rate.save(deps.storage, &Decimal::zero())?; state.fee_rate.save(deps.storage, &Decimal::zero())?; + state.fee_account_type.save(deps.storage,&FeeType::Wallet)?; ConfigV100::upgrade_stores(deps.storage,&deps.querier, env.contract.address)?; } "2.1.4" => { + let state = State::default(); ConfigV100::upgrade_stores(deps.storage, &deps.querier,env.contract.address)?; + state.fee_account_type.save(deps.storage,&FeeType::Wallet)?; } "2.1.5" => { ConfigV100::upgrade_stores(deps.storage, &deps.querier,env.contract.address)?; + let state = State::default(); + state.fee_account_type.save(deps.storage,&FeeType::Wallet)?; } "2.1.6" | "2.1.7" => { let state = State::default(); @@ -216,6 +224,13 @@ pub fn migrate(deps: DepsMut, env: Env, _msg: MigrateMsg) -> StdResult deps.storage, &get_denom_balance(&deps.querier, env.contract.address, denom)?, )?; + + state.fee_account_type.save(deps.storage,&FeeType::Wallet)?; + + }, + "2.1.8" => { + let state = State::default(); + state.fee_account_type.save(deps.storage,&FeeType::Wallet)?; } _ => {} }, diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index 6f26246..7838a40 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -8,10 +8,15 @@ use cw20::{Cw20ExecuteMsg, MinterResponse}; use cw20_base::msg::InstantiateMsg as Cw20InstantiateMsg; use crate::contract::{REPLY_INSTANTIATE_TOKEN, REPLY_REGISTER_RECEIVED_COINS}; -use steak::hub::{Batch, CallbackMsg, ExecuteMsg, InstantiateMsg, PendingBatch, UnbondRequest}; +use steak::hub::{ + Batch, CallbackMsg, ExecuteMsg, FeeType, InstantiateMsg, PendingBatch, UnbondRequest, +}; use steak::DecimalCheckedOps; -use crate::helpers::{get_denom_balance, parse_received_fund, query_cw20_total_supply, query_delegation, query_delegations}; +use crate::helpers::{ + get_denom_balance, parse_received_fund, query_cw20_total_supply, query_delegation, + query_delegations, +}; use crate::math::{ compute_mint_amount, compute_redelegations_for_rebalancing, compute_redelegations_for_removal, compute_unbond_amount, compute_undelegations, reconcile_batches, @@ -33,6 +38,8 @@ pub fn instantiate(deps: DepsMut, env: Env, msg: InstantiateMsg) -> StdResult msg.max_fee_amount { return Err(StdError::generic_err("fee can not exceed max fee")); } + let fee_type = FeeType::from_str(&msg.fee_account_type) + .map_err(|_| StdError::generic_err("Invalid Fee type: Wallet or FeeSplit only"))?; state .owner @@ -45,6 +52,8 @@ pub fn instantiate(deps: DepsMut, env: Env, msg: InstantiateMsg) -> StdResult) -> StdRes // Query the current supply of Steak and compute the amount to mint let usteak_supply = query_cw20_total_supply(&deps.querier, &steak_token)?; let usteak_to_mint = compute_mint_amount(usteak_supply, amount_to_bond, &delegations); - state.prev_denom.save(deps.storage, &get_denom_balance(&deps.querier,env.contract.address, denom.clone())?)?; + state.prev_denom.save( + deps.storage, + &get_denom_balance(&deps.querier, env.contract.address, denom.clone())?, + )?; let delegate_submsg = SubMsg::reply_on_success( new_delegation.to_cosmos_msg(), @@ -179,7 +191,10 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec) -> StdRes pub fn harvest(deps: DepsMut, env: Env) -> StdResult { let state = State::default(); let denom = state.denom.load(deps.storage)?; - state.prev_denom.save(deps.storage, &get_denom_balance(&deps.querier,env.contract.address.clone(), denom)?)?; + state.prev_denom.save( + deps.storage, + &get_denom_balance(&deps.querier, env.contract.address.clone(), denom)?, + )?; let withdraw_submsgs = deps .querier @@ -216,7 +231,8 @@ pub fn reinvest(deps: DepsMut, env: Env) -> StdResult { let validators = state.validators_active.load(deps.storage)?; let prev_coin = state.prev_denom.load(deps.storage)?; - let current_coin = get_denom_balance(&deps.querier,env.contract.address.clone(), denom.clone())?; + let current_coin = + get_denom_balance(&deps.querier, env.contract.address.clone(), denom.clone())?; if current_coin <= prev_coin { return Err(StdError::generic_err("no rewards")); @@ -226,15 +242,15 @@ pub fn reinvest(deps: DepsMut, env: Env) -> StdResult { /* - if unlocked_coins.is_empty() { - return Err(StdError::generic_err("no rewards")); - } - let amount_to_bond = unlocked_coins - .iter() - .find(|coin| coin.denom == denom) - .ok_or_else(|| StdError::generic_err("no native amount available to be bonded"))? - .amount; -*/ + if unlocked_coins.is_empty() { + return Err(StdError::generic_err("no rewards")); + } + let amount_to_bond = unlocked_coins + .iter() + .find(|coin| coin.denom == denom) + .ok_or_else(|| StdError::generic_err("no native amount available to be bonded"))? + .amount; + */ let delegations = query_delegations(&deps.querier, &validators, &env.contract.address, &denom)?; let mut validator = &delegations[0].validator; let mut amount = delegations[0].amount; @@ -265,14 +281,23 @@ pub fn reinvest(deps: DepsMut, env: Env) -> StdResult { if fee_amount > Uint128::zero() { let fee_account = state.fee_account.load(deps.storage)?; - - let send_msg = BankMsg::Send { - to_address: fee_account.to_string(), - amount: vec![Coin::new(fee_amount.into(), &denom)], + let fee_type = state.fee_account_type.load(deps.storage)?; + + let send_msgs = match fee_type { + FeeType::Wallet => + vec![CosmosMsg::Bank(BankMsg::Send { + to_address: fee_account.to_string(), + amount: vec![Coin::new(fee_amount.into(), &denom)], + })], + FeeType::FeeSplit => { + let msg = pfc_fee_split::fee_split_msg::ExecuteMsg::Deposit{ flush:false}; + + vec![msg .into_cosmos_msg(fee_account)? ] + } }; Ok(Response::new() .add_message(new_delegation.to_cosmos_msg()) - .add_message(CosmosMsg::Bank(send_msg)) + .add_messages(send_msgs) .add_event(event) .add_attribute("action", "steakhub/reinvest")) } else { @@ -440,7 +465,10 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { est_unbond_start_time: current_time + epoch_period, }, )?; - state.prev_denom.save(deps.storage, &get_denom_balance(&deps.querier,env.contract.address, denom)?)?; + state.prev_denom.save( + deps.storage, + &get_denom_balance(&deps.querier, env.contract.address, denom)?, + )?; let undelegate_submsgs = new_undelegations .iter() @@ -641,7 +669,10 @@ pub fn rebalance(deps: DepsMut, env: Env, minimum: Uint128) -> StdResult StdResult { + let state = State::default(); + + state.assert_owner(deps.storage, &sender)?; + state.unbond_period.save(deps.storage, &unbond_period)?; + let event = Event::new("steak/set_unbond_period") + .add_attribute("unbond_period", format!("{}", unbond_period)); + + Ok(Response::new() + .add_event(event) + .add_attribute("action", "steakhub/set_unbond_period")) +} pub fn transfer_ownership(deps: DepsMut, sender: Addr, new_owner: String) -> StdResult { let state = State::default(); @@ -851,11 +903,17 @@ pub fn accept_ownership(deps: DepsMut, sender: Addr) -> StdResult { pub fn transfer_fee_account( deps: DepsMut, sender: Addr, + fee_account_type: String, new_fee_account: String, ) -> StdResult { let state = State::default(); state.assert_owner(deps.storage, &sender)?; + let fee_type = FeeType::from_str(&fee_account_type) + .map_err(|_| StdError::generic_err("Invalid Fee type: Wallet or FeeSplit only"))?; + + state.fee_account_type.save(deps.storage, &fee_type)?; + state .fee_account .save(deps.storage, &deps.api.addr_validate(&new_fee_account)?)?; diff --git a/contracts/hub/src/migrations.rs b/contracts/hub/src/migrations.rs index 8d19c95..065ab3d 100644 --- a/contracts/hub/src/migrations.rs +++ b/contracts/hub/src/migrations.rs @@ -10,7 +10,7 @@ use crate::helpers::get_denom_balance; const BATCH_KEY_V100: &str = "previous_batches"; const BATCH_KEY_RECONCILED_V100: &str = "previous_batches__reconciled"; -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] pub struct BatchV100 { /// ID of this batch pub id: u64, @@ -24,7 +24,7 @@ pub struct BatchV100 { pub est_unbond_end_time: u64, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] pub struct ConfigV100 {} impl ConfigV100 { diff --git a/contracts/hub/src/queries.rs b/contracts/hub/src/queries.rs index 75fd136..e551bd6 100644 --- a/contracts/hub/src/queries.rs +++ b/contracts/hub/src/queries.rs @@ -24,6 +24,7 @@ pub fn config(deps: Deps) -> StdResult { epoch_period: state.epoch_period.load(deps.storage)?, unbond_period: state.unbond_period.load(deps.storage)?, denom: state.denom.load(deps.storage)?, + fee_type: state.fee_account_type.load(deps.storage)?.to_string(), fee_account: state.fee_account.load(deps.storage)?.to_string(), fee_rate: state.fee_rate.load(deps.storage)?, max_fee_rate: state.max_fee_rate.load(deps.storage)?, diff --git a/contracts/hub/src/state.rs b/contracts/hub/src/state.rs index 2605522..bc3bdd6 100644 --- a/contracts/hub/src/state.rs +++ b/contracts/hub/src/state.rs @@ -1,7 +1,7 @@ use cosmwasm_std::{Addr, Coin, Decimal, StdError, StdResult, Storage, Uint128}; use cw_storage_plus::{Index, IndexList, IndexedMap, Item, MultiIndex}; -use steak::hub::{Batch, PendingBatch, UnbondRequest}; +use steak::hub::{Batch, FeeType, PendingBatch, UnbondRequest}; use crate::types::BooleanKey; pub(crate) const BATCH_KEY_V101: &str = "previous_batches_101"; @@ -12,6 +12,7 @@ pub(crate) struct State<'a> { pub owner: Item<'a, Addr>, /// Pending ownership transfer, awaiting acceptance by the new owner pub new_owner: Item<'a, Addr>, + pub fee_account_type: Item<'a,FeeType>, /// Account to send fees to pub fee_account: Item<'a, Addr>, /// Current fee rate @@ -76,6 +77,7 @@ impl Default for State<'static> { unbond_requests: IndexedMap::new("unbond_requests", ubr_indexes), validators_active: Item::new("validators_active"), prev_denom: Item::new("prev_denom"), + fee_account_type: Item::new("fee_account_type") } } } diff --git a/contracts/hub/src/testing/tests.rs b/contracts/hub/src/testing/tests.rs index 52bee9c..db01fd6 100644 --- a/contracts/hub/src/testing/tests.rs +++ b/contracts/hub/src/testing/tests.rs @@ -42,6 +42,7 @@ fn setup_test() -> OwnedDeps { name: "Steak Token".to_string(), symbol: "STEAK".to_string(), denom: "uxyz".to_string(), + fee_account_type: "Wallet".to_string(), fee_account: "the_fee_man".to_string(), fee_amount: Decimal::from_ratio(10_u128, 100_u128), //10% max_fee_amount: Decimal::from_ratio(20_u128, 100_u128), //20% @@ -107,6 +108,85 @@ fn setup_test() -> OwnedDeps { deps } +fn setup_test_fee_split() -> OwnedDeps { + let mut deps = mock_dependencies(); + + let res = instantiate( + deps.as_mut(), + mock_env_at_timestamp(10000), + mock_info("deployer", &[]), + InstantiateMsg { + cw20_code_id: 69420, + owner: "larry".to_string(), + name: "Steak Token".to_string(), + symbol: "STEAK".to_string(), + denom: "uxyz".to_string(), + fee_account_type: "FeeSplit".to_string(), + fee_account: "fee_split_contract".to_string(), + fee_amount: Decimal::from_ratio(10_u128, 100_u128), //10% + max_fee_amount: Decimal::from_ratio(20_u128, 100_u128), //20% + decimals: 6, + epoch_period: 259200, // 3 * 24 * 60 * 60 = 3 days + unbond_period: 1814400, // 21 * 24 * 60 * 60 = 21 days + validators: vec![ + "alice".to_string(), + "bob".to_string(), + "charlie".to_string(), + ], + + }, + ) + .unwrap(); + + assert_eq!(res.messages.len(), 1); + assert_eq!( + res.messages[0], + SubMsg::reply_on_success( + CosmosMsg::Wasm(WasmMsg::Instantiate { + admin: Some("larry".to_string()), + code_id: 69420, + msg: to_binary(&Cw20InstantiateMsg { + name: "Steak Token".to_string(), + symbol: "STEAK".to_string(), + decimals: 6, + initial_balances: vec![], + mint: Some(MinterResponse { + minter: MOCK_CONTRACT_ADDR.to_string(), + cap: None + }), + marketing: None, + }) + .unwrap(), + funds: vec![], + label: "steak_token".to_string(), + }), + REPLY_INSTANTIATE_TOKEN + ) + ); + + let event = Event::new("instantiate") + .add_attribute("code_id", "69420") + .add_attribute("_contract_address", "steak_token"); + + let res = reply( + deps.as_mut(), + mock_env_at_timestamp(10000), + Reply { + id: REPLY_INSTANTIATE_TOKEN, + result: cosmwasm_std::SubMsgResult::Ok(SubMsgResponse { + events: vec![event], + data: None, + }), + }, + ) + .unwrap(); + + assert_eq!(res.messages.len(), 0); + + deps.querier.set_cw20_total_supply("steak_token", 0); + deps +} + //-------------------------------------------------------------------------------------------------- // Execution //-------------------------------------------------------------------------------------------------- @@ -125,6 +205,7 @@ fn proper_instantiation() { epoch_period: 259200, unbond_period: 1814400, denom: "uxyz".to_string(), + fee_type:"Wallet".to_string(), fee_account: "the_fee_man".to_string(), fee_rate: Decimal::from_ratio(10_u128, 100_u128), max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), @@ -156,6 +237,29 @@ fn proper_instantiation() { est_unbond_start_time: 269200, // 10,000 + 259,200 }, ); + let deps_fee_split = setup_test_fee_split(); + + let res_fee_split: ConfigResponse = query_helper(deps_fee_split.as_ref(), QueryMsg::Config {}); + assert_eq!( + res_fee_split, + ConfigResponse { + owner: "larry".to_string(), + new_owner: None, + steak_token: "steak_token".to_string(), + epoch_period: 259200, + unbond_period: 1814400, + denom: "uxyz".to_string(), + fee_type:"FeeSplit".to_string(), + fee_account: "fee_split_contract".to_string(), + fee_rate: Decimal::from_ratio(10_u128, 100_u128), + max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), + validators: vec![ + "alice".to_string(), + "bob".to_string(), + "charlie".to_string() + ] + } + ); } #[test] @@ -430,6 +534,78 @@ fn reinvesting() { "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B" )], ); + +} + +#[test] +fn reinvesting_fee_split() { + let mut deps = setup_test_fee_split(); + let state = State::default(); + + deps.querier.set_staking_delegations(&[ + Delegation::new("alice", 333334, "uxyz"), + Delegation::new("bob", 333333, "uxyz"), + Delegation::new("charlie", 333333, "uxyz"), + ]); + state.prev_denom.save(deps.as_mut().storage,&Uint128::from(0 as u32)).unwrap(); + deps.querier.set_bank_balances(&[Coin::new(234u128,"uxyz")]); + + // After the swaps, `unlocked_coins` should contain only uxyz and unknown denoms + state + .unlocked_coins + .save( + deps.as_mut().storage, + &vec![ + Coin::new(234, "uxyz"), + Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + ), + ], + ) + .unwrap(); + + // Bob has the smallest amount of delegations, so all proceeds go to him + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(MOCK_CONTRACT_ADDR, &[]), + ExecuteMsg::Callback(CallbackMsg::Reinvest {}), + ) + .unwrap(); + + assert_eq!(res.messages.len(), 2); + assert_eq!( + res.messages[0], + SubMsg { + id: 0, + msg: Delegation::new("bob", 234 - 23, "uxyz").to_cosmos_msg(), + gas_limit: None, + reply_on: ReplyOn::Never + } + ); + let send_msg = pfc_fee_split::fee_split_msg::ExecuteMsg::Deposit{ flush:false}; + + assert_eq!( + res.messages[1], + SubMsg { + id: 0, + msg: send_msg.into_cosmos_msg("fee_split_contract").unwrap(), + gas_limit: None, + reply_on: ReplyOn::Never + } + ); + + // Storage should have been updated + let unlocked_coins = state.unlocked_coins.load(deps.as_ref().storage).unwrap(); + assert_eq!( + unlocked_coins, + vec![Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B" + )], + ); + } #[test] @@ -1271,7 +1447,108 @@ fn transferring_ownership() { let owner = state.owner.load(deps.as_ref().storage).unwrap(); assert_eq!(owner, Addr::unchecked("jake")); } +#[test] +fn splitting_fees() { + let mut deps = setup_test(); + + + let err = execute( + deps.as_mut(), + mock_env(), + mock_info("jake", &[]), + ExecuteMsg::TransferFeeAccount { + fee_account_type: "Wallet".to_string(), + new_fee_account: "charlie".to_string() + }, + ) + .unwrap_err(); + + assert_eq!( + err, + StdError::generic_err("unauthorized: sender is not owner") + ); + + let err = execute( + deps.as_mut(), + mock_env(), + mock_info("larry", &[]), + ExecuteMsg::TransferFeeAccount { + fee_account_type: "xxxx".to_string(), + new_fee_account: "charlie".to_string() + }, + ) + .unwrap_err(); + + assert_eq!( + err, + StdError::generic_err("Invalid Fee type: Wallet or FeeSplit only") + ); + execute( + deps.as_mut(), + mock_env(), + mock_info("larry", &[]), + ExecuteMsg::TransferFeeAccount { + fee_account_type: "Wallet".to_string(), + new_fee_account: "charlie".to_string() + }, + ) + .unwrap(); + let res: ConfigResponse = query_helper(deps.as_ref(), QueryMsg::Config {}); + assert_eq!( + res, + ConfigResponse { + owner: "larry".to_string(), + new_owner: None, + steak_token: "steak_token".to_string(), + epoch_period: 259200, + unbond_period: 1814400, + denom: "uxyz".to_string(), + fee_type:"Wallet".to_string(), + fee_account: "charlie".to_string(), + fee_rate: Decimal::from_ratio(10_u128, 100_u128), + max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), + validators: vec![ + "alice".to_string(), + "bob".to_string(), + "charlie".to_string() + ] + } + ); + + + execute( + deps.as_mut(), + mock_env(), + mock_info("larry", &[]), + ExecuteMsg::TransferFeeAccount { + fee_account_type: "FeeSplit".to_string(), + new_fee_account: "contract".to_string() + }, + ) + .unwrap(); + let res: ConfigResponse = query_helper(deps.as_ref(), QueryMsg::Config {}); + assert_eq!( + res, + ConfigResponse { + owner: "larry".to_string(), + new_owner: None, + steak_token: "steak_token".to_string(), + epoch_period: 259200, + unbond_period: 1814400, + denom: "uxyz".to_string(), + fee_type:"FeeSplit".to_string(), + fee_account: "contract".to_string(), + fee_rate: Decimal::from_ratio(10_u128, 100_u128), + max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), + validators: vec![ + "alice".to_string(), + "bob".to_string(), + "charlie".to_string() + ] + } + ); +} //-------------------------------------------------------------------------------------------------- // Queries //-------------------------------------------------------------------------------------------------- diff --git a/packages/steak/Cargo.toml b/packages/steak/Cargo.toml index 9aa500c..6b95e99 100644 --- a/packages/steak/Cargo.toml +++ b/packages/steak/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "steak" -version = "2.1.5" +version = "2.1.6" authors = ["larry ", "PFC "] edition = "2018" description = "Liquid steaking protocol for the cosmos" diff --git a/packages/steak/src/hub.rs b/packages/steak/src/hub.rs index 1c300bf..4fb19e1 100644 --- a/packages/steak/src/hub.rs +++ b/packages/steak/src/hub.rs @@ -2,8 +2,9 @@ use cosmwasm_std::{to_binary, Addr, Coin, CosmosMsg, Decimal, Empty, StdResult, use cw20::Cw20ReceiveMsg; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use std::str::FromStr; -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug,Eq, PartialEq, JsonSchema)] pub struct InstantiateMsg { /// Code ID of the CW20 token contract pub cw20_code_id: u64, @@ -23,6 +24,8 @@ pub struct InstantiateMsg { pub validators: Vec, /// denomination of coins to steak (uXXXX) pub denom: String, + /// type of fee account + pub fee_account_type: String, /// Fee Account to send fees too pub fee_account: String, /// Fee "1.00 = 100%" @@ -48,6 +51,7 @@ pub enum ExecuteMsg { RemoveValidator { validator: String }, /// Remove a validator from the whitelist; callable by the owner. Does not undelegate. use for typos RemoveValidatorEx { validator: String }, + /// Pause a validator from accepting new delegations PauseValidator { validator: String }, /// Unpause a validator from accepting new delegations @@ -65,16 +69,21 @@ pub enum ExecuteMsg { Reconcile {}, /// Submit the current pending batch of unbonding requests to be unbonded SubmitBatch {}, + /// Set unbond period + SetUnbondPeriod { unbond_period: u64 }, /// Transfer Fee collection account to another account - TransferFeeAccount { new_fee_account: String }, + TransferFeeAccount { + fee_account_type: String, + new_fee_account: String, + }, /// Update fee collection amount UpdateFee { new_fee: Decimal }, /// Callbacks; can only be invoked by the contract itself Callback(CallbackMsg), } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, Eq,PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum ReceiveMsg { /// Submit an unbonding request to the current unbonding queue; automatically invokes `unbond` @@ -82,7 +91,7 @@ pub enum ReceiveMsg { QueueUnbond { receiver: Option }, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug,Eq, PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum CallbackMsg { /// Following the swaps, stake the Luna acquired to the whitelisted validators @@ -99,7 +108,7 @@ impl CallbackMsg { } } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, Eq,PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum QueryMsg { /// The contract's configurations. Response: `ConfigResponse` @@ -131,7 +140,7 @@ pub enum QueryMsg { }, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, Eq,PartialEq, JsonSchema)] pub struct ConfigResponse { /// Account who can call certain privileged functions pub owner: String, @@ -145,6 +154,8 @@ pub struct ConfigResponse { pub unbond_period: u64, /// denomination of coins to steak (uXXXX) pub denom: String, + /// type of account to send the fees too + pub fee_type:String, /// Fee Account to send fees too pub fee_account: String, /// Fee "1.00 = 100%" @@ -155,7 +166,7 @@ pub struct ConfigResponse { pub validators: Vec, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug,Eq, PartialEq, JsonSchema)] pub struct StateResponse { /// Total supply to the Steak token pub total_usteak: Uint128, @@ -167,7 +178,7 @@ pub struct StateResponse { pub unlocked_coins: Vec, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, Eq,PartialEq, JsonSchema)] pub struct PendingBatch { /// ID of this batch pub id: u64, @@ -177,7 +188,7 @@ pub struct PendingBatch { pub est_unbond_start_time: u64, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, Eq,PartialEq, JsonSchema)] pub struct Batch { /// ID of this batch pub id: u64, @@ -191,7 +202,7 @@ pub struct Batch { pub est_unbond_end_time: u64, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, Eq,PartialEq, JsonSchema)] pub struct UnbondRequest { /// ID of the batch pub id: u64, @@ -201,7 +212,7 @@ pub struct UnbondRequest { pub shares: Uint128, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, Eq,PartialEq, JsonSchema)] pub struct UnbondRequestsByBatchResponseItem { /// The user's address pub user: String, @@ -218,7 +229,7 @@ impl From for UnbondRequestsByBatchResponseItem { } } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, JsonSchema)] pub struct UnbondRequestsByUserResponseItem { /// ID of the batch pub id: u64, @@ -236,3 +247,27 @@ impl From for UnbondRequestsByUserResponseItem { } pub type MigrateMsg = Empty; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Copy, JsonSchema)] +pub enum FeeType { + Wallet, + FeeSplit, +} +impl FromStr for FeeType { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "Wallet" => Ok(FeeType::Wallet), + "FeeSplit" => Ok(FeeType::FeeSplit), + _ => Err(()), + } + } +} +impl ToString for FeeType { + fn to_string(&self) -> String { + match &self { + FeeType::Wallet => String::from("Wallet"), + FeeType::FeeSplit => String::from("FeeSplit"), + } + } +} From 2d522290f00a83de7933b2f6a84e9c531c8465a2 Mon Sep 17 00:00:00 2001 From: "PFC(Ian)" Date: Thu, 20 Oct 2022 13:43:32 -0500 Subject: [PATCH 20/77] fix: 2.1.10. actually send fees to fee_split --- Cargo.lock | 4 ++-- contracts/hub/Cargo.toml | 4 ++-- contracts/hub/src/contract.rs | 7 +++++++ contracts/hub/src/execute.rs | 2 +- contracts/hub/src/testing/tests.rs | 2 +- 5 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ffdd478..affdfcd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -486,9 +486,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "pfc-fee-split" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320b2bc8ca3b95a29a2aa7c9805efaaae5faedb8bb5bff384f8a659416d45515" +checksum = "e202f9af6235723bfdecb0682a4c95143f8a9842de7cf831a2c27f85733ed019" dependencies = [ "cosmwasm-std", "cw20 0.15.1", diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index 99a3983..4c97554 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "steak-hub" -version = "2.1.9" +version = "2.1.10" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" @@ -20,6 +20,6 @@ cw20-base = { version = "0.13", features = ["library"] } cw-storage-plus = "0.13" steak = { path = "../../packages/steak" } serde = { version = "1.0.103", default-features = false, features = ["derive"] } -pfc-fee-split = "0.1.0" +pfc-fee-split = "0.1.1" #[dev-dependencies] #serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/contracts/hub/src/contract.rs b/contracts/hub/src/contract.rs index 34bf961..b7df106 100644 --- a/contracts/hub/src/contract.rs +++ b/contracts/hub/src/contract.rs @@ -240,6 +240,13 @@ pub fn migrate(deps: DepsMut, env: Env, _msg: MigrateMsg) -> StdResult )) } } + /* + let state = State::default(); + + state.max_fee_rate.save(deps.storage,&Decimal::from_ratio(10u32,100u32))?; + state.fee_rate.save(deps.storage,&Decimal::from_ratio(10u32,100u32))?; + + */ set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; Ok(Response::new() diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index 7838a40..852421d 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -292,7 +292,7 @@ pub fn reinvest(deps: DepsMut, env: Env) -> StdResult { FeeType::FeeSplit => { let msg = pfc_fee_split::fee_split_msg::ExecuteMsg::Deposit{ flush:false}; - vec![msg .into_cosmos_msg(fee_account)? ] + vec![msg .into_cosmos_msg(fee_account, vec![Coin::new(fee_amount.into(), &denom)])? ] } }; Ok(Response::new() diff --git a/contracts/hub/src/testing/tests.rs b/contracts/hub/src/testing/tests.rs index db01fd6..7d42887 100644 --- a/contracts/hub/src/testing/tests.rs +++ b/contracts/hub/src/testing/tests.rs @@ -590,7 +590,7 @@ fn reinvesting_fee_split() { res.messages[1], SubMsg { id: 0, - msg: send_msg.into_cosmos_msg("fee_split_contract").unwrap(), + msg: send_msg.into_cosmos_msg("fee_split_contract", vec![Coin::new(23u128,"uxyz")]).unwrap(), gas_limit: None, reply_on: ReplyOn::Never } From ec79289fa3d61967b2785816c0c62296894c1014 Mon Sep 17 00:00:00 2001 From: "PFC(Ian)" Date: Fri, 21 Oct 2022 10:48:24 -0500 Subject: [PATCH 21/77] feat: 2.1.11 - pass in marketing info and cw20 token label --- Cargo.lock | 5 +++-- contracts/hub/Cargo.toml | 2 +- contracts/hub/src/execute.rs | 4 ++-- contracts/hub/src/testing/tests.rs | 4 ++++ packages/steak/Cargo.toml | 3 ++- packages/steak/src/hub.rs | 5 +++++ 6 files changed, 17 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index affdfcd..90a2f0c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -706,17 +706,18 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "steak" -version = "2.1.6" +version = "2.1.7" dependencies = [ "cosmwasm-std", "cw20 0.13.4", + "cw20-base", "schemars", "serde", ] [[package]] name = "steak-hub" -version = "2.1.9" +version = "2.1.11" dependencies = [ "cosmwasm-std", "cw-storage-plus 0.13.4", diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index 4c97554..fc8c1c6 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "steak-hub" -version = "2.1.10" +version = "2.1.11" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index 852421d..22eb227 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -83,10 +83,10 @@ pub fn instantiate(deps: DepsMut, env: Env, msg: InstantiateMsg) -> StdResult OwnedDeps { "bob".to_string(), "charlie".to_string(), ], + label: None, + marketing:None }, ) @@ -133,6 +135,8 @@ fn setup_test_fee_split() -> OwnedDeps { "bob".to_string(), "charlie".to_string(), ], + label: None, + marketing:None }, ) diff --git a/packages/steak/Cargo.toml b/packages/steak/Cargo.toml index 6b95e99..a831514 100644 --- a/packages/steak/Cargo.toml +++ b/packages/steak/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "steak" -version = "2.1.6" +version = "2.1.7" authors = ["larry ", "PFC "] edition = "2018" description = "Liquid steaking protocol for the cosmos" @@ -11,6 +11,7 @@ repository = "https://github.com/PFC-developer/steak-contracts" [dependencies] cosmwasm-std = "1.0" cw20 = "0.13" +cw20-base = { version = "0.13", features = ["library"] } schemars = "0.8.1" serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/packages/steak/src/hub.rs b/packages/steak/src/hub.rs index 4fb19e1..bcd048d 100644 --- a/packages/steak/src/hub.rs +++ b/packages/steak/src/hub.rs @@ -1,5 +1,6 @@ use cosmwasm_std::{to_binary, Addr, Coin, CosmosMsg, Decimal, Empty, StdResult, Uint128, WasmMsg}; use cw20::Cw20ReceiveMsg; +use cw20_base::msg::InstantiateMarketingInfo as Cw20InstantiateMarketingInfo; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::str::FromStr; @@ -32,6 +33,10 @@ pub struct InstantiateMsg { pub fee_amount: Decimal, /// Max Fee "1.00 = 100%" pub max_fee_amount: Decimal, + /// label for the CW20 token we create + pub label: Option, + /// Marketing info for the CW20 we create + pub marketing: Option } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] From 426f7e8114c3e2a14d22ac1ae4eec0c44bf203f1 Mon Sep 17 00:00:00 2001 From: "PFC(Ian)" Date: Mon, 21 Nov 2022 12:08:33 -0600 Subject: [PATCH 22/77] feat: 2.1.12 - rename cargo package to pfc-steak... to avoid collisions on lib.rs fix: clippy --- Cargo.lock | 68 ++++++++++++++-------------- Cargo.toml | 2 +- contracts/hub/Cargo.toml | 6 +-- contracts/hub/src/contract.rs | 2 +- contracts/hub/src/execute.rs | 6 +-- contracts/hub/src/math.rs | 10 ++-- contracts/hub/src/migrations.rs | 2 +- contracts/hub/src/queries.rs | 2 +- contracts/hub/src/state.rs | 2 +- contracts/hub/src/testing/helpers.rs | 2 +- contracts/hub/src/testing/tests.rs | 12 ++--- contracts/token/Cargo.toml | 4 +- packages/steak/Cargo.toml | 4 +- packages/steak/src/hub.rs | 2 +- 14 files changed, 62 insertions(+), 62 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 90a2f0c..1b5098e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -496,6 +496,40 @@ dependencies = [ "serde", ] +[[package]] +name = "pfc-steak" +version = "2.1.8" +dependencies = [ + "cosmwasm-std", + "cw20 0.13.4", + "cw20-base", + "schemars", + "serde", +] + +[[package]] +name = "pfc-steak-hub" +version = "2.1.12" +dependencies = [ + "cosmwasm-std", + "cw-storage-plus 0.13.4", + "cw2 0.13.4", + "cw20 0.13.4", + "cw20-base", + "pfc-fee-split", + "pfc-steak", + "serde", +] + +[[package]] +name = "pfc-steak-token" +version = "2.0.1" +dependencies = [ + "cosmwasm-std", + "cw20 0.13.4", + "cw20-base", +] + [[package]] name = "pkcs8" version = "0.9.0" @@ -704,40 +738,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "steak" -version = "2.1.7" -dependencies = [ - "cosmwasm-std", - "cw20 0.13.4", - "cw20-base", - "schemars", - "serde", -] - -[[package]] -name = "steak-hub" -version = "2.1.11" -dependencies = [ - "cosmwasm-std", - "cw-storage-plus 0.13.4", - "cw2 0.13.4", - "cw20 0.13.4", - "cw20-base", - "pfc-fee-split", - "serde", - "steak", -] - -[[package]] -name = "steak-token" -version = "2.0.0" -dependencies = [ - "cosmwasm-std", - "cw20 0.13.4", - "cw20-base", -] - [[package]] name = "subtle" version = "2.4.1" diff --git a/Cargo.toml b/Cargo.toml index c2f449b..20494ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [workspace] members = ["contracts/*", "packages/*"] -[profile.release.package.steak] +[profile.release.package.pfc-steak] opt-level = 3 debug = false debug-assertions = false diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index fc8c1c6..184c3ba 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "steak-hub" -version = "2.1.11" +name = "pfc-steak-hub" +version = "2.1.12" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" @@ -18,7 +18,7 @@ cw2="0.13" cw20 = "0.13" cw20-base = { version = "0.13", features = ["library"] } cw-storage-plus = "0.13" -steak = { path = "../../packages/steak" } +pfc-steak = { path = "../../packages/steak" } serde = { version = "1.0.103", default-features = false, features = ["derive"] } pfc-fee-split = "0.1.1" #[dev-dependencies] diff --git a/contracts/hub/src/contract.rs b/contracts/hub/src/contract.rs index b7df106..cf405de 100644 --- a/contracts/hub/src/contract.rs +++ b/contracts/hub/src/contract.rs @@ -4,7 +4,7 @@ use cosmwasm_std::{ }; use cw20::Cw20ReceiveMsg; -use steak::hub::{CallbackMsg, ExecuteMsg, FeeType, InstantiateMsg, MigrateMsg, QueryMsg, ReceiveMsg}; +use pfc_steak::hub::{CallbackMsg, ExecuteMsg, FeeType, InstantiateMsg, MigrateMsg, QueryMsg, ReceiveMsg}; use crate::helpers::{get_denom_balance, unwrap_reply}; use crate::migrations::ConfigV100; diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index 22eb227..506a694 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -8,10 +8,10 @@ use cw20::{Cw20ExecuteMsg, MinterResponse}; use cw20_base::msg::InstantiateMsg as Cw20InstantiateMsg; use crate::contract::{REPLY_INSTANTIATE_TOKEN, REPLY_REGISTER_RECEIVED_COINS}; -use steak::hub::{ +use pfc_steak::hub::{ Batch, CallbackMsg, ExecuteMsg, FeeType, InstantiateMsg, PendingBatch, UnbondRequest, }; -use steak::DecimalCheckedOps; +use pfc_steak::DecimalCheckedOps; use crate::helpers::{ get_denom_balance, parse_received_fund, query_cw20_total_supply, query_delegation, @@ -86,7 +86,7 @@ pub fn instantiate(deps: DepsMut, env: Env, msg: InstantiateMsg) -> StdResult = vec![]; let mut native_available = native_to_unbond.u128(); for (i, d) in current_delegations.iter().enumerate() { - let remainder_for_validator: u128 = if (i + 1) as u128 <= remainder { 1 } else { 0 }; + let remainder_for_validator: u128 = u128::from( (i+1) as u128 <=remainder) as u128 ; let native_for_validator = native_per_validator + remainder_for_validator; let mut native_to_undelegate = if d.amount < native_for_validator { @@ -109,7 +109,7 @@ pub(crate) fn compute_redelegations_for_removal( let mut new_redelegations: Vec = vec![]; let mut native_available = delegation_to_remove.amount; for (i, d) in current_delegations.iter().enumerate() { - let remainder_for_validator: u128 = if (i + 1) as u128 <= remainder { 1 } else { 0 }; + let remainder_for_validator: u128 = u128::from( (i+1) as u128 <=remainder) as u128 ; let native_for_validator = native_per_validator + remainder_for_validator; let mut native_to_redelegate = if d.amount > native_for_validator { @@ -160,7 +160,7 @@ pub(crate) fn compute_redelegations_for_rebalancing( let mut src_delegations: Vec = vec![]; let mut dst_delegations: Vec = vec![]; for (i, d) in current_delegations.iter().enumerate() { - let remainder_for_validator: u128 = if (i + 1) as u128 <= remainder { 1 } else { 0 }; + let remainder_for_validator: u128 = u128::from( (i+1) as u128 <=remainder) as u128 ; let native_for_validator = native_per_validator + remainder_for_validator; // eprintln!("{} amount ={} native={} min={}", d.validator, d.amount, native_for_validator, min_difference); match d.amount.cmp(&native_for_validator) { @@ -235,7 +235,7 @@ pub(crate) fn reconcile_batches(batches: &mut [Batch], native_to_deduct: Uint128 let remainder = native_to_deduct.u128() % batch_count; for (i, batch) in batches.iter_mut().enumerate() { - let remainder_for_batch: u128 = if (i + 1) as u128 <= remainder { 1 } else { 0 }; + let remainder_for_batch: u128 = u128::from( (i+1) as u128 <=remainder) as u128 ; let native_for_batch = native_per_batch + remainder_for_batch; batch.amount_unclaimed -= Uint128::new(native_for_batch); diff --git a/contracts/hub/src/migrations.rs b/contracts/hub/src/migrations.rs index 065ab3d..c327a5d 100644 --- a/contracts/hub/src/migrations.rs +++ b/contracts/hub/src/migrations.rs @@ -2,7 +2,7 @@ use crate::state::{State, BATCH_KEY_V101}; use crate::types::BooleanKey; use cosmwasm_std::{Addr, Order, QuerierWrapper, StdError, StdResult, Storage, Uint128}; use cw_storage_plus::{Index, IndexList, IndexedMap, MultiIndex}; -use steak::hub::Batch; +use pfc_steak::hub::Batch; use serde::{Deserialize, Serialize}; use crate::helpers::get_denom_balance; diff --git a/contracts/hub/src/queries.rs b/contracts/hub/src/queries.rs index e551bd6..61944cf 100644 --- a/contracts/hub/src/queries.rs +++ b/contracts/hub/src/queries.rs @@ -1,7 +1,7 @@ use cosmwasm_std::{Addr, Decimal, Deps, Env, Order, StdResult, Uint128}; use cw_storage_plus::{Bound, CwIntKey}; -use steak::hub::{ +use pfc_steak::hub::{ Batch, ConfigResponse, PendingBatch, StateResponse, UnbondRequestsByBatchResponseItem, UnbondRequestsByUserResponseItem, }; diff --git a/contracts/hub/src/state.rs b/contracts/hub/src/state.rs index bc3bdd6..d53532b 100644 --- a/contracts/hub/src/state.rs +++ b/contracts/hub/src/state.rs @@ -1,7 +1,7 @@ use cosmwasm_std::{Addr, Coin, Decimal, StdError, StdResult, Storage, Uint128}; use cw_storage_plus::{Index, IndexList, IndexedMap, Item, MultiIndex}; -use steak::hub::{Batch, FeeType, PendingBatch, UnbondRequest}; +use pfc_steak::hub::{Batch, FeeType, PendingBatch, UnbondRequest}; use crate::types::BooleanKey; pub(crate) const BATCH_KEY_V101: &str = "previous_batches_101"; diff --git a/contracts/hub/src/testing/helpers.rs b/contracts/hub/src/testing/helpers.rs index 3e15439..172070f 100644 --- a/contracts/hub/src/testing/helpers.rs +++ b/contracts/hub/src/testing/helpers.rs @@ -5,7 +5,7 @@ use cosmwasm_std::{ }; use serde::de::DeserializeOwned; -use steak::hub::QueryMsg; +use pfc_steak::hub::QueryMsg; use crate::contract::query; diff --git a/contracts/hub/src/testing/tests.rs b/contracts/hub/src/testing/tests.rs index 0a02286..785223f 100644 --- a/contracts/hub/src/testing/tests.rs +++ b/contracts/hub/src/testing/tests.rs @@ -8,7 +8,7 @@ use cosmwasm_std::{ use cw20::{Cw20ExecuteMsg, MinterResponse}; use cw20_base::msg::InstantiateMsg as Cw20InstantiateMsg; -use steak::hub::{ +use pfc_steak::hub::{ Batch, CallbackMsg, ConfigResponse, ExecuteMsg, InstantiateMsg, PendingBatch, QueryMsg, ReceiveMsg, StateResponse, UnbondRequest, UnbondRequestsByBatchResponseItem, UnbondRequestsByUserResponseItem, @@ -1170,7 +1170,7 @@ fn withdrawing_unbonded() { assert_eq!( err, StdError::NotFound { - kind: "steak::hub::Batch".to_string() + kind: "pfc_steak::hub::Batch".to_string() } ); @@ -1193,13 +1193,13 @@ fn withdrawing_unbonded() { assert_eq!( err1, StdError::NotFound { - kind: "steak::hub::UnbondRequest".to_string() + kind: "pfc_steak::hub::UnbondRequest".to_string() } ); assert_eq!( err2, StdError::NotFound { - kind: "steak::hub::UnbondRequest".to_string() + kind: "pfc_steak::hub::UnbondRequest".to_string() } ); @@ -1236,7 +1236,7 @@ fn withdrawing_unbonded() { assert_eq!( err, StdError::NotFound { - kind: "steak::hub::Batch".to_string() + kind: "pfc_steak::hub::Batch".to_string() } ); @@ -1251,7 +1251,7 @@ fn withdrawing_unbonded() { assert_eq!( err, StdError::NotFound { - kind: "steak::hub::UnbondRequest".to_string() + kind: "pfc_steak::hub::UnbondRequest".to_string() } ); } diff --git a/contracts/token/Cargo.toml b/contracts/token/Cargo.toml index 571e642..5fcd874 100644 --- a/contracts/token/Cargo.toml +++ b/contracts/token/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "steak-token" -version = "2.0.0" +name = "pfc-steak-token" +version = "2.0.1" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/packages/steak/Cargo.toml b/packages/steak/Cargo.toml index a831514..2627287 100644 --- a/packages/steak/Cargo.toml +++ b/packages/steak/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "steak" -version = "2.1.7" +name = "pfc-steak" +version = "2.1.8" authors = ["larry ", "PFC "] edition = "2018" description = "Liquid steaking protocol for the cosmos" diff --git a/packages/steak/src/hub.rs b/packages/steak/src/hub.rs index bcd048d..1649cd5 100644 --- a/packages/steak/src/hub.rs +++ b/packages/steak/src/hub.rs @@ -5,7 +5,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::str::FromStr; -#[derive(Serialize, Deserialize, Clone, Debug,Eq, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct InstantiateMsg { /// Code ID of the CW20 token contract pub cw20_code_id: u64, From 6227b50bfd4d0edc85eb8f2a3b0cca0c189c718c Mon Sep 17 00:00:00 2001 From: The PFC <81114960+PFC-developer@users.noreply.github.com> Date: Wed, 7 Dec 2022 07:59:50 -0600 Subject: [PATCH 23/77] fix: 2.1.14 - mint doesn't send a message on minting. so switch to minting it yourself and then sending chore: upgrade package versions. cw-storage-plus left alone for now --- Cargo.lock | 115 +++++++++++++++++++++++++---- contracts/hub/Cargo.toml | 10 +-- contracts/hub/src/execute.rs | 17 ++++- contracts/hub/src/testing/tests.rs | 48 ++++++++++-- packages/steak/Cargo.toml | 6 +- 5 files changed, 162 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1b5098e..f8f97c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -58,9 +58,9 @@ checksum = "722e23542a15cea1f65d4a1419c4cfd7a26706c70871a13a04238ca3f40f1661" [[package]] name = "cosmwasm-crypto" -version = "1.1.5" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28376836c7677e1ea6d6656a754582e88b91e544ce22fae42956d5fe5549a958" +checksum = "227315dc11f0bb22a273d0c43d3ba8ef52041c42cf959f09045388a89c57e661" dependencies = [ "digest 0.10.5", "ed25519-zebra", @@ -71,9 +71,9 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.1.5" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eb69f4f7a8a4bce68c8fbd3646238fede1e77056e4ea31c5b6bfc37b709eec3" +checksum = "6fca30d51f7e5fbfa6440d8b10d7df0231bdf77e97fd3fe5d0cb79cc4822e50c" dependencies = [ "syn", ] @@ -104,9 +104,9 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.1.5" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46bf9157d060abbc55152aeadcace799d03dc630575daa66604079a1206cb060" +checksum = "b13d5a84d15cf7be17dc249a21588cdb0f7ef308907c50ce2723316a7d79c3dc" dependencies = [ "base64", "cosmwasm-crypto", @@ -193,6 +193,17 @@ dependencies = [ "serde", ] +[[package]] +name = "cw-storage-plus" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b6f91c0b94481a3e9ef1ceb183c37d00764f8751e39b45fc09f4d9b970d469" +dependencies = [ + "cosmwasm-std", + "schemars", + "serde", +] + [[package]] name = "cw-utils" version = "0.13.4" @@ -220,6 +231,21 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cw-utils" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6a84c6c1c0acc3616398eba50783934bd6c964bad6974241eaee3460c8f5b26" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw2 0.16.0", + "schemars", + "semver", + "serde", + "thiserror", +] + [[package]] name = "cw2" version = "0.13.4" @@ -245,6 +271,32 @@ dependencies = [ "serde", ] +[[package]] +name = "cw2" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91398113b806f4d2a8d5f8d05684704a20ffd5968bf87e3473e1973710b884ad" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 0.16.0", + "schemars", + "serde", +] + +[[package]] +name = "cw2" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03bdf3747540b47bc1bdaf50ba3aa5e4276ab0c2ce73e8b367ebe260cc37ff9c" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 0.16.0", + "schemars", + "serde", +] + [[package]] name = "cw20" version = "0.13.4" @@ -270,6 +322,19 @@ dependencies = [ "serde", ] +[[package]] +name = "cw20" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a4d69a9980e2295077c08fd3f797149658f357167f5898e27c00b9465fcbde" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils 0.16.0", + "schemars", + "serde", +] + [[package]] name = "cw20-base" version = "0.13.4" @@ -286,6 +351,24 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cw20-base" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f79314264ffc46b7658ee30caccc1540f14b9119568264bc02817f79c6f989a9" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 0.16.0", + "cw-utils 0.16.0", + "cw2 1.0.0", + "cw20 1.0.0", + "schemars", + "semver", + "serde", + "thiserror", +] + [[package]] name = "der" version = "0.6.0" @@ -486,9 +569,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "pfc-fee-split" -version = "0.1.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e202f9af6235723bfdecb0682a4c95143f8a9842de7cf831a2c27f85733ed019" +checksum = "e5e71d09b61b7c6babdf97bcc98cfef4f60d5e8f662b28d8a5ab42fde1b5c6db" dependencies = [ "cosmwasm-std", "cw20 0.15.1", @@ -498,24 +581,24 @@ dependencies = [ [[package]] name = "pfc-steak" -version = "2.1.8" +version = "2.1.14" dependencies = [ "cosmwasm-std", - "cw20 0.13.4", - "cw20-base", + "cw20 1.0.0", + "cw20-base 1.0.0", "schemars", "serde", ] [[package]] name = "pfc-steak-hub" -version = "2.1.12" +version = "2.1.14" dependencies = [ "cosmwasm-std", "cw-storage-plus 0.13.4", - "cw2 0.13.4", - "cw20 0.13.4", - "cw20-base", + "cw2 1.0.0", + "cw20 1.0.0", + "cw20-base 1.0.0", "pfc-fee-split", "pfc-steak", "serde", @@ -527,7 +610,7 @@ version = "2.0.1" dependencies = [ "cosmwasm-std", "cw20 0.13.4", - "cw20-base", + "cw20-base 0.13.4", ] [[package]] diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index 184c3ba..3fa8813 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub" -version = "2.1.12" +version = "2.1.14" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" @@ -14,12 +14,12 @@ backtraces = ["cosmwasm-std/backtraces"] [dependencies] cosmwasm-std = { version = "1.0", features = ["staking"] } -cw2="0.13" -cw20 = "0.13" -cw20-base = { version = "0.13", features = ["library"] } +cw2= "1.0.0" +cw20 = "1.0.0" +cw20-base = { version = "1.0.0", features = ["library"] } cw-storage-plus = "0.13" pfc-steak = { path = "../../packages/steak" } serde = { version = "1.0.103", default-features = false, features = ["derive"] } -pfc-fee-split = "0.1.1" +pfc-fee-split = "0.2.3" #[dev-dependencies] #serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index 506a694..51deb04 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -156,7 +156,7 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec) -> StdRes let usteak_to_mint = compute_mint_amount(usteak_supply, amount_to_bond, &delegations); state.prev_denom.save( deps.storage, - &get_denom_balance(&deps.querier, env.contract.address, denom.clone())?, + &get_denom_balance(&deps.querier, env.contract.address.clone(), denom.clone())?, )?; let delegate_submsg = SubMsg::reply_on_success( @@ -164,11 +164,21 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec) -> StdRes REPLY_REGISTER_RECEIVED_COINS, ); + // mint doesn't notify.. so split a single mint into a mint&send let mint_msg: CosmosMsg = CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: steak_token.into(), + contract_addr: steak_token.to_string(), msg: to_binary(&Cw20ExecuteMsg::Mint { - recipient: receiver.to_string(), + recipient: env.contract.address.to_string(), + amount: usteak_to_mint, + })?, + funds: vec![], + }); + let send_msg: CosmosMsg = CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: steak_token.to_string(), + msg: to_binary(&Cw20ExecuteMsg::Send { + contract: receiver.to_string(), amount: usteak_to_mint, + msg: Default::default(), })?, funds: vec![], }); @@ -184,6 +194,7 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec) -> StdRes Ok(Response::new() .add_submessage(delegate_submsg) .add_message(mint_msg) + .add_message(send_msg) .add_event(event) .add_attribute("action", "steakhub/bond")) } diff --git a/contracts/hub/src/testing/tests.rs b/contracts/hub/src/testing/tests.rs index 785223f..95d4a44 100644 --- a/contracts/hub/src/testing/tests.rs +++ b/contracts/hub/src/testing/tests.rs @@ -275,15 +275,15 @@ fn bonding() { let res = execute( deps.as_mut(), mock_env(), - mock_info("user_1", &[Coin::new(1000000, "uxyz")]), + mock_info("user_1", &[Coin::new(1_000_000, "uxyz")]), ExecuteMsg::Bond { receiver: None }, ) .unwrap(); - assert_eq!(res.messages.len(), 2); + assert_eq!(res.messages.len(), 3); assert_eq!( res.messages[0], - SubMsg::reply_on_success(Delegation::new("alice", 1000000, "uxyz").to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS) + SubMsg::reply_on_success(Delegation::new("alice", 1_000_000, "uxyz").to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS) ); assert_eq!( res.messages[1], @@ -292,8 +292,25 @@ fn bonding() { msg: CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: "steak_token".to_string(), msg: to_binary(&Cw20ExecuteMsg::Mint { - recipient: "user_1".to_string(), - amount: Uint128::new(1000000) + recipient: mock_env().contract.address.to_string(), + amount: Uint128::new(1_000_000) + }) + .unwrap(), + funds: vec![] + }), + gas_limit: None, + reply_on: ReplyOn::Never, + } + ); assert_eq!( + res.messages[2], + SubMsg { + id: 0, + msg: CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: "steak_token".to_string(), + msg: to_binary(&Cw20ExecuteMsg::Send { + contract: "user_1".to_string(), + amount: Uint128::new(1000000), + msg: Default::default() }) .unwrap(), funds: vec![] @@ -323,7 +340,7 @@ fn bonding() { ) .unwrap(); - assert_eq!(res.messages.len(), 2); + assert_eq!(res.messages.len(), 3); assert_eq!( res.messages[0], SubMsg::reply_on_success(Delegation::new("charlie", 12345, "uxyz").to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS) @@ -335,7 +352,7 @@ fn bonding() { msg: CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: "steak_token".to_string(), msg: to_binary(&Cw20ExecuteMsg::Mint { - recipient: "user_3".to_string(), + recipient: mock_env().contract.address.to_string(), amount: Uint128::new(12043) }) .unwrap(), @@ -344,6 +361,23 @@ fn bonding() { gas_limit: None, reply_on: ReplyOn::Never } + ); assert_eq!( + res.messages[2], + SubMsg { + id: 0, + msg: CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: "steak_token".to_string(), + msg: to_binary(&Cw20ExecuteMsg::Send { + contract: "user_3".to_string(), + amount: Uint128::new(12043), + msg: Default::default(), + }) + .unwrap(), + funds: vec![] + }), + gas_limit: None, + reply_on: ReplyOn::Never + } ); // Check the state after bonding diff --git a/packages/steak/Cargo.toml b/packages/steak/Cargo.toml index 2627287..291df33 100644 --- a/packages/steak/Cargo.toml +++ b/packages/steak/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak" -version = "2.1.8" +version = "2.1.14" authors = ["larry ", "PFC "] edition = "2018" description = "Liquid steaking protocol for the cosmos" @@ -10,8 +10,8 @@ repository = "https://github.com/PFC-developer/steak-contracts" [dependencies] cosmwasm-std = "1.0" -cw20 = "0.13" -cw20-base = { version = "0.13", features = ["library"] } +cw20 = "1.0.0" +cw20-base = { version = "1.0.0", features = ["library"] } schemars = "0.8.1" serde = { version = "1.0.103", default-features = false, features = ["derive"] } From 6fd24374f2fa0f8e590dd24422ccd90510b1f91d Mon Sep 17 00:00:00 2001 From: The PFC <81114960+PFC-developer@users.noreply.github.com> Date: Wed, 7 Dec 2022 09:33:31 -0600 Subject: [PATCH 24/77] revert: 2.1.14 - mint doesn't send a message on minting. so switch to minting it yourself and then sending --- build_arm.sh | 5 +++++ build_x86.sh | 5 +++++ contracts/hub/src/execute.rs | 9 +++++---- contracts/hub/src/testing/tests.rs | 20 +++++++++++++------- 4 files changed, 28 insertions(+), 11 deletions(-) create mode 100755 build_arm.sh create mode 100755 build_x86.sh diff --git a/build_arm.sh b/build_arm.sh new file mode 100755 index 0000000..d9eafb4 --- /dev/null +++ b/build_arm.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +docker run --rm -v "$(pwd)":/code \ + --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ + --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ + cosmwasm/workspace-optimizer-arm64:0.12.10 diff --git a/build_x86.sh b/build_x86.sh new file mode 100755 index 0000000..e067523 --- /dev/null +++ b/build_x86.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +docker run --rm -v "$(pwd)":/code \ + --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ + --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ + cosmwasm/workspace-optimizer:0.12.10 diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index 51deb04..8ae5388 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -164,15 +164,16 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec) -> StdRes REPLY_REGISTER_RECEIVED_COINS, ); - // mint doesn't notify.. so split a single mint into a mint&send + let mint_msg: CosmosMsg = CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: steak_token.to_string(), msg: to_binary(&Cw20ExecuteMsg::Mint { - recipient: env.contract.address.to_string(), + recipient: receiver.to_string(), amount: usteak_to_mint, })?, funds: vec![], }); + /* let send_msg: CosmosMsg = CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: steak_token.to_string(), msg: to_binary(&Cw20ExecuteMsg::Send { @@ -182,7 +183,7 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec) -> StdRes })?, funds: vec![], }); - +*/ let event = Event::new("steakhub/bonded") .add_attribute("time", env.block.time.seconds().to_string()) .add_attribute("height", env.block.height.to_string()) @@ -194,7 +195,7 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec) -> StdRes Ok(Response::new() .add_submessage(delegate_submsg) .add_message(mint_msg) - .add_message(send_msg) + // .add_message(send_msg) .add_event(event) .add_attribute("action", "steakhub/bond")) } diff --git a/contracts/hub/src/testing/tests.rs b/contracts/hub/src/testing/tests.rs index 95d4a44..8b8931c 100644 --- a/contracts/hub/src/testing/tests.rs +++ b/contracts/hub/src/testing/tests.rs @@ -280,7 +280,7 @@ fn bonding() { ) .unwrap(); - assert_eq!(res.messages.len(), 3); + assert_eq!(res.messages.len(), 2); assert_eq!( res.messages[0], SubMsg::reply_on_success(Delegation::new("alice", 1_000_000, "uxyz").to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS) @@ -292,7 +292,7 @@ fn bonding() { msg: CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: "steak_token".to_string(), msg: to_binary(&Cw20ExecuteMsg::Mint { - recipient: mock_env().contract.address.to_string(), + recipient: "user_1".to_string(), amount: Uint128::new(1_000_000) }) .unwrap(), @@ -301,7 +301,9 @@ fn bonding() { gas_limit: None, reply_on: ReplyOn::Never, } - ); assert_eq!( + ); + /* + assert_eq!( res.messages[2], SubMsg { id: 0, @@ -320,6 +322,8 @@ fn bonding() { } ); + */ + // Bond when there are existing delegations, and Luna:Steak exchange rate is >1 // Previously user 1 delegated 1,000,000 uluna. We assume we have accumulated 2.5% yield at 1025000 staked deps.querier.set_staking_delegations(&[ @@ -340,7 +344,7 @@ fn bonding() { ) .unwrap(); - assert_eq!(res.messages.len(), 3); + assert_eq!(res.messages.len(), 2); assert_eq!( res.messages[0], SubMsg::reply_on_success(Delegation::new("charlie", 12345, "uxyz").to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS) @@ -352,7 +356,7 @@ fn bonding() { msg: CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: "steak_token".to_string(), msg: to_binary(&Cw20ExecuteMsg::Mint { - recipient: mock_env().contract.address.to_string(), + recipient: "user_3".to_string(), amount: Uint128::new(12043) }) .unwrap(), @@ -361,7 +365,9 @@ fn bonding() { gas_limit: None, reply_on: ReplyOn::Never } - ); assert_eq!( + ); + /* + assert_eq!( res.messages[2], SubMsg { id: 0, @@ -379,7 +385,7 @@ fn bonding() { reply_on: ReplyOn::Never } ); - +*/ // Check the state after bonding deps.querier.set_staking_delegations(&[ Delegation::new("alice", 341667, "uxyz"), From e899f80946b8ae9c95afe30def12cbf45bd50cc8 Mon Sep 17 00:00:00 2001 From: The PFC <81114960+PFC-developer@users.noreply.github.com> Date: Wed, 7 Dec 2022 11:04:38 -0600 Subject: [PATCH 25/77] fix: 2.1.14 - mint will now send (or transfer) tokens --- contracts/hub/src/execute.rs | 69 +++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 25 deletions(-) diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index 8ae5388..df81a3a 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -1,18 +1,18 @@ use std::str::FromStr; use cosmwasm_std::{ - to_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, DepsMut, DistributionMsg, Env, Event, - Order, Response, StdError, StdResult, SubMsg, SubMsgResponse, Uint128, WasmMsg, + Addr, BankMsg, Coin, CosmosMsg, Decimal, DepsMut, DistributionMsg, Env, Event, Order, + Response, StdError, StdResult, SubMsg, SubMsgResponse, to_binary, Uint128, WasmMsg, }; use cw20::{Cw20ExecuteMsg, MinterResponse}; use cw20_base::msg::InstantiateMsg as Cw20InstantiateMsg; -use crate::contract::{REPLY_INSTANTIATE_TOKEN, REPLY_REGISTER_RECEIVED_COINS}; +use pfc_steak::DecimalCheckedOps; use pfc_steak::hub::{ Batch, CallbackMsg, ExecuteMsg, FeeType, InstantiateMsg, PendingBatch, UnbondRequest, }; -use pfc_steak::DecimalCheckedOps; +use crate::contract::{REPLY_INSTANTIATE_TOKEN, REPLY_REGISTER_RECEIVED_COINS}; use crate::helpers::{ get_denom_balance, parse_received_fund, query_cw20_total_supply, query_delegation, query_delegations, @@ -86,7 +86,7 @@ pub fn instantiate(deps: DepsMut, env: Env, msg: InstantiateMsg) -> StdResult) -> StdRes let mint_msg: CosmosMsg = CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: steak_token.to_string(), msg: to_binary(&Cw20ExecuteMsg::Mint { - recipient: receiver.to_string(), - amount: usteak_to_mint, - })?, - funds: vec![], - }); - /* - let send_msg: CosmosMsg = CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: steak_token.to_string(), - msg: to_binary(&Cw20ExecuteMsg::Send { - contract: receiver.to_string(), + recipient: env.contract.address.to_string(),//receiver.to_string(), amount: usteak_to_mint, - msg: Default::default(), })?, funds: vec![], }); -*/ + + let contract_info = deps.querier.query_wasm_contract_info(receiver.to_string()); + + let send_transfer_msg: CosmosMsg = match contract_info { + Ok(_) => { + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: steak_token.to_string(), + msg: to_binary(&Cw20ExecuteMsg::Send { + contract: receiver.to_string(), + amount: usteak_to_mint, + msg: Default::default(), + })?, + funds: vec![], + }) + } + Err(_) => { + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: steak_token.to_string(), + msg: to_binary(&Cw20ExecuteMsg::Transfer { + recipient: receiver.to_string(), + amount: usteak_to_mint, + // msg: Default::default(), + })?, + funds: vec![], + }) + } + }; + let event = Event::new("steakhub/bonded") .add_attribute("time", env.block.time.seconds().to_string()) .add_attribute("height", env.block.height.to_string()) - .add_attribute("receiver", receiver) + .add_attribute("steak_receiver", receiver) .add_attribute("denom_bonded", denom) .add_attribute("denom_amount", amount_to_bond) .add_attribute("usteak_minted", usteak_to_mint); Ok(Response::new() .add_submessage(delegate_submsg) - .add_message(mint_msg) - // .add_message(send_msg) + .add_messages(vec![mint_msg, send_transfer_msg]) + // .add_message(send_msg) .add_event(event) .add_attribute("action", "steakhub/bond")) } @@ -298,13 +315,13 @@ pub fn reinvest(deps: DepsMut, env: Env) -> StdResult { let send_msgs = match fee_type { FeeType::Wallet => vec![CosmosMsg::Bank(BankMsg::Send { - to_address: fee_account.to_string(), - amount: vec![Coin::new(fee_amount.into(), &denom)], - })], + to_address: fee_account.to_string(), + amount: vec![Coin::new(fee_amount.into(), &denom)], + })], FeeType::FeeSplit => { - let msg = pfc_fee_split::fee_split_msg::ExecuteMsg::Deposit{ flush:false}; + let msg = pfc_fee_split::fee_split_msg::ExecuteMsg::Deposit { flush: false }; - vec![msg .into_cosmos_msg(fee_account, vec![Coin::new(fee_amount.into(), &denom)])? ] + vec![msg.into_cosmos_msg(fee_account, vec![Coin::new(fee_amount.into(), &denom)])?] } }; Ok(Response::new() @@ -569,6 +586,7 @@ pub fn reconcile(deps: DepsMut, env: Env) -> StdResult { .add_event(event) .add_attribute("action", "steakhub/reconcile")) } + pub fn withdraw_unbonded_admin( deps: DepsMut, env: Env, @@ -859,6 +877,7 @@ pub fn unpause_validator( .add_event(event) .add_attribute("action", "steakhub/unpause_validator")) } + pub fn set_unbond_period( deps: DepsMut, _env: Env, From ddb9744e99373019c05d0afd8f9e90ea5015923b Mon Sep 17 00:00:00 2001 From: The PFC <81114960+PFC-developer@users.noreply.github.com> Date: Sat, 14 Jan 2023 15:18:07 -0600 Subject: [PATCH 26/77] fix: 2.1.15 - delegations should display ALL validators, not just whitelisted (thx @pupm0s) fix: test case was failing due 2.1.14 change of us minting to ourselves, and then sending/transferring. chore: upgrade to optimizer 0.12.11 chore: fix up new clippy warnings --- Cargo.lock | 2 +- build_x86.sh | 2 +- contracts/hub/Cargo.toml | 2 +- contracts/hub/src/execute.rs | 2 +- contracts/hub/src/math.rs | 69 ++++++++++++++---------------- contracts/hub/src/queries.rs | 11 ++++- contracts/hub/src/testing/tests.rs | 30 +++++++------ 7 files changed, 62 insertions(+), 56 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f8f97c7..98e8ce6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -592,7 +592,7 @@ dependencies = [ [[package]] name = "pfc-steak-hub" -version = "2.1.14" +version = "2.1.15" dependencies = [ "cosmwasm-std", "cw-storage-plus 0.13.4", diff --git a/build_x86.sh b/build_x86.sh index e067523..bc7e848 100755 --- a/build_x86.sh +++ b/build_x86.sh @@ -2,4 +2,4 @@ docker run --rm -v "$(pwd)":/code \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ - cosmwasm/workspace-optimizer:0.12.10 + cosmwasm/workspace-optimizer:0.12.11 diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index 3fa8813..c583389 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub" -version = "2.1.14" +version = "2.1.15" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index df81a3a..7216544 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -667,7 +667,7 @@ pub fn withdraw_unbonded( let refund_msg = CosmosMsg::Bank(BankMsg::Send { to_address: receiver.clone().into(), - amount: vec![Coin::new(total_native_to_refund.u128(), &denom)], + amount: vec![Coin::new(total_native_to_refund.u128(), denom)], }); let event = Event::new("steakhub/unbonded_withdrawn") diff --git a/contracts/hub/src/math.rs b/contracts/hub/src/math.rs index dab0842..d1c3602 100644 --- a/contracts/hub/src/math.rs +++ b/contracts/hub/src/math.rs @@ -64,7 +64,7 @@ pub(crate) fn compute_undelegations( let mut new_undelegations: Vec = vec![]; let mut native_available = native_to_unbond.u128(); for (i, d) in current_delegations.iter().enumerate() { - let remainder_for_validator: u128 = u128::from( (i+1) as u128 <=remainder) as u128 ; + let remainder_for_validator: u128 = u128::from((i + 1) as u128 <= remainder); let native_for_validator = native_per_validator + remainder_for_validator; let mut native_to_undelegate = if d.amount < native_for_validator { @@ -109,7 +109,7 @@ pub(crate) fn compute_redelegations_for_removal( let mut new_redelegations: Vec = vec![]; let mut native_available = delegation_to_remove.amount; for (i, d) in current_delegations.iter().enumerate() { - let remainder_for_validator: u128 = u128::from( (i+1) as u128 <=remainder) as u128 ; + let remainder_for_validator: u128 = u128::from((i + 1) as u128 <= remainder); let native_for_validator = native_per_validator + remainder_for_validator; let mut native_to_redelegate = if d.amount > native_for_validator { @@ -143,9 +143,9 @@ pub(crate) fn compute_redelegations_for_removal( /// /// This algorithm does not guarantee the minimal number of moves, but is the best I can some up with... pub(crate) fn compute_redelegations_for_rebalancing( - validators_active:Vec, + validators_active: Vec, current_delegations: &[Delegation], - min_difference: Uint128 + min_difference: Uint128, ) -> Vec { let native_staked: u128 = current_delegations.iter().map(|d| d.amount).sum(); let validator_count = validators_active.len() as u128; @@ -160,33 +160,31 @@ pub(crate) fn compute_redelegations_for_rebalancing( let mut src_delegations: Vec = vec![]; let mut dst_delegations: Vec = vec![]; for (i, d) in current_delegations.iter().enumerate() { - let remainder_for_validator: u128 = u128::from( (i+1) as u128 <=remainder) as u128 ; + let remainder_for_validator: u128 = u128::from((i + 1) as u128 <= remainder); let native_for_validator = native_per_validator + remainder_for_validator; - // eprintln!("{} amount ={} native={} min={}", d.validator, d.amount, native_for_validator, min_difference); - match d.amount.cmp(&native_for_validator) { - Ordering::Greater => { - if d.amount - native_for_validator > min_difference.u128() { - src_delegations.push(Delegation::new( - &d.validator, - d.amount - native_for_validator, - &d.denom, - )); - } + // eprintln!("{} amount ={} native={} min={}", d.validator, d.amount, native_for_validator, min_difference); + match d.amount.cmp(&native_for_validator) { + Ordering::Greater => { + if d.amount - native_for_validator > min_difference.u128() { + src_delegations.push(Delegation::new( + &d.validator, + d.amount - native_for_validator, + &d.denom, + )); } - Ordering::Less => { - if validators_active.contains(&d.validator) && - native_for_validator - d.amount > min_difference.u128() { - dst_delegations.push(Delegation::new( - &d.validator, - native_for_validator - d.amount, - &d.denom, - )); - - } + } + Ordering::Less => { + if validators_active.contains(&d.validator) && + native_for_validator - d.amount > min_difference.u128() { + dst_delegations.push(Delegation::new( + &d.validator, + native_for_validator - d.amount, + &d.denom, + )); } - Ordering::Equal => (), } - + Ordering::Equal => (), + } } let mut new_redelegations: Vec = vec![]; @@ -206,15 +204,14 @@ pub(crate) fn compute_redelegations_for_rebalancing( } else { dst_delegations[0].amount -= native_to_redelegate; } - new_redelegations.push(Redelegation::new( - &src_delegation.validator, - &dst_delegation.validator, - native_to_redelegate, - &src_delegation.denom, - )); - + new_redelegations.push(Redelegation::new( + &src_delegation.validator, + &dst_delegation.validator, + native_to_redelegate, + &src_delegation.denom, + )); } - // eprintln!("new redelegations ={:?}", new_redelegations); + // eprintln!("new redelegations ={:?}", new_redelegations); new_redelegations } @@ -235,7 +232,7 @@ pub(crate) fn reconcile_batches(batches: &mut [Batch], native_to_deduct: Uint128 let remainder = native_to_deduct.u128() % batch_count; for (i, batch) in batches.iter_mut().enumerate() { - let remainder_for_batch: u128 = u128::from( (i+1) as u128 <=remainder) as u128 ; + let remainder_for_batch: u128 = u128::from((i + 1) as u128 <= remainder); let native_for_batch = native_per_batch + remainder_for_batch; batch.amount_unclaimed -= Uint128::new(native_for_batch); diff --git a/contracts/hub/src/queries.rs b/contracts/hub/src/queries.rs index 61944cf..2e7eefb 100644 --- a/contracts/hub/src/queries.rs +++ b/contracts/hub/src/queries.rs @@ -1,3 +1,6 @@ +use std::collections::HashSet; +use std::iter::FromIterator; + use cosmwasm_std::{Addr, Decimal, Deps, Env, Order, StdResult, Uint128}; use cw_storage_plus::{Bound, CwIntKey}; @@ -39,8 +42,12 @@ pub fn state(deps: Deps, env: Env) -> StdResult { let steak_token = state.steak_token.load(deps.storage)?; let total_usteak = query_cw20_total_supply(&deps.querier, &steak_token)?; - let validators = state.validators.load(deps.storage)?; - let delegations = query_delegations(&deps.querier, &validators, &env.contract.address, &denom)?; + let mut validators:HashSet = HashSet::from_iter(state.validators.load(deps.storage)?); + let validators_active:HashSet = HashSet::from_iter(state.validators_active.load(deps.storage)?); + validators.extend(validators_active); + let validator_vec: Vec = Vec::from_iter(validators.into_iter()); + + let delegations = query_delegations(&deps.querier, &validator_vec, &env.contract.address, &denom)?; let total_native: u128 = delegations.iter().map(|d| d.amount).sum(); let exchange_rate = if total_usteak.is_zero() { diff --git a/contracts/hub/src/testing/tests.rs b/contracts/hub/src/testing/tests.rs index 8b8931c..58c0754 100644 --- a/contracts/hub/src/testing/tests.rs +++ b/contracts/hub/src/testing/tests.rs @@ -280,7 +280,11 @@ fn bonding() { ) .unwrap(); - assert_eq!(res.messages.len(), 2); + // 3 messages. (switched to 3 so we can 'send' instead of 'transfer' minted tokens, so contract will know about it + // 1 - delegate + // 2 - mint token (to ourselves) + // 3 - send/transfer it + assert_eq!(res.messages.len(), 3); assert_eq!( res.messages[0], SubMsg::reply_on_success(Delegation::new("alice", 1_000_000, "uxyz").to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS) @@ -292,7 +296,7 @@ fn bonding() { msg: CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: "steak_token".to_string(), msg: to_binary(&Cw20ExecuteMsg::Mint { - recipient: "user_1".to_string(), + recipient: "cosmos2contract".to_string(), amount: Uint128::new(1_000_000) }) .unwrap(), @@ -302,17 +306,16 @@ fn bonding() { reply_on: ReplyOn::Never, } ); - /* + assert_eq!( res.messages[2], SubMsg { id: 0, msg: CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: "steak_token".to_string(), - msg: to_binary(&Cw20ExecuteMsg::Send { - contract: "user_1".to_string(), + msg: to_binary(&Cw20ExecuteMsg::Transfer { + recipient: "user_1".to_string(), amount: Uint128::new(1000000), - msg: Default::default() }) .unwrap(), funds: vec![] @@ -322,7 +325,7 @@ fn bonding() { } ); - */ + // Bond when there are existing delegations, and Luna:Steak exchange rate is >1 // Previously user 1 delegated 1,000,000 uluna. We assume we have accumulated 2.5% yield at 1025000 staked @@ -344,7 +347,7 @@ fn bonding() { ) .unwrap(); - assert_eq!(res.messages.len(), 2); + assert_eq!(res.messages.len(), 3); assert_eq!( res.messages[0], SubMsg::reply_on_success(Delegation::new("charlie", 12345, "uxyz").to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS) @@ -356,7 +359,7 @@ fn bonding() { msg: CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: "steak_token".to_string(), msg: to_binary(&Cw20ExecuteMsg::Mint { - recipient: "user_3".to_string(), + recipient: "cosmos2contract".to_string(), amount: Uint128::new(12043) }) .unwrap(), @@ -366,17 +369,16 @@ fn bonding() { reply_on: ReplyOn::Never } ); - /* + assert_eq!( res.messages[2], SubMsg { id: 0, msg: CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: "steak_token".to_string(), - msg: to_binary(&Cw20ExecuteMsg::Send { - contract: "user_3".to_string(), + msg: to_binary(&Cw20ExecuteMsg::Transfer { + recipient: "user_3".to_string(), amount: Uint128::new(12043), - msg: Default::default(), }) .unwrap(), funds: vec![] @@ -385,7 +387,7 @@ fn bonding() { reply_on: ReplyOn::Never } ); -*/ + // Check the state after bonding deps.querier.set_staking_delegations(&[ Delegation::new("alice", 341667, "uxyz"), From ff2619edf12c5f7c5f24551d87340f27d84d37e1 Mon Sep 17 00:00:00 2001 From: The PFC <81114960+PFC-developer@users.noreply.github.com> Date: Sun, 15 Jan 2023 11:56:56 -0600 Subject: [PATCH 27/77] add stars testnet init --- init/elgafar_hub_init.json | 25 + scripts/.eslintrc.json | 11 - scripts/1_manage_keys.ts | 102 - scripts/2_deploy.ts | 84 - scripts/3_migrate.ts | 69 - scripts/4_bond.ts | 49 - scripts/5_harvest.ts | 38 - scripts/6_rebalance.ts | 38 - scripts/7_queue_unbond.ts | 52 - scripts/8_submit_batch.ts | 38 - scripts/9_withdraw_unbonded.ts | 38 - scripts/README.md | 39 - scripts/helpers.ts | 114 - scripts/keystore.ts | 87 - scripts/package-lock.json | 4182 -------------------------------- scripts/package.json | 21 - scripts/tsconfig.json | 24 - 17 files changed, 25 insertions(+), 4986 deletions(-) create mode 100644 init/elgafar_hub_init.json delete mode 100644 scripts/.eslintrc.json delete mode 100644 scripts/1_manage_keys.ts delete mode 100644 scripts/2_deploy.ts delete mode 100644 scripts/3_migrate.ts delete mode 100644 scripts/4_bond.ts delete mode 100644 scripts/5_harvest.ts delete mode 100644 scripts/6_rebalance.ts delete mode 100644 scripts/7_queue_unbond.ts delete mode 100644 scripts/8_submit_batch.ts delete mode 100644 scripts/9_withdraw_unbonded.ts delete mode 100644 scripts/README.md delete mode 100644 scripts/helpers.ts delete mode 100644 scripts/keystore.ts delete mode 100644 scripts/package-lock.json delete mode 100644 scripts/package.json delete mode 100644 scripts/tsconfig.json diff --git a/init/elgafar_hub_init.json b/init/elgafar_hub_init.json new file mode 100644 index 0000000..8e970ac --- /dev/null +++ b/init/elgafar_hub_init.json @@ -0,0 +1,25 @@ +{ + "cw20_code_id": 801, + "owner": "stars1kdtdg0lvy8asxn8clnjfpusuvf93zuknxrad8g", + "name": "bStars Token", + "symbol": "bStars", + "decimals": 6, + "epoch_period": 259200, + "unbond_period": 1209600, + "validators": [ + "starsvaloper1jt9w26mpxxjsk63mvd4m2ynj0af09cslura0ec", + "starsvaloper12xlxetf292m9llh5y9gwv92wkqwwagxulwl5l0", + "starsvaloper1ck4n6fq2er7g7380pdzqhxjlk6tevyzp7389vm" + ], + "denom": "ustars", + "fee_account": "stars1xek55lr3hzchhdp5z0t0xgzhcf0362vz54teqgne50f0ysgn9yrshdhd0z", + "fee_amount": "0.10", + "max_fee_amount": "0.10", + "fee_account_type": "FeeSplit", + "label": "boneStars", + "marketing": { + "project": "https://backbonelabs.io", + "description": "The GraveDigger is the Liquid Staking Product of BackBone Labs. It's liquid staking derivative (LSD) is boneStars.", + "marketing": "stars1kdtdg0lvy8asxn8clnjfpusuvf93zuknxrad8g" + } +} \ No newline at end of file diff --git a/scripts/.eslintrc.json b/scripts/.eslintrc.json deleted file mode 100644 index 93c4a4e..0000000 --- a/scripts/.eslintrc.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": 2020, - "sourceType": "module" - }, - "extends": [ - "plugin:@typescript-eslint/recommended" - ], - "rules": {} -} diff --git a/scripts/1_manage_keys.ts b/scripts/1_manage_keys.ts deleted file mode 100644 index b53e21e..0000000 --- a/scripts/1_manage_keys.ts +++ /dev/null @@ -1,102 +0,0 @@ -import * as fs from "fs"; -import * as path from "path"; -import * as promptly from "promptly"; -import yargs from "yargs"; -import { hideBin } from "yargs/helpers"; -import * as keystore from "./keystore"; - -async function addKey(keyName: string, keyDir: string, coinType: number) { - if (!fs.existsSync(keyDir)) { - fs.mkdirSync(keyDir, { recursive: true }); - } - - const mnemonic = await promptly.prompt("Enter BIP-39 seed phrase:"); - - const password = await promptly.password("Enter a password to encrypt the key:"); - const repeat = await promptly.password("Repeat the password:"); - if (password != repeat) { - throw new Error("Passwords don't match!"); - } - - const accAddress = keystore.save(keyName, keyDir, mnemonic, coinType, password); - console.log("Success! Address:", accAddress); -} - -function listKeys(keyDir: string) { - fs.readdirSync(keyDir) - .filter((fn) => { - return fn.endsWith(".json"); - }) - .sort() - .forEach((fn) => { - const entity: keystore.Entity = JSON.parse(fs.readFileSync(path.join(keyDir, fn), "utf8")); - console.log(`- name: ${entity.name}`); - console.log(` address: ${entity.address}`); - }); -} - -function removeKey(keyName: string, keyDir: string) { - keystore.remove(keyName, keyDir); - console.log("Success!"); -} - -yargs(hideBin(process.argv)) - .command( - "add ", - "Add a key with the given name", - (yargs) => { - return yargs - .positional("key", { - type: "string", - describe: "name of the key", - demandOption: true, - }) - .option("key-dir", { - type: "string", - describe: "path to the directory where encrypted key files are stored", - demandOption: false, - default: keystore.DEFAULT_KEY_DIR, - }) - .option("coin-type", { - type: "number", - describe: "SLIP-0044 coin type for use in derivation of the private key", - demandOption: false, - default: 118, // Terra = 330, Cosmos = 118 - }); - }, - (argv) => addKey(argv["key"], argv["key-dir"], argv["coin-type"]).catch(console.log) - ) - .command( - "rm ", - "Remove a key of the given name", - (yargs) => { - return yargs - .positional("key", { - type: "string", - describe: "name of the key", - demandOption: true, - }) - .option("key-dir", { - type: "string", - describe: "path to the directory where encrypted key files are stored", - demandOption: false, - default: keystore.DEFAULT_KEY_DIR, - }); - }, - (argv) => removeKey(argv["key"], argv["key-dir"]) - ) - .command( - "ls", - "List all keys", - (yargs) => { - return yargs.option("key-dir", { - type: "string", - describe: "path to the directory where encrypted key files are stored", - demandOption: false, - default: keystore.DEFAULT_KEY_DIR, - }); - }, - (argv) => listKeys(argv["key-dir"]) - ) - .wrap(100) - .parse(); diff --git a/scripts/2_deploy.ts b/scripts/2_deploy.ts deleted file mode 100644 index f8c1622..0000000 --- a/scripts/2_deploy.ts +++ /dev/null @@ -1,84 +0,0 @@ -import * as fs from "fs"; -import * as path from "path"; -import yargs from "yargs/yargs"; -import * as keystore from "./keystore"; -import { - createLCDClient, - createWallet, - waitForConfirm, - storeCodeWithConfirm, - instantiateWithConfirm, -} from "./helpers"; -import { Wallet } from "@terra-money/terra.js"; - -const argv = yargs(process.argv) - .options({ - network: { - type: "string", - demandOption: true, - }, - key: { - type: "string", - demandOption: true, - }, - "key-dir": { - type: "string", - demandOption: false, - default: keystore.DEFAULT_KEY_DIR, - }, - admin: { - type: "string", - demandOption: false, - }, - msg: { - type: "string", - demandOption: true, - }, - "hub-code-id": { - type: "number", - demandOption: false, - }, - "token-code-id": { - type: "number", - demandOption: false, - }, - "hub-binary": { - type: "string", - demandOption: false, - default: "../artifacts/steak_hub.wasm", - }, - "token-binary": { - type: "string", - demandOption: false, - default: "../artifacts/steak_token.wasm", - }, - }) - .parseSync(); - -async function uploadCode(deployer: Wallet, path: string) { - await waitForConfirm(`Upload code ${path}?`); - const codeId = await storeCodeWithConfirm(deployer, path); - console.log(`Code uploaded! ID: ${codeId}`); - return codeId; -} - -(async function () { - const terra = createLCDClient(argv["network"]); - const deployer = await createWallet(terra, argv["key"], argv["key-dir"]); - - const hubCodeId = argv["hub-code-id"] ?? await uploadCode(deployer, path.resolve(argv["hub-binary"])); - const tokenCodeId = argv["token-code-id"] ?? await uploadCode(deployer, path.resolve(argv["token-binary"])); - - const msg = JSON.parse(fs.readFileSync(path.resolve(argv["msg"]), "utf8")); - msg["cw20_code_id"] = tokenCodeId; - - await waitForConfirm("Proceed to deploy contracts?"); - const result = await instantiateWithConfirm( - deployer, - argv["admin"] ?? deployer.key.accAddress, - hubCodeId, - msg - ); - const address = result.logs[0].eventsByType["instantiate_contract"]["contract_address"][0]; - console.log(`Contract instantiated! Address: ${address}`); -})(); diff --git a/scripts/3_migrate.ts b/scripts/3_migrate.ts deleted file mode 100644 index 4e82976..0000000 --- a/scripts/3_migrate.ts +++ /dev/null @@ -1,69 +0,0 @@ -import * as path from "path"; -import yargs from "yargs/yargs"; -import { MsgMigrateContract } from "@terra-money/terra.js"; -import * as keystore from "./keystore"; -import { - createLCDClient, - createWallet, - waitForConfirm, - sendTxWithConfirm, - storeCodeWithConfirm, -} from "./helpers"; - -const argv = yargs(process.argv) - .options({ - network: { - type: "string", - demandOption: true, - }, - key: { - type: "string", - demandOption: true, - }, - "key-dir": { - type: "string", - demandOption: false, - default: keystore.DEFAULT_KEY_DIR, - }, - "contract-address": { - type: "string", - demandOption: true, - }, - msg: { - type: "string", - demandOption: false, - default: "{}", - }, - "code-id": { - type: "number", - demandOption: false, - }, - binary: { - type: "string", - demandOption: false, - default: "../artifacts/steak_hub.wasm", - }, - }) - .parseSync(); - -(async function () { - const terra = createLCDClient(argv["network"]); - const admin = await createWallet(terra, argv["key"], argv["key-dir"]); - - let codeId = argv["code-id"]; - if (!codeId) { - codeId = await storeCodeWithConfirm(admin, path.resolve(argv["binary"])); - console.log(`Code uploaded! codeId: ${codeId}`); - await waitForConfirm("Proceed to migrate contract?"); - } - - const { txhash } = await sendTxWithConfirm(admin, [ - new MsgMigrateContract( - admin.key.accAddress, - argv["contract-address"], - codeId, - JSON.parse(argv["msg"]) - ), - ]); - console.log(`Contract migrated! Txhash: ${txhash}`); -})(); diff --git a/scripts/4_bond.ts b/scripts/4_bond.ts deleted file mode 100644 index 74c9c28..0000000 --- a/scripts/4_bond.ts +++ /dev/null @@ -1,49 +0,0 @@ -import yargs from "yargs/yargs"; -import { MsgExecuteContract } from "@terra-money/terra.js"; -import * as keystore from "./keystore"; -import { createLCDClient, createWallet, sendTxWithConfirm } from "./helpers"; - -const argv = yargs(process.argv) - .options({ - network: { - type: "string", - demandOption: true, - }, - key: { - type: "string", - demandOption: true, - }, - "key-dir": { - type: "string", - demandOption: false, - default: keystore.DEFAULT_KEY_DIR, - }, - "hub-address": { - type: "string", - demandOption: true, - }, - amount: { - type: "string", - demandOption: true, - }, - }) - .parseSync(); - -(async function () { - const terra = createLCDClient(argv["network"]); - const user = await createWallet(terra, argv["key"], argv["key-dir"]); - - const { txhash } = await sendTxWithConfirm(user, [ - new MsgExecuteContract( - user.key.accAddress, - argv["hub-address"], - { - bond: {}, - }, - { - uluna: argv["amount"], - } - ), - ]); - console.log(`Success! Txhash: ${txhash}`); -})(); diff --git a/scripts/5_harvest.ts b/scripts/5_harvest.ts deleted file mode 100644 index 0588b39..0000000 --- a/scripts/5_harvest.ts +++ /dev/null @@ -1,38 +0,0 @@ -import yargs from "yargs/yargs"; -import { MsgExecuteContract } from "@terra-money/terra.js"; -import * as keystore from "./keystore"; -import { createLCDClient, createWallet, sendTxWithConfirm } from "./helpers"; - -const argv = yargs(process.argv) - .options({ - network: { - type: "string", - demandOption: true, - }, - key: { - type: "string", - demandOption: true, - }, - "key-dir": { - type: "string", - demandOption: false, - default: keystore.DEFAULT_KEY_DIR, - }, - "hub-address": { - type: "string", - demandOption: true, - }, - }) - .parseSync(); - -(async function () { - const terra = createLCDClient(argv["network"]); - const worker = await createWallet(terra, argv["key"], argv["key-dir"]); - - const { txhash } = await sendTxWithConfirm(worker, [ - new MsgExecuteContract(worker.key.accAddress, argv["hub-address"], { - harvest: {}, - }), - ]); - console.log(`Success! Txhash: ${txhash}`); -})(); diff --git a/scripts/6_rebalance.ts b/scripts/6_rebalance.ts deleted file mode 100644 index 99caf2a..0000000 --- a/scripts/6_rebalance.ts +++ /dev/null @@ -1,38 +0,0 @@ -import yargs from "yargs/yargs"; -import { MsgExecuteContract } from "@terra-money/terra.js"; -import * as keystore from "./keystore"; -import { createLCDClient, createWallet, sendTxWithConfirm } from "./helpers"; - -const argv = yargs(process.argv) - .options({ - network: { - type: "string", - demandOption: true, - }, - key: { - type: "string", - demandOption: true, - }, - "key-dir": { - type: "string", - demandOption: false, - default: keystore.DEFAULT_KEY_DIR, - }, - "hub-address": { - type: "string", - demandOption: true, - }, - }) - .parseSync(); - -(async function () { - const terra = createLCDClient(argv["network"]); - const worker = await createWallet(terra, argv["key"], argv["key-dir"]); - - const { txhash } = await sendTxWithConfirm(worker, [ - new MsgExecuteContract(worker.key.accAddress, argv["hub-address"], { - rebalance: {}, - }), - ]); - console.log(`Success! Txhash: ${txhash}`); -})(); diff --git a/scripts/7_queue_unbond.ts b/scripts/7_queue_unbond.ts deleted file mode 100644 index 1488ef8..0000000 --- a/scripts/7_queue_unbond.ts +++ /dev/null @@ -1,52 +0,0 @@ -import yargs from "yargs/yargs"; -import { MsgExecuteContract } from "@terra-money/terra.js"; -import * as keystore from "./keystore"; -import { createLCDClient, createWallet, encodeBase64, sendTxWithConfirm } from "./helpers"; - -const argv = yargs(process.argv) - .options({ - network: { - type: "string", - demandOption: true, - }, - key: { - type: "string", - demandOption: true, - }, - "key-dir": { - type: "string", - demandOption: false, - default: keystore.DEFAULT_KEY_DIR, - }, - "hub-address": { - type: "string", - demandOption: true, - }, - amount: { - type: "string", - demandOption: true, - }, - }) - .parseSync(); - -(async function () { - const terra = createLCDClient(argv["network"]); - const worker = await createWallet(terra, argv["key"], argv["key-dir"]); - - const config: { steak_token: string } = await terra.wasm.contractQuery(argv["hub-address"], { - config: {}, - }); - - const { txhash } = await sendTxWithConfirm(worker, [ - new MsgExecuteContract(worker.key.accAddress, config["steak_token"], { - send: { - contract: argv["hub-address"], - amount: argv["amount"], - msg: encodeBase64({ - queue_unbond: {}, - }), - }, - }), - ]); - console.log(`Success! Txhash: ${txhash}`); -})(); diff --git a/scripts/8_submit_batch.ts b/scripts/8_submit_batch.ts deleted file mode 100644 index 6e2e01f..0000000 --- a/scripts/8_submit_batch.ts +++ /dev/null @@ -1,38 +0,0 @@ -import yargs from "yargs/yargs"; -import { MsgExecuteContract } from "@terra-money/terra.js"; -import * as keystore from "./keystore"; -import { createLCDClient, createWallet, sendTxWithConfirm } from "./helpers"; - -const argv = yargs(process.argv) - .options({ - network: { - type: "string", - demandOption: true, - }, - key: { - type: "string", - demandOption: true, - }, - "key-dir": { - type: "string", - demandOption: false, - default: keystore.DEFAULT_KEY_DIR, - }, - "hub-address": { - type: "string", - demandOption: true, - }, - }) - .parseSync(); - -(async function () { - const terra = createLCDClient(argv["network"]); - const worker = await createWallet(terra, argv["key"], argv["key-dir"]); - - const { txhash } = await sendTxWithConfirm(worker, [ - new MsgExecuteContract(worker.key.accAddress, argv["hub-address"], { - submit_batch: {}, - }), - ]); - console.log(`Success! Txhash: ${txhash}`); -})(); diff --git a/scripts/9_withdraw_unbonded.ts b/scripts/9_withdraw_unbonded.ts deleted file mode 100644 index e230621..0000000 --- a/scripts/9_withdraw_unbonded.ts +++ /dev/null @@ -1,38 +0,0 @@ -import yargs from "yargs/yargs"; -import { MsgExecuteContract } from "@terra-money/terra.js"; -import * as keystore from "./keystore"; -import { createLCDClient, createWallet, sendTxWithConfirm } from "./helpers"; - -const argv = yargs(process.argv) - .options({ - network: { - type: "string", - demandOption: true, - }, - key: { - type: "string", - demandOption: true, - }, - "key-dir": { - type: "string", - demandOption: false, - default: keystore.DEFAULT_KEY_DIR, - }, - "hub-address": { - type: "string", - demandOption: true, - }, - }) - .parseSync(); - -(async function () { - const terra = createLCDClient(argv["network"]); - const worker = await createWallet(terra, argv["key"], argv["key-dir"]); - - const { txhash } = await sendTxWithConfirm(worker, [ - new MsgExecuteContract(worker.key.accAddress, argv["hub-address"], { - withdraw_unbonded: {}, - }), - ]); - console.log(`Success! Txhash: ${txhash}`); -})(); diff --git a/scripts/README.md b/scripts/README.md deleted file mode 100644 index 3096c82..0000000 --- a/scripts/README.md +++ /dev/null @@ -1,39 +0,0 @@ -# Scripts - -This directory contains scripts to deploy, migrate, or interact with Steak Hub smart contract. - -## How to Use - -Insteall dependencies: - -```bash -cd steak/scripts -npm install -``` - -Import the key to use to sign transactions. You will be prompted to enter the seed phrase and a password to encrypt the private key. By default, the encrypted key will be saved at `steak/scripts/keys/{keyname}.json`. The script also provide commands to list or remove keys. - -```bash -ts-node 1_manage_keys.ts add [--key-dir string] -``` - -To deploy the contract, create a JSON file containing the instantiation message, and use the following command. You will be prompted to enter the password to decrypt the private key. - -```bash -ts-node 2_deploy.ts \ - --network mainnet|testnet|localterra \ - --key keyname \ - --msg /path/to/instantiate_msg.json -``` - -To stake Luna and mint Steak: - -```bash -ts-node 4_bond.ts \ - --network mainnet|testnet|localterra \ - --key keyname \ - --contract-address terra... \ - --amount 1000000 -``` - -Other scripts work similarly to the examples above. diff --git a/scripts/helpers.ts b/scripts/helpers.ts deleted file mode 100644 index 7831cd4..0000000 --- a/scripts/helpers.ts +++ /dev/null @@ -1,114 +0,0 @@ -import * as fs from "fs"; -import * as promptly from "promptly"; -import { - isTxError, - LCDClient, - LocalTerra, - Msg, - MsgInstantiateContract, - MsgStoreCode, - Wallet, -} from "@terra-money/terra.js"; -import * as keystore from "./keystore"; - -const DEFAULT_GAS_SETTINGS = { - gasPrices: "1.25usek", - gasAdjustment: 1.2, -}; - -/** - * @notice Create an `LCDClient` instance based on provided network identifier - */ -export function createLCDClient(network: string): LCDClient { - if (network === "mainnet") { - return new LCDClient({ - chainID: "phoenix-1", - URL: "https://lcd.terra.dev", - }); - } else if (network === "testnet") { - return new LCDClient({ - chainID: "pisco-1", - URL: "https://terra-testnet-api.polkachu.com/", - }); - } else if (network === "localterra") { - return new LocalTerra(); - } else { - throw new Error( - `invalid network: ${network}, must be mainnet|testnet|localterra` - ); - } -} - -/** - * @notice Create a `Wallet` instance by loading the private key stored in the keystore - */ -export async function createWallet( - terra: LCDClient, - keyName: string, - keyDir: string -): Promise { - const password = await promptly.password( - "Enter password to decrypt the key:" - ); - return terra.wallet(keystore.load(keyName, keyDir, password)); -} - -/** - * @notice Pause script execution until user confirms - */ -export async function waitForConfirm(msg: string) { - const proceed = await promptly.confirm(`${msg} [y/N]:`); - if (!proceed) { - console.log("User aborted!"); - process.exit(1); - } -} - -/** - * @notice Same with `sendTransaction`, but requires confirmation for CLI before broadcasting - */ -export async function sendTxWithConfirm(signer: Wallet, msgs: Msg[]) { - const tx = await signer.createAndSignTx({ msgs, ...DEFAULT_GAS_SETTINGS }); - console.log("\n" + JSON.stringify(tx).replace(/\\/g, "") + "\n"); - - await waitForConfirm("Confirm transaction before broadcasting"); - - const result = await signer.lcd.tx.broadcast(tx); - if (isTxError(result)) { - throw new Error(`tx failed! raw log: ${result.raw_log}`); - } - return result; -} - -/** - * @notice Same with `storeCode`, but requires confirmation for CLI before broadcasting - */ -export async function storeCodeWithConfirm(signer: Wallet, filePath: string) { - const code = fs.readFileSync(filePath).toString("base64"); - const result = await sendTxWithConfirm(signer, [ - new MsgStoreCode(signer.key.accAddress, code), - ]); - return parseInt(result.logs[0].eventsByType["store_code"]["code_id"][0]); -} - -/** - * @notice Same with `instantiateContract`, but requires confirmation for CLI before broadcasting - */ -export async function instantiateWithConfirm( - signer: Wallet, - admin: string, - codeId: number, - initMsg: object -) { - const result = await sendTxWithConfirm(signer, [ - new MsgInstantiateContract(signer.key.accAddress, admin, codeId, initMsg), - ]); - return result; -} - -/** - * Encode a JSON object to base64 string - */ -export function encodeBase64(obj: object | string | number) { - return Buffer.from(JSON.stringify(obj)).toString("base64"); -} diff --git a/scripts/keystore.ts b/scripts/keystore.ts deleted file mode 100644 index 5f869bf..0000000 --- a/scripts/keystore.ts +++ /dev/null @@ -1,87 +0,0 @@ -import * as fs from "fs"; -import * as path from "path"; -import * as crypto from "crypto"; - -import { MnemonicKey, RawKey } from "@terra-money/terra.js"; - -export const DEFAULT_KEY_DIR = path.join(__dirname, "./keys"); - -const KEY_SIZE = 256; -const ITERATIONS = 100; - -export type Entity = { - name: string; - address: string; - cipherText: string; -}; - -function encrypt(plainText: string, password: string): string { - const salt = crypto.randomBytes(16); - const iv = crypto.randomBytes(16); - const key = crypto.pbkdf2Sync(password, salt, ITERATIONS, KEY_SIZE / 8, "sha1"); - - const cipher = crypto.createCipheriv("AES-256-CBC", key, iv); - const encryptedText = Buffer.concat([cipher.update(plainText), cipher.final()]); - - return salt.toString("hex") + iv.toString("hex") + encryptedText.toString("base64"); -} - -function decrypt(cipherText: string, password: string): string { - const salt = Buffer.from(cipherText.slice(0, 32), "hex"); - const iv = Buffer.from(cipherText.slice(32, 64), "hex"); - const key = crypto.pbkdf2Sync(password, salt, ITERATIONS, KEY_SIZE / 8, "sha1"); - - const encrypedText = cipherText.slice(64); - const cipher = crypto.createDecipheriv("AES-256-CBC", key, iv); - const decryptedText = Buffer.concat([cipher.update(encrypedText, "base64"), cipher.final()]); - - return decryptedText.toString(); -} - -export function save( - keyName: string, - keyDir: string, - mnemonic: string, - coinType: number, - password: string -) { - const filePath = path.join(keyDir, `${keyName}.json`); - if (fs.existsSync(filePath)) { - throw new Error(`file ${filePath} already exists!`); - } - - const mnemonicKey = new MnemonicKey({ mnemonic, coinType }); - const privateKey = mnemonicKey.privateKey.toString("hex"); - const cipherText = encrypt(privateKey, password); - - const entity: Entity = { - name: keyName, - address: mnemonicKey.accAddress, - cipherText, - }; - fs.writeFileSync(filePath, JSON.stringify(entity, null, 2)); - - return mnemonicKey.accAddress; -} - -export function load(keyName: string, keyDir: string, password: string): RawKey { - const filePath = path.join(keyDir, `${keyName}.json`); - if (!fs.existsSync(filePath)) { - throw new Error(`file ${filePath} does not exist!`); - } - - const entity: Entity = JSON.parse(fs.readFileSync(filePath, "utf8")); - const privateKey = decrypt(entity.cipherText, password); - const rawKey = new RawKey(Buffer.from(privateKey, "hex")); - - return rawKey; -} - -export function remove(keyName: string, keyDir: string) { - const filePath = path.join(keyDir, `${keyName}.json`); - if (!fs.existsSync(filePath)) { - throw new Error(`file ${filePath} does not exist!`); - } - - fs.unlinkSync(filePath); -} diff --git a/scripts/package-lock.json b/scripts/package-lock.json deleted file mode 100644 index f0cb54c..0000000 --- a/scripts/package-lock.json +++ /dev/null @@ -1,4182 +0,0 @@ -{ - "name": "scripts", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "dependencies": { - "@terra-money/terra.js": "^3.1.2", - "promptly": "^3.2.0", - "ts-node": "^10.7.0", - "yargs": "^17.4.0" - }, - "devDependencies": { - "@types/promptly": "^3.0.2", - "@types/yargs": "^17.0.10", - "@typescript-eslint/eslint-plugin": "^5.16.0", - "@typescript-eslint/parser": "^5.16.0", - "eslint": "^8.11.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-prettier": "^4.0.0", - "typescript": "^4.6.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@cspotcode/source-map-consumer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", - "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", - "engines": { - "node": ">= 12" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", - "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", - "dependencies": { - "@cspotcode/source-map-consumer": "0.8.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", - "integrity": "sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.3.1", - "globals": "^13.9.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", - "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "node_modules/@improbable-eng/grpc-web": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@improbable-eng/grpc-web/-/grpc-web-0.14.1.tgz", - "integrity": "sha512-XaIYuunepPxoiGVLLHmlnVminUGzBTnXr8Wv7khzmLWbNw4TCwJKX09GSMJlKhu/TRk6gms0ySFxewaETSBqgw==", - "dependencies": { - "browser-headers": "^0.4.1" - }, - "peerDependencies": { - "google-protobuf": "^3.14.0" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" - }, - "node_modules/@terra-money/legacy.proto": { - "name": "@terra-money/terra.proto", - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/@terra-money/terra.proto/-/terra.proto-0.1.7.tgz", - "integrity": "sha512-NXD7f6pQCulvo6+mv6MAPzhOkUzRjgYVuHZE/apih+lVnPG5hDBU0rRYnOGGofwvKT5/jQoOENnFn/gioWWnyQ==", - "dependencies": { - "google-protobuf": "^3.17.3", - "long": "^4.0.0", - "protobufjs": "~6.11.2" - } - }, - "node_modules/@terra-money/terra.js": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@terra-money/terra.js/-/terra.js-3.1.2.tgz", - "integrity": "sha512-jEvgujX/gbXzq6aTlYORw4chuk8b46weIArThDmpvQN8c3P3GrcMZYsI7uPUFtoWhcKO8+qI++dWzIIFoNhmMQ==", - "dependencies": { - "@terra-money/legacy.proto": "npm:@terra-money/terra.proto@^0.1.7", - "@terra-money/terra.proto": "^0.2.0-beta.4", - "axios": "^0.26.1", - "bech32": "^2.0.0", - "bip32": "^2.0.6", - "bip39": "^3.0.3", - "bufferutil": "^4.0.3", - "decimal.js": "^10.2.1", - "jscrypto": "^1.0.1", - "readable-stream": "^3.6.0", - "secp256k1": "^4.0.2", - "tmp": "^0.2.1", - "utf-8-validate": "^5.0.5", - "ws": "^7.5.5" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@terra-money/terra.proto": { - "version": "0.2.0-beta.4", - "resolved": "https://registry.npmjs.org/@terra-money/terra.proto/-/terra.proto-0.2.0-beta.4.tgz", - "integrity": "sha512-ALwIOpiZgpELvUO27+Lgc4qQz6k5NB58YsUHX2Bf7gocspShRftps5kjj/bovZs/M6AO4J7Qj07QJRBekMinMA==", - "dependencies": { - "@improbable-eng/grpc-web": "^0.14.1", - "google-protobuf": "^3.17.3", - "long": "^4.0.0", - "protobufjs": "~6.11.2" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", - "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==" - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", - "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==" - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", - "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==" - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", - "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==" - }, - "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, - "node_modules/@types/long": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", - "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" - }, - "node_modules/@types/node": { - "version": "17.0.23", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz", - "integrity": "sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==" - }, - "node_modules/@types/promptly": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/promptly/-/promptly-3.0.2.tgz", - "integrity": "sha512-cJFwE7d8GlraY+DJoZ0NhpoJ55slkcbNsGIKMY0H+5h0xaGqXBqXz9zeu+Ey9KfN1UiHQXiIT0GroxyPYMPP/w==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/yargs": { - "version": "17.0.10", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz", - "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", - "dev": true - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.17.0.tgz", - "integrity": "sha512-qVstvQilEd89HJk3qcbKt/zZrfBZ+9h2ynpAGlWjWiizA7m/MtLT9RoX6gjtpE500vfIg8jogAkDzdCxbsFASQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.17.0", - "@typescript-eslint/type-utils": "5.17.0", - "@typescript-eslint/utils": "5.17.0", - "debug": "^4.3.2", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", - "regexpp": "^3.2.0", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.17.0.tgz", - "integrity": "sha512-aRzW9Jg5Rlj2t2/crzhA2f23SIYFlF9mchGudyP0uiD6SenIxzKoLjwzHbafgHn39dNV/TV7xwQkLfFTZlJ4ig==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.17.0", - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/typescript-estree": "5.17.0", - "debug": "^4.3.2" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", - "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/visitor-keys": "5.17.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.17.0.tgz", - "integrity": "sha512-3hU0RynUIlEuqMJA7dragb0/75gZmwNwFf/QJokWzPehTZousP/MNifVSgjxNcDCkM5HI2K22TjQWUmmHUINSg==", - "dev": true, - "dependencies": { - "@typescript-eslint/utils": "5.17.0", - "debug": "^4.3.2", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", - "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz", - "integrity": "sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/visitor-keys": "5.17.0", - "debug": "^4.3.2", - "globby": "^11.0.4", - "is-glob": "^4.0.3", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.17.0.tgz", - "integrity": "sha512-DVvndq1QoxQH+hFv+MUQHrrWZ7gQ5KcJzyjhzcqB1Y2Xes1UQQkTRPUfRpqhS8mhTWsSb2+iyvDW1Lef5DD7vA==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.17.0", - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/typescript-estree": "5.17.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz", - "integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.17.0", - "eslint-visitor-keys": "^3.0.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/axios": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", - "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", - "dependencies": { - "follow-redirects": "^1.14.8" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/base-x": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", - "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/bech32": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", - "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==" - }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/bip32": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.6.tgz", - "integrity": "sha512-HpV5OMLLGTjSVblmrtYRfFFKuQB+GArM0+XP8HGWfJ5vxYBqo+DesvJwOdC2WJ3bCkZShGf0QIfoIpeomVzVdA==", - "dependencies": { - "@types/node": "10.12.18", - "bs58check": "^2.1.1", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "tiny-secp256k1": "^1.1.3", - "typeforce": "^1.11.5", - "wif": "^2.0.6" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/bip32/node_modules/@types/node": { - "version": "10.12.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", - "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==" - }, - "node_modules/bip39": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.0.4.tgz", - "integrity": "sha512-YZKQlb752TrUWqHWj7XAwCSjYEgGAk+/Aas3V7NyjQeZYsztO8JnQUaCWhcnL4T+jL8nvB8typ2jRPzTlgugNw==", - "dependencies": { - "@types/node": "11.11.6", - "create-hash": "^1.1.0", - "pbkdf2": "^3.0.9", - "randombytes": "^2.0.1" - } - }, - "node_modules/bip39/node_modules/@types/node": { - "version": "11.11.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.6.tgz", - "integrity": "sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ==" - }, - "node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" - }, - "node_modules/browser-headers": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/browser-headers/-/browser-headers-0.4.1.tgz", - "integrity": "sha512-CA9hsySZVo9371qEHjHZtYxV2cFtVj5Wj/ZHi8ooEsrtm4vOnl9Y9HmyYWk9q+05d7K3rdoAE0j3MVEFVvtQtg==" - }, - "node_modules/bs58": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", - "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=", - "dependencies": { - "base-x": "^3.0.2" - } - }, - "node_modules/bs58check": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", - "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", - "dependencies": { - "bs58": "^4.0.0", - "create-hash": "^1.1.0", - "safe-buffer": "^5.1.2" - } - }, - "node_modules/bufferutil": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.6.tgz", - "integrity": "sha512-jduaYOYtnio4aIAyc6UbvPCVcgq7nYpVnucyxr6eCYg/Woad9Hf/oxxBRDnGGjPfjUm6j5O/uBWhIu4iLebFaw==", - "hasInstallScript": true, - "dependencies": { - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=6.14.2" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "node_modules/create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dependencies": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "node_modules/create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dependencies": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decimal.js": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", - "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==" - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", - "dependencies": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.12.0.tgz", - "integrity": "sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q==", - "dev": true, - "dependencies": { - "@eslint/eslintrc": "^1.2.1", - "@humanwhocodes/config-array": "^0.9.2", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.1", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.6.0", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-config-prettier": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", - "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", - "dev": true, - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-plugin-prettier": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz", - "integrity": "sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==", - "dev": true, - "dependencies": { - "prettier-linter-helpers": "^1.0.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "peerDependencies": { - "eslint": ">=7.28.0", - "prettier": ">=2.0.0" - }, - "peerDependenciesMeta": { - "eslint-config-prettier": { - "optional": true - } - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/espree": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", - "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", - "dev": true, - "dependencies": { - "acorn": "^8.7.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "node_modules/fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", - "dev": true - }, - "node_modules/follow-redirects": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", - "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "13.13.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", - "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/google-protobuf": { - "version": "3.20.1", - "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.20.1.tgz", - "integrity": "sha512-XMf1+O32FjYIV3CYu6Tuh5PNbfNEU5Xu22X+Xkdb/DUexFlCzhvv7d5Iirm4AOwn8lv4al1YvIhzGrg2j9Zfzw==" - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/hash-base": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", - "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "dependencies": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dependencies": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "node_modules/hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dependencies": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jscrypto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/jscrypto/-/jscrypto-1.0.3.tgz", - "integrity": "sha512-lryZl0flhodv4SZHOqyb1bx5sKcJxj0VBo0Kzb4QMAg3L021IC9uGpl0RCZa+9KJwlRGSK2C80ITcwbe19OKLQ==", - "bin": { - "jscrypto": "bin/cli.js" - } - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" - }, - "node_modules/md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" - }, - "node_modules/minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" - }, - "node_modules/nan": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", - "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==" - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "node_modules/node-addon-api": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", - "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" - }, - "node_modules/node-gyp-build": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", - "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/pbkdf2": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", - "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", - "dependencies": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.1.tgz", - "integrity": "sha512-8UVbTBYGwN37Bs9LERmxCPjdvPxlEowx2urIL6urHzdb3SDq4B/Z6xLFCblrSnE4iKWcS6ziJ3aOYrc1kz/E2A==", - "dev": true, - "peer": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/promptly": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/promptly/-/promptly-3.2.0.tgz", - "integrity": "sha512-WnR9obtgW+rG4oUV3hSnNGl1pHm3V1H/qD9iJBumGSmVsSC5HpZOLuu8qdMb6yCItGfT7dcRszejr/5P3i9Pug==", - "dependencies": { - "read": "^1.0.4" - } - }, - "node_modules/protobufjs": { - "version": "6.11.3", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", - "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", - "hasInstallScript": true, - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/long": "^4.0.1", - "@types/node": ">=13.7.0", - "long": "^4.0.0" - }, - "bin": { - "pbjs": "bin/pbjs", - "pbts": "bin/pbts" - } - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/read": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", - "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", - "dependencies": { - "mute-stream": "~0.0.4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/secp256k1": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", - "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", - "hasInstallScript": true, - "dependencies": { - "elliptic": "^6.5.4", - "node-addon-api": "^2.0.0", - "node-gyp-build": "^4.2.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - }, - "bin": { - "sha.js": "bin.js" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "node_modules/tiny-secp256k1": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz", - "integrity": "sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA==", - "hasInstallScript": true, - "dependencies": { - "bindings": "^1.3.0", - "bn.js": "^4.11.8", - "create-hmac": "^1.1.7", - "elliptic": "^6.4.0", - "nan": "^2.13.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dependencies": { - "rimraf": "^3.0.0" - }, - "engines": { - "node": ">=8.17.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ts-node": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", - "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==", - "dependencies": { - "@cspotcode/source-map-support": "0.7.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.0", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typeforce": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz", - "integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==" - }, - "node_modules/typescript": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", - "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/utf-8-validate": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.9.tgz", - "integrity": "sha512-Yek7dAy0v3Kl0orwMlvi7TPtiCNrdfHNd7Gcc/pLq4BLXqfAmd0J7OWMizUQnTTJsyjKn02mU7anqwfmUP4J8Q==", - "hasInstallScript": true, - "dependencies": { - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=6.14.2" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz", - "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==" - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wif": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz", - "integrity": "sha1-CNP1IFbGZnkplyb63g1DKudLRwQ=", - "dependencies": { - "bs58check": "<3.0.0" - } - }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "node_modules/ws": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", - "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/yargs": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.0.tgz", - "integrity": "sha512-WJudfrk81yWFSOkZYpAZx4Nt7V4xp7S/uJkX0CnxovMCt1wCE8LNftPpNuF9X/u9gN5nsD7ycYtRcDf2pL3UiA==", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", - "engines": { - "node": ">=12" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "engines": { - "node": ">=6" - } - } - }, - "dependencies": { - "@cspotcode/source-map-consumer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", - "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==" - }, - "@cspotcode/source-map-support": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", - "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", - "requires": { - "@cspotcode/source-map-consumer": "0.8.0" - } - }, - "@eslint/eslintrc": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", - "integrity": "sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.3.1", - "globals": "^13.9.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - } - }, - "@humanwhocodes/config-array": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", - "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - } - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "@improbable-eng/grpc-web": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@improbable-eng/grpc-web/-/grpc-web-0.14.1.tgz", - "integrity": "sha512-XaIYuunepPxoiGVLLHmlnVminUGzBTnXr8Wv7khzmLWbNw4TCwJKX09GSMJlKhu/TRk6gms0ySFxewaETSBqgw==", - "requires": { - "browser-headers": "^0.4.1" - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" - }, - "@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" - }, - "@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" - }, - "@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" - }, - "@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "requires": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" - }, - "@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" - }, - "@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" - }, - "@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" - }, - "@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" - }, - "@terra-money/legacy.proto": { - "version": "npm:@terra-money/terra.proto@0.1.7", - "resolved": "https://registry.npmjs.org/@terra-money/terra.proto/-/terra.proto-0.1.7.tgz", - "integrity": "sha512-NXD7f6pQCulvo6+mv6MAPzhOkUzRjgYVuHZE/apih+lVnPG5hDBU0rRYnOGGofwvKT5/jQoOENnFn/gioWWnyQ==", - "requires": { - "google-protobuf": "^3.17.3", - "long": "^4.0.0", - "protobufjs": "~6.11.2" - } - }, - "@terra-money/terra.js": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@terra-money/terra.js/-/terra.js-3.1.2.tgz", - "integrity": "sha512-jEvgujX/gbXzq6aTlYORw4chuk8b46weIArThDmpvQN8c3P3GrcMZYsI7uPUFtoWhcKO8+qI++dWzIIFoNhmMQ==", - "requires": { - "@terra-money/legacy.proto": "npm:@terra-money/terra.proto@^0.1.7", - "@terra-money/terra.proto": "^0.2.0-beta.4", - "axios": "^0.26.1", - "bech32": "^2.0.0", - "bip32": "^2.0.6", - "bip39": "^3.0.3", - "bufferutil": "^4.0.3", - "decimal.js": "^10.2.1", - "jscrypto": "^1.0.1", - "readable-stream": "^3.6.0", - "secp256k1": "^4.0.2", - "tmp": "^0.2.1", - "utf-8-validate": "^5.0.5", - "ws": "^7.5.5" - } - }, - "@terra-money/terra.proto": { - "version": "0.2.0-beta.4", - "resolved": "https://registry.npmjs.org/@terra-money/terra.proto/-/terra.proto-0.2.0-beta.4.tgz", - "integrity": "sha512-ALwIOpiZgpELvUO27+Lgc4qQz6k5NB58YsUHX2Bf7gocspShRftps5kjj/bovZs/M6AO4J7Qj07QJRBekMinMA==", - "requires": { - "@improbable-eng/grpc-web": "^0.14.1", - "google-protobuf": "^3.17.3", - "long": "^4.0.0", - "protobufjs": "~6.11.2" - } - }, - "@tsconfig/node10": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", - "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==" - }, - "@tsconfig/node12": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", - "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==" - }, - "@tsconfig/node14": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", - "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==" - }, - "@tsconfig/node16": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", - "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==" - }, - "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, - "@types/long": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", - "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" - }, - "@types/node": { - "version": "17.0.23", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz", - "integrity": "sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==" - }, - "@types/promptly": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/promptly/-/promptly-3.0.2.tgz", - "integrity": "sha512-cJFwE7d8GlraY+DJoZ0NhpoJ55slkcbNsGIKMY0H+5h0xaGqXBqXz9zeu+Ey9KfN1UiHQXiIT0GroxyPYMPP/w==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/yargs": { - "version": "17.0.10", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz", - "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", - "dev": true - }, - "@typescript-eslint/eslint-plugin": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.17.0.tgz", - "integrity": "sha512-qVstvQilEd89HJk3qcbKt/zZrfBZ+9h2ynpAGlWjWiizA7m/MtLT9RoX6gjtpE500vfIg8jogAkDzdCxbsFASQ==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.17.0", - "@typescript-eslint/type-utils": "5.17.0", - "@typescript-eslint/utils": "5.17.0", - "debug": "^4.3.2", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", - "regexpp": "^3.2.0", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/parser": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.17.0.tgz", - "integrity": "sha512-aRzW9Jg5Rlj2t2/crzhA2f23SIYFlF9mchGudyP0uiD6SenIxzKoLjwzHbafgHn39dNV/TV7xwQkLfFTZlJ4ig==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.17.0", - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/typescript-estree": "5.17.0", - "debug": "^4.3.2" - } - }, - "@typescript-eslint/scope-manager": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", - "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/visitor-keys": "5.17.0" - } - }, - "@typescript-eslint/type-utils": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.17.0.tgz", - "integrity": "sha512-3hU0RynUIlEuqMJA7dragb0/75gZmwNwFf/QJokWzPehTZousP/MNifVSgjxNcDCkM5HI2K22TjQWUmmHUINSg==", - "dev": true, - "requires": { - "@typescript-eslint/utils": "5.17.0", - "debug": "^4.3.2", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/types": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", - "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz", - "integrity": "sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/visitor-keys": "5.17.0", - "debug": "^4.3.2", - "globby": "^11.0.4", - "is-glob": "^4.0.3", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/utils": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.17.0.tgz", - "integrity": "sha512-DVvndq1QoxQH+hFv+MUQHrrWZ7gQ5KcJzyjhzcqB1Y2Xes1UQQkTRPUfRpqhS8mhTWsSb2+iyvDW1Lef5DD7vA==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.17.0", - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/typescript-estree": "5.17.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz", - "integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.17.0", - "eslint-visitor-keys": "^3.0.0" - } - }, - "acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==" - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} - }, - "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==" - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "axios": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", - "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", - "requires": { - "follow-redirects": "^1.14.8" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "base-x": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", - "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "bech32": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", - "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==" - }, - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "requires": { - "file-uri-to-path": "1.0.0" - } - }, - "bip32": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.6.tgz", - "integrity": "sha512-HpV5OMLLGTjSVblmrtYRfFFKuQB+GArM0+XP8HGWfJ5vxYBqo+DesvJwOdC2WJ3bCkZShGf0QIfoIpeomVzVdA==", - "requires": { - "@types/node": "10.12.18", - "bs58check": "^2.1.1", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "tiny-secp256k1": "^1.1.3", - "typeforce": "^1.11.5", - "wif": "^2.0.6" - }, - "dependencies": { - "@types/node": { - "version": "10.12.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", - "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==" - } - } - }, - "bip39": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.0.4.tgz", - "integrity": "sha512-YZKQlb752TrUWqHWj7XAwCSjYEgGAk+/Aas3V7NyjQeZYsztO8JnQUaCWhcnL4T+jL8nvB8typ2jRPzTlgugNw==", - "requires": { - "@types/node": "11.11.6", - "create-hash": "^1.1.0", - "pbkdf2": "^3.0.9", - "randombytes": "^2.0.1" - }, - "dependencies": { - "@types/node": { - "version": "11.11.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.6.tgz", - "integrity": "sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ==" - } - } - }, - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" - }, - "browser-headers": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/browser-headers/-/browser-headers-0.4.1.tgz", - "integrity": "sha512-CA9hsySZVo9371qEHjHZtYxV2cFtVj5Wj/ZHi8ooEsrtm4vOnl9Y9HmyYWk9q+05d7K3rdoAE0j3MVEFVvtQtg==" - }, - "bs58": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", - "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=", - "requires": { - "base-x": "^3.0.2" - } - }, - "bs58check": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", - "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", - "requires": { - "bs58": "^4.0.0", - "create-hash": "^1.1.0", - "safe-buffer": "^5.1.2" - } - }, - "bufferutil": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.6.tgz", - "integrity": "sha512-jduaYOYtnio4aIAyc6UbvPCVcgq7nYpVnucyxr6eCYg/Woad9Hf/oxxBRDnGGjPfjUm6j5O/uBWhIu4iLebFaw==", - "requires": { - "node-gyp-build": "^4.3.0" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "decimal.js": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", - "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==" - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", - "requires": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "eslint": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.12.0.tgz", - "integrity": "sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q==", - "dev": true, - "requires": { - "@eslint/eslintrc": "^1.2.1", - "@humanwhocodes/config-array": "^0.9.2", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.1", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.6.0", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "eslint-config-prettier": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", - "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", - "dev": true, - "requires": {} - }, - "eslint-plugin-prettier": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz", - "integrity": "sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==", - "dev": true, - "requires": { - "prettier-linter-helpers": "^1.0.0" - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - } - } - }, - "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true - }, - "espree": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", - "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", - "dev": true, - "requires": { - "acorn": "^8.7.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^3.3.0" - } - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", - "dev": true - }, - "fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", - "dev": true - }, - "follow-redirects": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", - "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==" - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" - }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "globals": { - "version": "13.13.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", - "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "google-protobuf": { - "version": "3.20.1", - "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.20.1.tgz", - "integrity": "sha512-XMf1+O32FjYIV3CYu6Tuh5PNbfNEU5Xu22X+Xkdb/DUexFlCzhvv7d5Iirm4AOwn8lv4al1YvIhzGrg2j9Zfzw==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "hash-base": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", - "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "requires": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - } - }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "jscrypto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/jscrypto/-/jscrypto-1.0.3.tgz", - "integrity": "sha512-lryZl0flhodv4SZHOqyb1bx5sKcJxj0VBo0Kzb4QMAg3L021IC9uGpl0RCZa+9KJwlRGSK2C80ITcwbe19OKLQ==" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" - }, - "md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" - }, - "nan": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", - "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==" - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "node-addon-api": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", - "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" - }, - "node-gyp-build": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", - "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "pbkdf2": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", - "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", - "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "prettier": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.1.tgz", - "integrity": "sha512-8UVbTBYGwN37Bs9LERmxCPjdvPxlEowx2urIL6urHzdb3SDq4B/Z6xLFCblrSnE4iKWcS6ziJ3aOYrc1kz/E2A==", - "dev": true, - "peer": true - }, - "prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "requires": { - "fast-diff": "^1.1.2" - } - }, - "promptly": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/promptly/-/promptly-3.2.0.tgz", - "integrity": "sha512-WnR9obtgW+rG4oUV3hSnNGl1pHm3V1H/qD9iJBumGSmVsSC5HpZOLuu8qdMb6yCItGfT7dcRszejr/5P3i9Pug==", - "requires": { - "read": "^1.0.4" - } - }, - "protobufjs": { - "version": "6.11.3", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", - "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", - "requires": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/long": "^4.0.1", - "@types/node": ">=13.7.0", - "long": "^4.0.0" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "read": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", - "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", - "requires": { - "mute-stream": "~0.0.4" - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "requires": { - "glob": "^7.1.3" - } - }, - "ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "secp256k1": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", - "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", - "requires": { - "elliptic": "^6.5.4", - "node-addon-api": "^2.0.0", - "node-gyp-build": "^4.2.0" - } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "tiny-secp256k1": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz", - "integrity": "sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA==", - "requires": { - "bindings": "^1.3.0", - "bn.js": "^4.11.8", - "create-hmac": "^1.1.7", - "elliptic": "^6.4.0", - "nan": "^2.13.2" - } - }, - "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "requires": { - "rimraf": "^3.0.0" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "ts-node": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", - "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==", - "requires": { - "@cspotcode/source-map-support": "0.7.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.0", - "yn": "3.1.1" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - }, - "typeforce": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz", - "integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==" - }, - "typescript": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", - "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==" - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "utf-8-validate": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.9.tgz", - "integrity": "sha512-Yek7dAy0v3Kl0orwMlvi7TPtiCNrdfHNd7Gcc/pLq4BLXqfAmd0J7OWMizUQnTTJsyjKn02mU7anqwfmUP4J8Q==", - "requires": { - "node-gyp-build": "^4.3.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "v8-compile-cache-lib": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz", - "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==" - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "wif": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz", - "integrity": "sha1-CNP1IFbGZnkplyb63g1DKudLRwQ=", - "requires": { - "bs58check": "<3.0.0" - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "ws": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", - "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", - "requires": {} - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "yargs": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.0.tgz", - "integrity": "sha512-WJudfrk81yWFSOkZYpAZx4Nt7V4xp7S/uJkX0CnxovMCt1wCE8LNftPpNuF9X/u9gN5nsD7ycYtRcDf2pL3UiA==", - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" - } - }, - "yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==" - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==" - } - } -} diff --git a/scripts/package.json b/scripts/package.json deleted file mode 100644 index d60ba38..0000000 --- a/scripts/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "dependencies": { - "@terra-money/terra.js": "^3.1.2", - "promptly": "^3.2.0", - "ts-node": "^10.7.0", - "yargs": "^17.4.0" - }, - "devDependencies": { - "@types/promptly": "^3.0.2", - "@types/yargs": "^17.0.10", - "@typescript-eslint/eslint-plugin": "^5.16.0", - "@typescript-eslint/parser": "^5.16.0", - "eslint": "^8.11.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-prettier": "^4.0.0", - "typescript": "^4.6.3" - }, - "engines": { - "node": ">=16" - } -} diff --git a/scripts/tsconfig.json b/scripts/tsconfig.json deleted file mode 100644 index e80fc72..0000000 --- a/scripts/tsconfig.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "compilerOptions": { - "target": "es6", - "module": "commonjs", - "allowJs": false, - "strict": true, - "strictNullChecks": true, - "allowUnreachableCode": false, - "allowUnusedLabels": false, - "exactOptionalPropertyTypes": true, - "noEmit": true, - "noFallthroughCasesInSwitch": true, - "noImplicitOverride": true, - "noImplicitReturns": true, - "noPropertyAccessFromIndexSignature": true, - // "noUncheckedIndexedAccess": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "forceConsistentCasingInFileNames": true, - "esModuleInterop": true, - "resolveJsonModule": true - }, - "include": ["**/*.ts"] -} \ No newline at end of file From bcfb2e63d70c720983e46eecc859154e708b5d71 Mon Sep 17 00:00:00 2001 From: The PFC <81114960+PFC-developer@users.noreply.github.com> Date: Fri, 20 Jan 2023 12:48:10 -0600 Subject: [PATCH 28/77] fix: 2.1.16 -fix mint/unmint calculations where paused validators have stake --- Cargo.lock | 4 ++-- contracts/hub/Cargo.toml | 2 +- contracts/hub/src/execute.rs | 22 ++++++++++++++++++++-- contracts/hub/src/math.rs | 11 +++++++++-- contracts/hub/src/queries.rs | 18 ++++++++++++++---- contracts/hub/src/testing/tests.rs | 12 ++++++++---- packages/steak/Cargo.toml | 2 +- packages/steak/src/hub.rs | 3 ++- 8 files changed, 57 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 98e8ce6..8383ffc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -581,7 +581,7 @@ dependencies = [ [[package]] name = "pfc-steak" -version = "2.1.14" +version = "2.1.15" dependencies = [ "cosmwasm-std", "cw20 1.0.0", @@ -592,7 +592,7 @@ dependencies = [ [[package]] name = "pfc-steak-hub" -version = "2.1.15" +version = "2.1.16" dependencies = [ "cosmwasm-std", "cw-storage-plus 0.13.4", diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index c583389..c627d9e 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub" -version = "2.1.15" +version = "2.1.16" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index 7216544..c02d64f 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -1,3 +1,5 @@ +use std::collections::HashSet; +use std::iter::FromIterator; use std::str::FromStr; use cosmwasm_std::{ @@ -133,10 +135,18 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec) -> StdRes let steak_token = state.steak_token.load(deps.storage)?; let validators = state.validators_active.load(deps.storage)?; + let mut validators_wl:HashSet = HashSet::from_iter(state.validators.load(deps.storage)?); + for v in validators.iter() { + validators_wl.remove(v); + } + let non_active_validator_list = Vec::from_iter(validators_wl); + // Query the current delegations made to validators, and find the validator with the smallest // delegated amount through a linear search // The code for linear search is a bit uglier than using `sort_by` but cheaper: O(n) vs O(n * log(n)) + let delegations_non_active = query_delegations(&deps.querier, &non_active_validator_list, &env.contract.address, &denom)?; let delegations = query_delegations(&deps.querier, &validators, &env.contract.address, &denom)?; + let mut validator = &delegations[0].validator; let mut amount = delegations[0].amount; for d in &delegations[1..] { @@ -153,7 +163,7 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec) -> StdRes // Query the current supply of Steak and compute the amount to mint let usteak_supply = query_cw20_total_supply(&deps.querier, &steak_token)?; - let usteak_to_mint = compute_mint_amount(usteak_supply, amount_to_bond, &delegations); + let usteak_to_mint = compute_mint_amount(usteak_supply, amount_to_bond, &delegations, &delegations_non_active); state.prev_denom.save( deps.storage, &get_denom_balance(&deps.querier, env.contract.address.clone(), denom.clone())?, @@ -446,6 +456,7 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { let denom = state.denom.load(deps.storage)?; let steak_token = state.steak_token.load(deps.storage)?; let validators = state.validators.load(deps.storage)?; + let unbond_period = state.unbond_period.load(deps.storage)?; let pending_batch = state.pending_batch.load(deps.storage)?; @@ -456,12 +467,19 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { pending_batch.est_unbond_start_time ))); } + let mut validators_active:HashSet = HashSet::from_iter(state.validators_active.load(deps.storage)?); + for v in validators.iter() { + validators_active.remove(v); + } + let active_validator_list = Vec::from_iter(validators_active); + // for unbonding we still need to look at let delegations = query_delegations(&deps.querier, &validators, &env.contract.address, &denom)?; + let delegations_active = query_delegations(&deps.querier, &active_validator_list, &env.contract.address, &denom)?; let usteak_supply = query_cw20_total_supply(&deps.querier, &steak_token)?; let amount_to_bond = - compute_unbond_amount(usteak_supply, pending_batch.usteak_to_burn, &delegations); + compute_unbond_amount(usteak_supply, pending_batch.usteak_to_burn, &delegations, &delegations_active); let new_undelegations = compute_undelegations(amount_to_bond, &delegations, &denom); // NOTE: Regarding the `amount_unclaimed` value diff --git a/contracts/hub/src/math.rs b/contracts/hub/src/math.rs index d1c3602..4618e14 100644 --- a/contracts/hub/src/math.rs +++ b/contracts/hub/src/math.rs @@ -17,8 +17,11 @@ pub(crate) fn compute_mint_amount( usteak_supply: Uint128, native_to_bond: Uint128, current_delegations: &[Delegation], + inactive_delegations: &[Delegation], ) -> Uint128 { - let native_bonded: u128 = current_delegations.iter().map(|d| d.amount).sum(); + let native_bonded_c: u128 = current_delegations.iter().map(|d| d.amount).sum() ; + let native_bonded_inactive: u128 = inactive_delegations.iter().map(|d| d.amount).sum(); + let native_bonded = native_bonded_c+ native_bonded_inactive; if native_bonded == 0 { native_to_bond } else { @@ -34,8 +37,12 @@ pub(crate) fn compute_unbond_amount( usteak_supply: Uint128, usteak_to_burn: Uint128, current_delegations: &[Delegation], + active_delegations: &[Delegation], + ) -> Uint128 { - let native_bonded: u128 = current_delegations.iter().map(|d| d.amount).sum(); + let native_bonded_c: u128 = current_delegations.iter().map(|d| d.amount).sum() ; + let native_bonded_a: u128 = active_delegations.iter().map(|d| d.amount).sum() ; + let native_bonded = native_bonded_c+ native_bonded_a; Uint128::new(native_bonded).multiply_ratio(usteak_to_burn, usteak_supply) } diff --git a/contracts/hub/src/queries.rs b/contracts/hub/src/queries.rs index 2e7eefb..bf265d6 100644 --- a/contracts/hub/src/queries.rs +++ b/contracts/hub/src/queries.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use std::collections::{BTreeSet, HashSet}; use std::iter::FromIterator; use cosmwasm_std::{Addr, Decimal, Deps, Env, Order, StdResult, Uint128}; @@ -17,6 +17,15 @@ const DEFAULT_LIMIT: u32 = 10; pub fn config(deps: Deps) -> StdResult { let state = State::default(); + let mut validators: BTreeSet = BTreeSet::from_iter(state.validators.load(deps.storage)?); + let validators_active: BTreeSet = BTreeSet::from_iter(state.validators_active.load(deps.storage)?); + + for v in validators_active.iter() { + validators.remove(v); + } + let validator_active_vec: Vec = Vec::from_iter(validators_active.into_iter()); + let paused_validators: Vec = Vec::from_iter(validators.into_iter()); + Ok(ConfigResponse { owner: state.owner.load(deps.storage)?.into(), new_owner: state @@ -31,7 +40,8 @@ pub fn config(deps: Deps) -> StdResult { fee_account: state.fee_account.load(deps.storage)?.to_string(), fee_rate: state.fee_rate.load(deps.storage)?, max_fee_rate: state.max_fee_rate.load(deps.storage)?, - validators: state.validators.load(deps.storage)?, + validators: validator_active_vec, + paused_validators, }) } @@ -42,8 +52,8 @@ pub fn state(deps: Deps, env: Env) -> StdResult { let steak_token = state.steak_token.load(deps.storage)?; let total_usteak = query_cw20_total_supply(&deps.querier, &steak_token)?; - let mut validators:HashSet = HashSet::from_iter(state.validators.load(deps.storage)?); - let validators_active:HashSet = HashSet::from_iter(state.validators_active.load(deps.storage)?); + let mut validators: HashSet = HashSet::from_iter(state.validators.load(deps.storage)?); + let validators_active: HashSet = HashSet::from_iter(state.validators_active.load(deps.storage)?); validators.extend(validators_active); let validator_vec: Vec = Vec::from_iter(validators.into_iter()); diff --git a/contracts/hub/src/testing/tests.rs b/contracts/hub/src/testing/tests.rs index 58c0754..864b998 100644 --- a/contracts/hub/src/testing/tests.rs +++ b/contracts/hub/src/testing/tests.rs @@ -217,7 +217,8 @@ fn proper_instantiation() { "alice".to_string(), "bob".to_string(), "charlie".to_string() - ] + ], + paused_validators: vec![] } ); @@ -261,7 +262,8 @@ fn proper_instantiation() { "alice".to_string(), "bob".to_string(), "charlie".to_string() - ] + ], + paused_validators: vec![] } ); } @@ -1558,7 +1560,8 @@ fn splitting_fees() { "alice".to_string(), "bob".to_string(), "charlie".to_string() - ] + ], + paused_validators: vec![] } ); @@ -1591,7 +1594,8 @@ fn splitting_fees() { "alice".to_string(), "bob".to_string(), "charlie".to_string() - ] + ], + paused_validators: vec![] } ); } diff --git a/packages/steak/Cargo.toml b/packages/steak/Cargo.toml index 291df33..47a40d2 100644 --- a/packages/steak/Cargo.toml +++ b/packages/steak/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak" -version = "2.1.14" +version = "2.1.15" authors = ["larry ", "PFC "] edition = "2018" description = "Liquid steaking protocol for the cosmos" diff --git a/packages/steak/src/hub.rs b/packages/steak/src/hub.rs index 1649cd5..9b4ff9a 100644 --- a/packages/steak/src/hub.rs +++ b/packages/steak/src/hub.rs @@ -167,8 +167,9 @@ pub struct ConfigResponse { pub fee_rate: Decimal, /// Max Fee "1.00 = 100%" pub max_fee_rate: Decimal, - /// Initial set of validators who will receive the delegations + /// Set of validators who will receive the delegations pub validators: Vec, + pub paused_validators: Vec, } #[derive(Serialize, Deserialize, Clone, Debug,Eq, PartialEq, JsonSchema)] From 34860bdd75a06c156537276fcfbb4b3a4820eef3 Mon Sep 17 00:00:00 2001 From: The PFC <81114960+PFC-developer@users.noreply.github.com> Date: Sat, 4 Feb 2023 11:24:22 -0600 Subject: [PATCH 29/77] fix: 2.1.17 - underflow issues, thank-you to @eris/ampLuna for the notification --- contracts/hub/Cargo.toml | 2 +- contracts/hub/src/math.rs | 40 ++++++-- contracts/hub/src/testing/tests.rs | 141 +++++++++++++++++++++++++++++ init/elgafar_hub_init.json | 8 +- 4 files changed, 176 insertions(+), 15 deletions(-) diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index c627d9e..4de64f2 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub" -version = "2.1.16" +version = "2.1.17" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub/src/math.rs b/contracts/hub/src/math.rs index 4618e14..01d140e 100644 --- a/contracts/hub/src/math.rs +++ b/contracts/hub/src/math.rs @@ -19,9 +19,9 @@ pub(crate) fn compute_mint_amount( current_delegations: &[Delegation], inactive_delegations: &[Delegation], ) -> Uint128 { - let native_bonded_c: u128 = current_delegations.iter().map(|d| d.amount).sum() ; - let native_bonded_inactive: u128 = inactive_delegations.iter().map(|d| d.amount).sum(); - let native_bonded = native_bonded_c+ native_bonded_inactive; + let native_bonded_c: u128 = current_delegations.iter().map(|d| d.amount).sum(); + let native_bonded_inactive: u128 = inactive_delegations.iter().map(|d| d.amount).sum(); + let native_bonded = native_bonded_c + native_bonded_inactive; if native_bonded == 0 { native_to_bond } else { @@ -38,11 +38,10 @@ pub(crate) fn compute_unbond_amount( usteak_to_burn: Uint128, current_delegations: &[Delegation], active_delegations: &[Delegation], - ) -> Uint128 { - let native_bonded_c: u128 = current_delegations.iter().map(|d| d.amount).sum() ; - let native_bonded_a: u128 = active_delegations.iter().map(|d| d.amount).sum() ; - let native_bonded = native_bonded_c+ native_bonded_a; + let native_bonded_c: u128 = current_delegations.iter().map(|d| d.amount).sum(); + let native_bonded_a: u128 = active_delegations.iter().map(|d| d.amount).sum(); + let native_bonded = native_bonded_c + native_bonded_a; Uint128::new(native_bonded).multiply_ratio(usteak_to_burn, usteak_supply) } @@ -237,12 +236,33 @@ pub(crate) fn reconcile_batches(batches: &mut [Batch], native_to_deduct: Uint128 let batch_count = batches.len() as u128; let native_per_batch = native_to_deduct.u128() / batch_count; let remainder = native_to_deduct.u128() % batch_count; - + let mut remaining_underflow = Uint128::zero(); + // distribute the underflows uniformly accross non-underflowing batches for (i, batch) in batches.iter_mut().enumerate() { let remainder_for_batch: u128 = u128::from((i + 1) as u128 <= remainder); - let native_for_batch = native_per_batch + remainder_for_batch; + let native_for_batch = Uint128::new(native_per_batch + remainder_for_batch); - batch.amount_unclaimed -= Uint128::new(native_for_batch); + if batch.amount_unclaimed < native_for_batch && batch_count > 1 { + remaining_underflow += native_for_batch - batch.amount_unclaimed; + } + batch.amount_unclaimed.saturating_sub(native_for_batch); batch.reconciled = true; } + if !remaining_underflow.is_zero() { + // the remaining underflow will be applied by oldest batch first. + for (_, batch) in batches.iter_mut().enumerate() { + if !batch.amount_unclaimed.is_zero() && !remaining_underflow.is_zero() { + if batch.amount_unclaimed >= remaining_underflow { + batch.amount_unclaimed -= remaining_underflow; + remaining_underflow = Uint128::zero() + } else { + remaining_underflow -= batch.amount_unclaimed; + batch.amount_unclaimed = Uint128::zero(); + } + } + } + } + if !remaining_underflow.is_zero() { + // no way to reconcile right now, need to top up some funds. + } } diff --git a/contracts/hub/src/testing/tests.rs b/contracts/hub/src/testing/tests.rs index 864b998..94fc901 100644 --- a/contracts/hub/src/testing/tests.rs +++ b/contracts/hub/src/testing/tests.rs @@ -2044,3 +2044,144 @@ fn receiving_funds() { let amount = parse_received_fund(&[Coin::new(69420, "uxyz")], "uxyz").unwrap(); assert_eq!(amount, Uint128::new(69420)); } + + +#[test] +fn reconciling_underflow() { + let mut deps = setup_test(); + let state = State::default(); + let previous_batches = vec![ + Batch { + id: 1, + reconciled: true, + total_shares: Uint128::new(92876), + amount_unclaimed: Uint128::new(95197), // 1.025 Token per Stake + est_unbond_end_time: 10000, + }, + Batch { + id: 2, + reconciled: false, + total_shares: Uint128::new(1345), + amount_unclaimed: Uint128::new(1385), // 1.030 Token per Stake + est_unbond_end_time: 20000, + }, + Batch { + id: 3, + reconciled: false, + total_shares: Uint128::new(1456), + amount_unclaimed: Uint128::new(1506), // 1.035 Token per Stake + est_unbond_end_time: 30000, + }, + Batch { + id: 4, + reconciled: false, + total_shares: Uint128::new(1), + amount_unclaimed: Uint128::new(1), + est_unbond_end_time: 30001, + }, + ]; + for previous_batch in &previous_batches { + state + .previous_batches + .save(deps.as_mut().storage, previous_batch.id, previous_batch) + .unwrap(); + } + state + .unlocked_coins + .save( + deps.as_mut().storage, + &vec![ + Coin::new(10000, "uatom"), + Coin::new(234, "ukrw"), + Coin::new(345, "uusd"), + Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + ), + ], + ) + .unwrap(); + deps.querier.set_bank_balances(&[ + Coin::new(12345, "uatom"), + Coin::new(234, "ukrw"), + Coin::new(345, "uusd"), + Coin::new(69420, "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B"), + ]); + execute( + deps.as_mut(), + mock_env_at_timestamp(35000), + mock_info("worker", &[]), + ExecuteMsg::Reconcile {}, + ) + .unwrap(); +} + +#[test] +fn reconciling_underflow_second() { + let mut deps = setup_test(); + let state = State::default(); + let previous_batches = vec![ + Batch { + id: 1, + reconciled: true, + total_shares: Uint128::new(92876), + amount_unclaimed: Uint128::new(95197), // 1.025 Token per Stake + est_unbond_end_time: 10000, + }, + Batch { + id: 2, + reconciled: false, + total_shares: Uint128::new(1345), + amount_unclaimed: Uint128::new(1385), // 1.030 Token per Stake + est_unbond_end_time: 20000, + }, + Batch { + id: 3, + reconciled: false, + total_shares: Uint128::new(176), + amount_unclaimed: Uint128::new(183), // 1.035 Token per Stake + est_unbond_end_time: 30000, + }, + Batch { + id: 4, + reconciled: false, + total_shares: Uint128::new(1), + amount_unclaimed: Uint128::new(1), + est_unbond_end_time: 30001, + }, + ]; + for previous_batch in &previous_batches { + state + .previous_batches + .save(deps.as_mut().storage, previous_batch.id, previous_batch) + .unwrap(); + } + state + .unlocked_coins + .save( + deps.as_mut().storage, + &vec![ + Coin::new(10000, "uatom"), + Coin::new(234, "ukrw"), + Coin::new(345, "uusd"), + Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + ), + ], + ) + .unwrap(); + deps.querier.set_bank_balances(&[ + Coin::new(12345 - 1323, "uatom"), + Coin::new(234, "ukrw"), + Coin::new(345, "uusd"), + Coin::new(69420, "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B"), + ]); + execute( + deps.as_mut(), + mock_env_at_timestamp(35000), + mock_info("worker", &[]), + ExecuteMsg::Reconcile {}, + ) + .unwrap(); +} \ No newline at end of file diff --git a/init/elgafar_hub_init.json b/init/elgafar_hub_init.json index 8e970ac..bcca70b 100644 --- a/init/elgafar_hub_init.json +++ b/init/elgafar_hub_init.json @@ -1,8 +1,8 @@ { "cw20_code_id": 801, "owner": "stars1kdtdg0lvy8asxn8clnjfpusuvf93zuknxrad8g", - "name": "bStars Token", - "symbol": "bStars", + "name": "bSTARS Token", + "symbol": "bSTARS", "decimals": 6, "epoch_period": 259200, "unbond_period": 1209600, @@ -16,10 +16,10 @@ "fee_amount": "0.10", "max_fee_amount": "0.10", "fee_account_type": "FeeSplit", - "label": "boneStars", + "label": "boneSTARS", "marketing": { "project": "https://backbonelabs.io", - "description": "The GraveDigger is the Liquid Staking Product of BackBone Labs. It's liquid staking derivative (LSD) is boneStars.", + "description": "The GraveDigger is the Liquid Staking Product of BackBone Labs. It's liquid staking derivative (LSD) is boneSTARS.", "marketing": "stars1kdtdg0lvy8asxn8clnjfpusuvf93zuknxrad8g" } } \ No newline at end of file From a82edf4abc3014e030f09b78e164bca3240f0899 Mon Sep 17 00:00:00 2001 From: The PFC <81114960+PFC-developer@users.noreply.github.com> Date: Thu, 23 Feb 2023 21:21:41 -0600 Subject: [PATCH 30/77] fix: 2.1.17a - correct saturating sub line. --- contracts/hub/src/math.rs | 55 ++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/contracts/hub/src/math.rs b/contracts/hub/src/math.rs index 01d140e..d3517fe 100644 --- a/contracts/hub/src/math.rs +++ b/contracts/hub/src/math.rs @@ -1,4 +1,5 @@ use std::{cmp, cmp::Ordering}; +use std::collections::HashMap; use cosmwasm_std::Uint128; @@ -236,33 +237,57 @@ pub(crate) fn reconcile_batches(batches: &mut [Batch], native_to_deduct: Uint128 let batch_count = batches.len() as u128; let native_per_batch = native_to_deduct.u128() / batch_count; let remainder = native_to_deduct.u128() % batch_count; - let mut remaining_underflow = Uint128::zero(); + //let mut remaining_underflow = Uint128::zero(); + let mut underflows: HashMap = HashMap::default(); + // distribute the underflows uniformly accross non-underflowing batches for (i, batch) in batches.iter_mut().enumerate() { let remainder_for_batch: u128 = u128::from((i + 1) as u128 <= remainder); let native_for_batch = Uint128::new(native_per_batch + remainder_for_batch); if batch.amount_unclaimed < native_for_batch && batch_count > 1 { - remaining_underflow += native_for_batch - batch.amount_unclaimed; + // remaining_underflow += native_for_batch - batch.amount_unclaimed; + underflows.insert(i, native_for_batch - batch.amount_unclaimed); } - batch.amount_unclaimed.saturating_sub(native_for_batch); + batch.amount_unclaimed = batch.amount_unclaimed.saturating_sub(native_for_batch); + batch.reconciled = true; } - if !remaining_underflow.is_zero() { + if !underflows.is_empty() { + let batch_count: u128 = batch_count - (underflows.len() as u128); + let to_deduct: Uint128 = underflows.iter().map(|v| v.1).sum(); + let native_per_batch = to_deduct.u128() / batch_count; + let remainder = to_deduct.u128() % batch_count; + let mut remaining_underflow = Uint128::zero(); // the remaining underflow will be applied by oldest batch first. - for (_, batch) in batches.iter_mut().enumerate() { - if !batch.amount_unclaimed.is_zero() && !remaining_underflow.is_zero() { - if batch.amount_unclaimed >= remaining_underflow { - batch.amount_unclaimed -= remaining_underflow; - remaining_underflow = Uint128::zero() - } else { - remaining_underflow -= batch.amount_unclaimed; - batch.amount_unclaimed = Uint128::zero(); + for (i, batch) in batches.iter_mut().enumerate() { + if !batch.amount_unclaimed.is_zero() { + let remainder_for_batch: u128 = u128::from((i + 1) as u128 <= remainder); + let native_for_batch = Uint128::new(native_per_batch + remainder_for_batch); + if batch.amount_unclaimed < native_for_batch && batch_count > 1 { + remaining_underflow += native_for_batch - batch.amount_unclaimed; } + batch.amount_unclaimed = batch.amount_unclaimed.saturating_sub(native_for_batch); + } + } + + if !remaining_underflow.is_zero() { + // the remaining underflow will be applied by oldest batch first. + for (_, batch) in batches.iter_mut().enumerate() { + if !batch.amount_unclaimed.is_zero() && !remaining_underflow.is_zero() { + if batch.amount_unclaimed >= remaining_underflow { + batch.amount_unclaimed -= remaining_underflow; + remaining_underflow = Uint128::zero() + } else { + remaining_underflow -= batch.amount_unclaimed; + batch.amount_unclaimed = Uint128::zero(); + } + } + } + + if !remaining_underflow.is_zero() { + // no way to reconcile right now, need to top up some funds. } } - } - if !remaining_underflow.is_zero() { - // no way to reconcile right now, need to top up some funds. } } From 6c73082e434e6ecab54f0f2251cb083cadc579f0 Mon Sep 17 00:00:00 2001 From: The PFC <81114960+PFC-developer@users.noreply.github.com> Date: Fri, 24 Feb 2023 12:45:25 -0600 Subject: [PATCH 31/77] feat: tf --- Cargo.lock | 280 ++- contracts/hub-tf/Cargo.toml | 33 + contracts/hub-tf/README.md | 45 + contracts/hub-tf/src/contract.rs | 208 ++ contracts/hub-tf/src/execute.rs | 1055 +++++++++ contracts/hub-tf/src/helpers.rs | 113 + contracts/hub-tf/src/lib.rs | 13 + contracts/hub-tf/src/math.rs | 293 +++ contracts/hub-tf/src/migrations.rs | 1 + contracts/hub-tf/src/queries.rs | 184 ++ contracts/hub-tf/src/state.rs | 155 ++ .../hub-tf/src/testing/custom_querier.rs | 69 + contracts/hub-tf/src/testing/helpers.rs | 48 + contracts/hub-tf/src/testing/mod.rs | 4 + contracts/hub-tf/src/testing/tests.rs | 2018 +++++++++++++++++ contracts/hub-tf/src/token_factory/denom.rs | 142 ++ contracts/hub-tf/src/token_factory/mod.rs | 2 + contracts/hub-tf/src/types/coins.rs | 55 + contracts/hub-tf/src/types/keys.rs | 45 + contracts/hub-tf/src/types/mod.rs | 7 + contracts/hub-tf/src/types/staking.rs | 77 + contracts/hub-tf/unbonding-queue.png | Bin 0 -> 98283 bytes contracts/hub/Cargo.toml | 3 +- contracts/hub/src/contract.rs | 9 +- contracts/hub/src/execute.rs | 42 + contracts/hub/src/queries.rs | 1 + contracts/hub/src/state.rs | 5 +- contracts/hub/src/testing/tests.rs | 248 +- init/migaloo_hub_init.json | 25 + init/narwhal_hub_init.json | 25 + packages/dust_collector/Cargo.toml | 17 + packages/steak/Cargo.toml | 5 +- packages/steak/src/hub.rs | 39 +- packages/steak/src/hub_tf.rs | 89 + packages/steak/src/lib.rs | 1 + 35 files changed, 5184 insertions(+), 172 deletions(-) create mode 100644 contracts/hub-tf/Cargo.toml create mode 100644 contracts/hub-tf/README.md create mode 100644 contracts/hub-tf/src/contract.rs create mode 100644 contracts/hub-tf/src/execute.rs create mode 100644 contracts/hub-tf/src/helpers.rs create mode 100644 contracts/hub-tf/src/lib.rs create mode 100644 contracts/hub-tf/src/math.rs create mode 100644 contracts/hub-tf/src/migrations.rs create mode 100644 contracts/hub-tf/src/queries.rs create mode 100644 contracts/hub-tf/src/state.rs create mode 100644 contracts/hub-tf/src/testing/custom_querier.rs create mode 100644 contracts/hub-tf/src/testing/helpers.rs create mode 100644 contracts/hub-tf/src/testing/mod.rs create mode 100644 contracts/hub-tf/src/testing/tests.rs create mode 100644 contracts/hub-tf/src/token_factory/denom.rs create mode 100644 contracts/hub-tf/src/token_factory/mod.rs create mode 100644 contracts/hub-tf/src/types/coins.rs create mode 100644 contracts/hub-tf/src/types/keys.rs create mode 100644 contracts/hub-tf/src/types/mod.rs create mode 100644 contracts/hub-tf/src/types/staking.rs create mode 100644 contracts/hub-tf/unbonding-queue.png create mode 100644 init/migaloo_hub_init.json create mode 100644 init/narwhal_hub_init.json create mode 100644 packages/dust_collector/Cargo.toml create mode 100644 packages/steak/src/hub_tf.rs diff --git a/Cargo.lock b/Cargo.lock index 8383ffc..f9708a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "anyhow" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" + [[package]] name = "base16ct" version = "0.1.1" @@ -44,6 +50,12 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + [[package]] name = "cfg-if" version = "1.0.0" @@ -58,9 +70,9 @@ checksum = "722e23542a15cea1f65d4a1419c4cfd7a26706c70871a13a04238ca3f40f1661" [[package]] name = "cosmwasm-crypto" -version = "1.1.9" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "227315dc11f0bb22a273d0c43d3ba8ef52041c42cf959f09045388a89c57e661" +checksum = "7fecd74d3a0041114110d1260f77fcb644c5d2403549b37096c44f0e643a5177" dependencies = [ "digest 0.10.5", "ed25519-zebra", @@ -71,18 +83,18 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.1.9" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fca30d51f7e5fbfa6440d8b10d7df0231bdf77e97fd3fe5d0cb79cc4822e50c" +checksum = "d5abeeb891e6d0098402e4d3d042f90451db52651d2fe14b170e69a1dd3e4115" dependencies = [ "syn", ] [[package]] name = "cosmwasm-schema" -version = "1.1.5" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a227cfeb9a7152b26a354b1c990e930e962f75fd68f57ab5ae2ef888c8524292" +checksum = "9118e36843df6648fd0a626c46438f87038f296ec750cef3832cafc483c483f9" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -93,9 +105,9 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.1.5" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3626cb42eef870de67f791e873711255325224d86f281bf628c42abd295f3a14" +checksum = "78d6fc9854ac14e46cb69b0f396547893f93d2847aef975950ebbe73342324f3" dependencies = [ "proc-macro2", "quote", @@ -104,9 +116,9 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.1.9" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b13d5a84d15cf7be17dc249a21588cdb0f7ef308907c50ce2723316a7d79c3dc" +checksum = "5034c772c1369b160731aa00bb81f93733ab2884928edd8d588733d607ac5af4" dependencies = [ "base64", "cosmwasm-crypto", @@ -117,6 +129,7 @@ dependencies = [ "schemars", "serde", "serde-json-wasm", + "sha2 0.10.6", "thiserror", "uint", ] @@ -171,6 +184,51 @@ dependencies = [ "zeroize", ] +[[package]] +name = "cw-address-like" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b137a72f7ed4eaf591ec1bba7aedb062cde38ff08086276d8de6e37dc9b98621" +dependencies = [ + "cosmwasm-std", +] + +[[package]] +name = "cw-item-set" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2fe22a9086c61c6b41ee46669c6f61ca4142111d38551ca156be4fa9d9fa39d" +dependencies = [ + "cosmwasm-std", + "cw-storage-plus 1.0.1", +] + +[[package]] +name = "cw-ownable" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27fc0c75fc61cf268e5dc5f23ff24bd84b629c575b5a1522e2349f61b46874b4" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-address-like", + "cw-ownable-derive", + "cw-storage-plus 1.0.1", + "cw-utils 1.0.1", + "thiserror", +] + +[[package]] +name = "cw-ownable-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac447d5b5314ee18c2663b5436451c030c072db1eb392a167d19c04e247d77f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "cw-storage-plus" version = "0.13.4" @@ -204,6 +262,17 @@ dependencies = [ "serde", ] +[[package]] +name = "cw-storage-plus" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053a5083c258acd68386734f428a5a171b29f7d733151ae83090c6fcc9417ffa" +dependencies = [ + "cosmwasm-std", + "schemars", + "serde", +] + [[package]] name = "cw-utils" version = "0.13.4" @@ -246,6 +315,21 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cw-utils" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c80e93d1deccb8588db03945016a292c3c631e6325d349ebb35d2db6f4f946f7" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw2 1.0.1", + "schemars", + "semver", + "serde", + "thiserror", +] + [[package]] name = "cw2" version = "0.13.4" @@ -286,13 +370,13 @@ dependencies = [ [[package]] name = "cw2" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03bdf3747540b47bc1bdaf50ba3aa5e4276ab0c2ce73e8b367ebe260cc37ff9c" +checksum = "8fb70cee2cf0b4a8ff7253e6bc6647107905e8eb37208f87d54f67810faa62f8" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw-storage-plus 0.16.0", + "cw-storage-plus 1.0.1", "schemars", "serde", ] @@ -361,7 +445,7 @@ dependencies = [ "cosmwasm-std", "cw-storage-plus 0.16.0", "cw-utils 0.16.0", - "cw2 1.0.0", + "cw2 1.0.1", "cw20 1.0.0", "schemars", "semver", @@ -443,6 +527,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + [[package]] name = "elliptic-curve" version = "0.12.3" @@ -537,6 +627,15 @@ dependencies = [ "digest 0.10.5", ] +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.1" @@ -555,18 +654,62 @@ dependencies = [ "sha2 0.10.6", ] +[[package]] +name = "kujira" +version = "0.7.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b69b49819362f0ab701d4c322e86a0b2735b6129ca857b3b982b2a6ec5431b22" +dependencies = [ + "anyhow", + "cosmwasm-std", + "cw20 1.0.0", + "hex", + "schemars", + "serde", + "sha2 0.10.6", + "thiserror", +] + [[package]] name = "libc" version = "0.2.124" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21a41fed9d98f27ab1c6d161da622a4fa35e8a54a8adc24bbf3ddd0ef70b0e50" +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + [[package]] name = "opaque-debug" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "osmosis-std-derive" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a455e262a6fdfd3914f3a4e11e6bc0ce491901cb9d507d7856d7ef6e129e90c6" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pfc-dust-collector" +version = "3.0.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "schemars", + "serde", +] + [[package]] name = "pfc-fee-split" version = "0.2.3" @@ -581,8 +724,9 @@ dependencies = [ [[package]] name = "pfc-steak" -version = "2.1.15" +version = "2.2.2" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw20 1.0.0", "cw20-base 1.0.0", @@ -592,15 +736,37 @@ dependencies = [ [[package]] name = "pfc-steak-hub" -version = "2.1.16" +version = "3.0.1" dependencies = [ "cosmwasm-std", "cw-storage-plus 0.13.4", - "cw2 1.0.0", + "cw2 1.0.1", "cw20 1.0.0", "cw20-base 1.0.0", + "pfc-dust-collector", + "pfc-fee-split", + "pfc-steak", + "serde", +] + +[[package]] +name = "pfc-steak-hub-tf" +version = "3.0.1" +dependencies = [ + "cosmwasm-std", + "cw-item-set", + "cw-ownable", + "cw-storage-plus 1.0.1", + "cw2 1.0.1", + "kujira", + "osmosis-std-derive", + "pfc-dust-collector", "pfc-fee-split", "pfc-steak", + "prost", + "prost-types", + "protobuf", + "schemars", "serde", ] @@ -625,18 +791,72 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.37" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" +checksum = "21dc42e00223fc37204bd4aa177e69420c604ca4a183209a8f9de30c6d934698" dependencies = [ - "unicode-xid", + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda8c0881ea9f722eb9629376db3d0b903b462477c1aafcb0566610ac28ac5d" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e0526209433e96d83d750dd81a99118edbc55739e7e61a46764fd2ad537788" +dependencies = [ + "bytes", + "prost", +] + +[[package]] +name = "protobuf" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55bad9126f378a853655831eb7363b7b01b81d19f8cb1218861086ca4a1a61e" +dependencies = [ + "bytes", + "once_cell", + "protobuf-support", + "thiserror", +] + +[[package]] +name = "protobuf-support" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d4d7b8601c814cfb36bcebb79f0e61e45e1e93640cf778837833bbed05c372" +dependencies = [ + "thiserror", ] [[package]] name = "quote" -version = "1.0.18" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" dependencies = [ "proc-macro2", ] @@ -731,9 +951,9 @@ dependencies = [ [[package]] name = "serde-json-wasm" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479b4dbc401ca13ee8ce902851b834893251404c4f3c65370a49e047a6be09a5" +checksum = "a15bee9b04dd165c3f4e142628982ddde884c2022a89e8ddf99c4829bf2c3a58" dependencies = [ "serde", ] @@ -829,13 +1049,13 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.91" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] @@ -877,10 +1097,10 @@ dependencies = [ ] [[package]] -name = "unicode-xid" -version = "0.2.2" +name = "unicode-ident" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "version_check" diff --git a/contracts/hub-tf/Cargo.toml b/contracts/hub-tf/Cargo.toml new file mode 100644 index 0000000..a9a140c --- /dev/null +++ b/contracts/hub-tf/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "pfc-steak-hub-tf" +version = "3.0.1" +authors = ["larry ", "PFC "] +edition = "2018" +license = "GPL-3.0-or-later" +repository = "https://github.com/pfc-developer/steak-contracts" + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +backtraces = ["cosmwasm-std/backtraces"] + +[dependencies] +cosmwasm-std = { version = "1.2.1", features=["iterator","stargate","cosmwasm_1_1","staking"]} + +cw2= "1.0.1" +#cw20 = "1.0.0" +#cw20-base = { version = "1.0.0", features = ["library"] } +cw-storage-plus = "1.0.1" +cw-ownable = "0.5.0" +cw-item-set = "0.7.0" +prost = {version = "0.11.0", default-features = false, features = ["prost-derive"]} +prost-types = {version = "0.11.1", default-features = false} +schemars = "0.8.11" +pfc-steak = { path = "../../packages/steak" } +pfc-dust-collector = { path = "../../packages/dust_collector" } +serde = { version = "1.0.103", default-features = false, features = ["derive"] } +pfc-fee-split = "0.2.3" +kujira = "0.7.25" +osmosis-std-derive="0.13.2" +protobuf = { version = "3.1.0", features = ["with-bytes"] } \ No newline at end of file diff --git a/contracts/hub-tf/README.md b/contracts/hub-tf/README.md new file mode 100644 index 0000000..babc90b --- /dev/null +++ b/contracts/hub-tf/README.md @@ -0,0 +1,45 @@ +# Steak Hub + +Steak Hub contract manages the bonding/unbonding of Luna, minting/burning of Steak, and reinvestment of staking rewards. + +## Overview + +### Exchange rate + +Unlike [Lido's stETH](https://github.com/lidofinance/lido-dao/tree/master/contracts/0.4.24), the Steak token does not rebase; instead, the exchange rate between Luna and Steak increases (i.e. each Steak becomes worth more Luna) as staking rewards are harvested, and reduces if validators are slashed. + +The exchange rate, as defined by the amount of `uluna` redeemable per `usteak`, is calculated as + +```plain +exchange_rate = total_uluna_staked / total_usteak_supply +``` + +### Unlocked coins + +Unlocked coin refers to coins held by the Steak Hub contract (referred to as "the contract" hereafter) that can be reinvested. The contract tracks the amounts of unlocked coins using a `Vec` variable stored under the `unlocked_coins` key. + +Each time the Hub contract delegates to or undelegates from a validator, the claimable staking rewards are automatically transferred to the contract. The amounts of coins transferred are recorded in the `coin_received` event. When handling the response, the contract parses this event and updates the `unlocked_coins` variable accordingly. + +When harvesting, the contract needs to swap Terra stablecoins into Luna. the contract offers all unlocked coins that have exchange rates defined against Luna to be swapped, and deduct them from `unlocked_coins` accordingly. When handling the response, the contract parses the `swap` event and increments the unlocked Luna amount. + +### Unbonding + +Cosmos chains, by default, has a limit of 7 undelegations at a time per validator-delegator pair. In order to support unbonding requests from many users, the contract needs to bundle unbonding requests together and submit them in batches. + +![illustration-of-unbonding-queue](./unbonding-queue.png) + +For mainnet, the contract will submit a batch every 3 days, such that there are at most 7 undelegations at a time with each validator. This 3 day interval is defined by the `epoch_period` parameter. + +During the 3 day period, the contract accepts unbonding requests from users and store them in an `IndexedMap` data structure under the `unbond_requests` key, and the aggregated properties of the pending batch under the `pending_batch` key. Each user's share in the batch is proportional to the amount of Steak tokens the user requests to burn. + +At the end of the 3 day period, anyone can invoke the `ExecuteMsg::SubmitUnbond` function to submit the pending batch to be unbonded. The contract calculates the amount of Luna to unbond based on the Luna/Steak exchange rate at the time, burns the Steak tokens, and initiates undelegations with the validators. + +At the end of the following 21 day unbonding period, the user can invoke the `ExecuteMsg::WithdrawUnbonded` function. The contract pulls all of the user's unclaimed unbonding requests, and refunds appropriate amounts of Luna based on the each request's share in that batch, to the user. + +## Reference + +Similar projects: + +* [Lido - stLUNA](https://github.com/lidofinance/lido-terra-contracts) +* [Stader - LunaX](https://github.com/stader-labs/stader-liquid-token) +* [Staking derivatives (dSCRT)](https://github.com/Cashmaney/SecretStaking) \ No newline at end of file diff --git a/contracts/hub-tf/src/contract.rs b/contracts/hub-tf/src/contract.rs new file mode 100644 index 0000000..5af1d78 --- /dev/null +++ b/contracts/hub-tf/src/contract.rs @@ -0,0 +1,208 @@ +use cosmwasm_std::{ + entry_point, to_binary, Binary, Deps, DepsMut, Env, MessageInfo, + Response, StdError, StdResult, +}; + +use pfc_steak::hub::{CallbackMsg, MigrateMsg, QueryMsg}; + +//use crate::helpers::{ unwrap_reply}; + +use crate::{execute, queries}; +use cw2::{get_contract_version, set_contract_version, ContractVersion}; +use pfc_steak::hub_tf::{ExecuteMsg, InstantiateMsg}; + +/// Contract name that is used for migration. +pub const CONTRACT_NAME: &str = "steak-hub-tf"; +/// Contract version that is used for migration. +pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); +pub const REPLY_INSTANTIATE_TOKEN: u64 = 1; +pub const REPLY_REGISTER_RECEIVED_COINS: u64 = 2; + +#[entry_point] +pub fn instantiate( + deps: DepsMut, + env: Env, + _info: MessageInfo, + msg: InstantiateMsg, +) -> Result { + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + execute::instantiate(deps, env, msg) +} + +#[entry_point] +pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> StdResult { + let api = deps.api; + match msg { + + ExecuteMsg::Bond { receiver,exec_msg } => execute::bond( + deps, + env, + receiver + .map(|s| api.addr_validate(&s)) + .transpose()? + .unwrap_or(info.sender), + info.funds, + exec_msg + ), ExecuteMsg::Unbond { receiver} => execute::queue_unbond( + deps, + env, + receiver + .map(|s| api.addr_validate(&s)) + .transpose()? + .unwrap_or(info.sender),info.funds + + ), + ExecuteMsg::WithdrawUnbonded { receiver } => execute::withdraw_unbonded( + deps, + env, + info.sender.clone(), + receiver + .map(|s| api.addr_validate(&s)) + .transpose()? + .unwrap_or(info.sender), + ), + ExecuteMsg::WithdrawUnbondedAdmin { address } => { + execute::withdraw_unbonded_admin(deps, env, info.sender, api.addr_validate(&address)?) + } + ExecuteMsg::AddValidator { validator } => { + execute::add_validator(deps, info.sender, validator) + } + ExecuteMsg::RemoveValidator { validator } => { + execute::remove_validator(deps, env, info.sender, validator) + } + ExecuteMsg::RemoveValidatorEx { validator } => { + execute::remove_validator_ex(deps, env, info.sender, validator) + } + + ExecuteMsg::TransferOwnership { new_owner } => { + execute::transfer_ownership(deps, info.sender, new_owner) + } + ExecuteMsg::AcceptOwnership {} => execute::accept_ownership(deps, info.sender), + ExecuteMsg::Harvest {} => execute::harvest(deps, env), + ExecuteMsg::Rebalance { minimum } => execute::rebalance(deps, env, minimum), + ExecuteMsg::Reconcile {} => execute::reconcile(deps, env), + ExecuteMsg::SubmitBatch {} => execute::submit_batch(deps, env), + ExecuteMsg::TransferFeeAccount { fee_account_type,new_fee_account } => { + execute::transfer_fee_account(deps, info.sender, fee_account_type, new_fee_account) + } + ExecuteMsg::UpdateFee { new_fee } => execute::update_fee(deps, info.sender, new_fee), + ExecuteMsg::Callback(callback_msg) => callback(deps, env, info, callback_msg), + ExecuteMsg::PauseValidator { validator } => { + execute::pause_validator(deps, env, info.sender, validator) + } + ExecuteMsg::UnPauseValidator { validator } => { + execute::unpause_validator(deps, env, info.sender, validator) + } + ExecuteMsg::SetUnbondPeriod { unbond_period } => execute::set_unbond_period(deps, env, info.sender, unbond_period), + + ExecuteMsg::SetDustCollector { dust_collector } => {execute::set_dust_collector(deps,env,info.sender,dust_collector)} + ExecuteMsg::CollectDust { } => { execute::collect_dust(deps,env)} + ExecuteMsg::ReturnDenom { } => {execute::return_denom(deps,env,info.funds)} + } +} + + +fn callback( + deps: DepsMut, + env: Env, + info: MessageInfo, + callback_msg: CallbackMsg, +) -> StdResult { + if env.contract.address != info.sender { + return Err(StdError::generic_err( + "callbacks can only be invoked by the contract itself", + )); + } + + match callback_msg { + CallbackMsg::Reinvest {} => execute::reinvest(deps, env), + } +} +/* +#[entry_point] +pub fn reply(deps: DepsMut, env: Env, reply: Reply) -> StdResult { + match reply.id { + 1 => execute::register_steak_token(deps, unwrap_reply(reply)?), + REPLY_REGISTER_RECEIVED_COINS => { + execute::register_received_coins(deps, env, unwrap_reply(reply)?.events) + } + id => Err(StdError::generic_err(format!( + "invalid reply id: {}; must be 1-2", + id + ))), + } +} +*/ +#[entry_point] +pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { + match msg { + QueryMsg::Config {} => to_binary(&queries::config(deps)?), + QueryMsg::State {} => to_binary(&queries::state(deps, env)?), + QueryMsg::PendingBatch {} => to_binary(&queries::pending_batch(deps)?), + QueryMsg::PreviousBatch(id) => to_binary(&queries::previous_batch(deps, id)?), + QueryMsg::PreviousBatches { start_after, limit } => { + to_binary(&queries::previous_batches(deps, start_after, limit)?) + } + QueryMsg::UnbondRequestsByBatch { + id, + start_after, + limit, + } => to_binary(&queries::unbond_requests_by_batch( + deps, + id, + start_after, + limit, + )?), + QueryMsg::UnbondRequestsByUser { + user, + start_after, + limit, + } => to_binary(&queries::unbond_requests_by_user( + deps, + user, + start_after, + limit, + )?), + } +} + +#[entry_point] +pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult { + let contract_version = match get_contract_version(deps.storage) { + Ok(version) => version, + Err(_) => ContractVersion { + contract: "steak-hub-tf".to_string(), + version: "0".to_string(), + }, + }; + match contract_version.contract.as_ref() { + #[allow(clippy::single_match)] + "steak-hub-tf" => match contract_version.version.as_ref() { + #[allow(clippy::single_match)] + "0" => { + + } + + _ => {} + }, + _ => { + return Err(StdError::generic_err( + "contract name is not the same. aborting {}", + )) + } + } + /* + let state = State::default(); + + state.max_fee_rate.save(deps.storage,&Decimal::from_ratio(10u32,100u32))?; + state.fee_rate.save(deps.storage,&Decimal::from_ratio(10u32,100u32))?; + + */ + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + + Ok(Response::new() + .add_attribute("previous_contract_name", &contract_version.contract) + .add_attribute("previous_contract_version", &contract_version.version) + .add_attribute("new_contract_name", CONTRACT_NAME) + .add_attribute("new_contract_version", CONTRACT_VERSION)) +} diff --git a/contracts/hub-tf/src/execute.rs b/contracts/hub-tf/src/execute.rs new file mode 100644 index 0000000..2744e38 --- /dev/null +++ b/contracts/hub-tf/src/execute.rs @@ -0,0 +1,1055 @@ +use std::collections::HashSet; +use std::iter::FromIterator; +use std::str::FromStr; + +use cosmwasm_std::{Addr, BankMsg, Coin, CosmosMsg, Decimal, DepsMut, DistributionMsg, Env, Event, Order, Response, StdError, StdResult, SubMsg, to_binary, Uint128, WasmMsg}; +use kujira::denom::Denom; +use kujira::msg::DenomMsg; + +use pfc_steak::DecimalCheckedOps; +use pfc_steak::hub::{ + Batch, CallbackMsg, FeeType, PendingBatch, UnbondRequest, +}; +use pfc_steak::hub_tf::{ExecuteMsg, InstantiateMsg}; + +use crate::contract::REPLY_REGISTER_RECEIVED_COINS; +use crate::helpers::{get_denom_balance, parse_received_fund, query_all_delegations, query_delegation, query_delegations}; +use crate::math::{ + compute_mint_amount, compute_redelegations_for_rebalancing, compute_redelegations_for_removal, + compute_unbond_amount, compute_undelegations, reconcile_batches, +}; +use crate::state::{previous_batches, State, unbond_requests, VALIDATORS, VALIDATORS_ACTIVE}; +use crate::token_factory; +use crate::token_factory::denom::{MsgBurn, MsgCreateDenom, MsgMint}; +use crate::types::{Coins, Delegation}; + +//-------------------------------------------------------------------------------------------------- +// Instantiation +//-------------------------------------------------------------------------------------------------- + +pub fn instantiate(deps: DepsMut, env: Env, msg: InstantiateMsg) -> StdResult { + let state = State::default(); + + if msg.max_fee_amount > Decimal::from_str("1.00")? { + return Err(StdError::generic_err("Max fee can not exceed 1/100%")); + } + + if msg.fee_amount > msg.max_fee_amount { + return Err(StdError::generic_err("fee can not exceed max fee")); + } + let fee_type = FeeType::from_str(&msg.fee_account_type) + .map_err(|_| StdError::generic_err("Invalid Fee type: Wallet or FeeSplit only"))?; + + state + .owner + .save(deps.storage, &deps.api.addr_validate(&msg.owner)?)?; + state.epoch_period.save(deps.storage, &msg.epoch_period)?; + state.unbond_period.save(deps.storage, &msg.unbond_period)?; + state.unlocked_coins.save(deps.storage, &vec![])?; + state.prev_denom.save(deps.storage, &Uint128::zero())?; + state.denom.save(deps.storage, &msg.denom)?; + state.max_fee_rate.save(deps.storage, &msg.max_fee_amount)?; + state.fee_rate.save(deps.storage, &msg.fee_amount)?; + state.fee_account_type.save(deps.storage, &fee_type)?; + + state + .fee_account + .save(deps.storage, &deps.api.addr_validate(&msg.fee_account)?)?; + + state.pending_batch.save( + deps.storage, + &PendingBatch { + id: 1, + usteak_to_burn: Uint128::zero(), + est_unbond_start_time: env.block.time.seconds() + msg.epoch_period, + }, + )?; + + + for v in msg.validators { + VALIDATORS.insert(deps.storage, &v)?; + VALIDATORS_ACTIVE.insert(deps.storage, &v)?; + } + state.kuji_token_factory.save(deps.storage, &msg.kuji_token_factory)?; + let steak_denom = format!("factory/{0}/{1}", env.contract.address, msg.steak_denom); + let steak_denom_msg = msg.steak_denom; + state.steak_denom.save(deps.storage, &steak_denom)?; + state.steak_minted.save(deps.storage, &Uint128::zero())?; + + if let Some(dust) = msg.dust_collector { + state.dust_collector.save(deps.storage, &Some(deps.api.addr_validate(&dust)?))? + } else { + state.dust_collector.save(deps.storage, &None)? + } + if msg.kuji_token_factory { + todo!() + /* + Ok(Response::new().add_submessage(SubMsg::new(DenomMsg::Create { + subdenom: Denom::from(steak_denom_msg) + }))) + + */ + } else { + let c = >::into( + MsgCreateDenom { sender: env.contract.address.to_string(), subdenom: steak_denom_msg }); + + Ok(Response::new().add_message(c)) + } +} + + +//-------------------------------------------------------------------------------------------------- +// Bonding and harvesting logics +//-------------------------------------------------------------------------------------------------- + +/// NOTE: In a previous implementation, we split up the deposited Luna over all validators, so that +/// they all have the same amount of delegation. This is however quite gas-expensive: $1.5 cost in +/// the case of 15 validators. +/// +/// To save gas for users, now we simply delegate all deposited Luna to the validator with the +/// smallest amount of delegation. If delegations become severely unbalance as a result of this +/// (e.g. when a single user makes a very big deposit), anyone can invoke `ExecuteMsg::Rebalance` +/// to balance the delegations. +pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec, bond_msg: Option) -> StdResult { + let state = State::default(); + let denom = state.denom.load(deps.storage)?; + let amount_to_bond = parse_received_fund(&funds, &denom)?; + let steak_minted = state.steak_minted.load(deps.storage)?; + let steak_denom = state.steak_denom.load(deps.storage)?; + let kuji_version = state.kuji_token_factory.load(deps.storage)?; + let mut validators: Vec = Default::default(); + for v in VALIDATORS_ACTIVE.items(deps.storage, None, None, Order::Ascending) { + validators.push(v?); + } + + let mut validators_wl: HashSet = Default::default(); + for v in VALIDATORS.items(deps.storage, None, None, Order::Ascending) { + validators_wl.insert(v?); + } + for v in validators.iter() { + validators_wl.remove(v); + } + let non_active_validator_list = Vec::from_iter(validators_wl); + + // Query the current delegations made to validators, and find the validator with the smallest + // delegated amount through a linear search + // The code for linear search is a bit uglier than using `sort_by` but cheaper: O(n) vs O(n * log(n)) + let delegations_non_active = query_delegations(&deps.querier, &non_active_validator_list, &env.contract.address, &denom)?; + let delegations = query_delegations(&deps.querier, &validators, &env.contract.address, &denom)?; + + let mut validator = &delegations[0].validator; + let mut amount = delegations[0].amount; + for d in &delegations[1..] { + if d.amount < amount { + validator = &d.validator; + amount = d.amount; + } + } + let new_delegation = Delegation { + validator: validator.clone(), + amount: amount_to_bond.u128(), + denom: denom.clone(), + }; + + // Query the current supply of Steak and compute the amount to mint + // let usteak_supply = steak_minted; + let usteak_to_mint = compute_mint_amount(steak_minted, amount_to_bond, &delegations, &delegations_non_active); + state.steak_minted.save(deps.storage, &(steak_minted + usteak_to_mint))?; + // TODO deal with multiple token returns + state.prev_denom.save( + deps.storage, + &get_denom_balance(&deps.querier, env.contract.address.clone(), denom.clone())?, + )?; + + let delegate_submsg = SubMsg::reply_on_success( + new_delegation.to_cosmos_msg(), + REPLY_REGISTER_RECEIVED_COINS, + ); + + let mint_msg = if kuji_version { + let _k = DenomMsg::Mint { + denom: Denom::from(steak_denom), + amount: usteak_to_mint, + recipient: env.contract.address, + }; + // CosmosMsg::from(k); + todo!() + } else { + >::into(MsgMint { + sender: env.contract.address.to_string(), + amount: Some(token_factory::denom::Coin { + denom: steak_denom.clone(), + amount: usteak_to_mint.to_string(), + }), + }) + }; + + + let contract_info = deps.querier.query_wasm_contract_info(receiver.to_string()); + + // send the uSteak, optionally calling a smart contract + let send_transfer_msg: CosmosMsg = match contract_info { + Ok(_) => { + + //CosmosMsg::Bank(BankMsg::Send { to_address: "".to_string(), amount: vec![] }) + if let Some(exec_msg) = bond_msg { + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: receiver.to_string(), + msg: to_binary(&exec_msg)?, + funds: vec![Coin { + denom: steak_denom, + amount: usteak_to_mint, + }], + }) + } else { + CosmosMsg::Bank(BankMsg::Send { + to_address: receiver.to_string(), + amount: vec![Coin { + denom: steak_denom, + amount: usteak_to_mint, + }], + }) + } + } + Err(_) => { + CosmosMsg::Bank(BankMsg::Send { + to_address: receiver.to_string(), + amount: vec![Coin { + denom: steak_denom, + amount: usteak_to_mint, + }], + }) + } + }; + + let event = Event::new("steakhub/bonded") + .add_attribute("time", env.block.time.seconds().to_string()) + .add_attribute("height", env.block.height.to_string()) + .add_attribute("steak_receiver", receiver) + .add_attribute("denom_bonded", denom) + .add_attribute("denom_amount", amount_to_bond) + .add_attribute("usteak_minted", usteak_to_mint); + + Ok(Response::new() + .add_submessage(delegate_submsg) + .add_messages(vec![mint_msg, send_transfer_msg]) + // .add_message(send_msg) + .add_event(event) + .add_attribute("action", "steakhub/bond")) +} + +pub fn harvest(deps: DepsMut, env: Env) -> StdResult { + let state = State::default(); + let denom = state.denom.load(deps.storage)?; + state.prev_denom.save( + deps.storage, + &get_denom_balance(&deps.querier, env.contract.address.clone(), denom)?, + )?; + + let withdraw_submsgs = deps + .querier + .query_all_delegations(&env.contract.address)? + .into_iter() + .map(|d| { + SubMsg::reply_on_success( + CosmosMsg::Distribution(DistributionMsg::WithdrawDelegatorReward { + validator: d.validator, + }), + REPLY_REGISTER_RECEIVED_COINS, + ) + }) + .collect::>(); + + let callback_msg = CallbackMsg::Reinvest {}.into_cosmos_msg(&env.contract.address)?; + + Ok(Response::new() + .add_submessages(withdraw_submsgs) + .add_message(callback_msg) + .add_attribute("action", "steakhub/harvest")) +} + +/// NOTE: +/// 1. When delegation Native denom here, we don't need to use a `SubMsg` to handle the received coins, +/// because we have already withdrawn all claimable staking rewards previously in the same atomic +/// execution. +/// 2. Same as with `bond`, in the latest implementation we only delegate staking rewards with the +/// validator that has the smallest delegation amount. +pub fn reinvest(deps: DepsMut, env: Env) -> StdResult { + let state = State::default(); + let denom = state.denom.load(deps.storage)?; + let fee = state.fee_rate.load(deps.storage)?; + + let mut validators: Vec = Default::default(); + for v in VALIDATORS_ACTIVE.items(deps.storage, None, None, Order::Ascending) { + validators.push(v?); + } + let prev_coin = state.prev_denom.load(deps.storage)?; + let current_coin = + get_denom_balance(&deps.querier, env.contract.address.clone(), denom.clone())?; + + if current_coin <= prev_coin { + return Err(StdError::generic_err("no rewards")); + } + let amount_to_bond = current_coin.saturating_sub(prev_coin); + let mut unlocked_coins = state.unlocked_coins.load(deps.storage)?; + + /* + + if unlocked_coins.is_empty() { + return Err(StdError::generic_err("no rewards")); + } + let amount_to_bond = unlocked_coins + .iter() + .find(|coin| coin.denom == denom) + .ok_or_else(|| StdError::generic_err("no native amount available to be bonded"))? + .amount; + */ + let delegations = query_delegations(&deps.querier, &validators, &env.contract.address, &denom)?; + let mut validator = &delegations[0].validator; + let mut amount = delegations[0].amount; + for d in &delegations[1..] { + if d.amount < amount { + validator = &d.validator; + amount = d.amount; + } + } + let fee_amount = if fee.is_zero() { + Uint128::zero() + } else { + fee.checked_mul_uint(amount_to_bond)? + }; + let amount_to_bond_minus_fees = amount_to_bond.saturating_sub(fee_amount); + + let new_delegation = Delegation::new(validator, amount_to_bond_minus_fees.u128(), &denom); + + unlocked_coins.retain(|coin| coin.denom != denom); + state.unlocked_coins.save(deps.storage, &unlocked_coins)?; + + let event = Event::new("steakhub/harvested") + .add_attribute("time", env.block.time.seconds().to_string()) + .add_attribute("height", env.block.height.to_string()) + .add_attribute("denom", &denom) + .add_attribute("fees_deducted", fee_amount) + .add_attribute("denom_bonded", amount_to_bond_minus_fees); + + if fee_amount > Uint128::zero() { + let fee_account = state.fee_account.load(deps.storage)?; + let fee_type = state.fee_account_type.load(deps.storage)?; + + let send_msgs = match fee_type { + FeeType::Wallet => + vec![CosmosMsg::Bank(BankMsg::Send { + to_address: fee_account.to_string(), + amount: vec![Coin::new(fee_amount.into(), &denom)], + })], + FeeType::FeeSplit => { + let msg = pfc_fee_split::fee_split_msg::ExecuteMsg::Deposit { flush: false }; + + vec![msg.into_cosmos_msg(fee_account, vec![Coin::new(fee_amount.into(), &denom)])?] + } + }; + Ok(Response::new() + .add_message(new_delegation.to_cosmos_msg()) + .add_messages(send_msgs) + .add_event(event) + .add_attribute("action", "steakhub/reinvest")) + } else { + Ok(Response::new() + .add_message(new_delegation.to_cosmos_msg()) + .add_event(event) + .add_attribute("action", "steakhub/reinvest")) + } +} + +/// NOTE: a `SubMsgResponse` may contain multiple coin-receiving events, must handle them individually +pub fn register_received_coins( + deps: DepsMut, + env: Env, + mut events: Vec, +) -> StdResult { + events.retain(|event| event.ty == "coin_received"); + if events.is_empty() { + return Ok(Response::new()); + } + + let mut received_coins = Coins(vec![]); + for event in &events { + received_coins.add_many(&parse_coin_receiving_event(&env, event)?)?; + } + + let state = State::default(); + state + .unlocked_coins + .update(deps.storage, |coins| -> StdResult<_> { + let mut coins = Coins(coins); + coins.add_many(&received_coins)?; + Ok(coins.0) + })?; + + Ok(Response::new().add_attribute("action", "steakhub/register_received_coins")) +} + +fn parse_coin_receiving_event(env: &Env, event: &Event) -> StdResult { + let receiver = &event + .attributes + .iter() + .find(|attr| attr.key == "receiver") + .ok_or_else(|| StdError::generic_err("cannot find `receiver` attribute"))? + .value; + + let amount_str = &event + .attributes + .iter() + .find(|attr| attr.key == "amount") + .ok_or_else(|| StdError::generic_err("cannot find `amount` attribute"))? + .value; + + let amount = if *receiver == env.contract.address { + Coins::from_str(amount_str)? + } else { + Coins(vec![]) + }; + + Ok(amount) +} + +//-------------------------------------------------------------------------------------------------- +// Unbonding logics +//-------------------------------------------------------------------------------------------------- + +pub fn queue_unbond( + deps: DepsMut, + env: Env, + receiver: Addr, + funds: Vec, +) -> StdResult { + let state = State::default(); + let steak_denom = state.steak_denom.load(deps.storage)?; + + let usteak_to_burn = funds.iter().filter(|p| p.denom == steak_denom).map(|steak_funds| steak_funds.amount).sum::(); + if funds.len() != 1 || usteak_to_burn.is_zero() { + return Err(StdError::generic_err(format!( + "you can only send {} tokens to unbond", + steak_denom + ))); + } + let mut pending_batch = state.pending_batch.load(deps.storage)?; + pending_batch.usteak_to_burn += usteak_to_burn; + state.pending_batch.save(deps.storage, &pending_batch)?; + + unbond_requests().update( + deps.storage, + (pending_batch.id, receiver.as_ref()), + |x| -> StdResult<_> { + let mut request = x.unwrap_or_else(|| UnbondRequest { + id: pending_batch.id, + user: receiver.clone(), + shares: Uint128::zero(), + }); + request.shares += usteak_to_burn; + Ok(request) + }, + )?; + + let mut msgs: Vec = vec![]; + if env.block.time.seconds() >= pending_batch.est_unbond_start_time { + msgs.push(CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: env.contract.address.into(), + msg: to_binary(&ExecuteMsg::SubmitBatch {})?, + funds: vec![], + })); + } + + let event = Event::new("steakhub/unbond_queued") + .add_attribute("time", env.block.time.seconds().to_string()) + .add_attribute("height", env.block.height.to_string()) + .add_attribute("id", pending_batch.id.to_string()) + .add_attribute("receiver", receiver) + .add_attribute("usteak_to_burn", usteak_to_burn); + + Ok(Response::new() + .add_messages(msgs) + .add_event(event) + .add_attribute("action", "steakhub/queue_unbond")) +} + +pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { + let state = State::default(); + let denom = state.denom.load(deps.storage)?; + let steak_denom = state.steak_denom.load(deps.storage)?; + let kuji_version = state.kuji_token_factory.load(deps.storage)?; + let usteak_supply = state.steak_minted.load(deps.storage)?; + let mut validators: Vec = Default::default(); + for v in VALIDATORS.items(deps.storage, None, None, Order::Ascending) { + validators.push(v?); + } + + let unbond_period = state.unbond_period.load(deps.storage)?; + let pending_batch = state.pending_batch.load(deps.storage)?; + + let current_time = env.block.time.seconds(); + if current_time < pending_batch.est_unbond_start_time { + return Err(StdError::generic_err(format!( + "batch can only be submitted for unbonding after {}", + pending_batch.est_unbond_start_time + ))); + } + let mut validators_active: HashSet = Default::default(); + for v in VALIDATORS_ACTIVE.items(deps.storage, None, None, Order::Ascending) { + validators_active.insert(v?); + } + for v in validators.iter() { + validators_active.remove(v); + } + let active_validator_list = Vec::from_iter(validators_active); + + // for unbonding we still need to look at + // TODO verify denom + let delegations = query_all_delegations(&deps.querier, &env.contract.address)?; + let delegations_active = query_delegations(&deps.querier, &active_validator_list, &env.contract.address, &denom)?; + // let usteak_supply = query_cw20_total_supply(&deps.querier, &steak_token)?; + + let amount_to_unbond = + compute_unbond_amount(usteak_supply, pending_batch.usteak_to_burn, &delegations, &delegations_active); + + let new_undelegations = compute_undelegations(amount_to_unbond, &delegations, &denom); + + // NOTE: Regarding the `amount_unclaimed` value + // + // If validators misbehave and get slashed during the unbonding period, the contract can receive + // LESS Luna than `amount_to_unbond` when unbonding finishes! + // + // In this case, users who invokes `withdraw_unbonded` will have their txs failed as the contract + // does not have enough Luna balance. + // + // I don't have a solution for this... other than to manually fund contract with the slashed amount. + previous_batches().save( + deps.storage, + pending_batch.id, + &Batch { + id: pending_batch.id, + reconciled: false, + total_shares: pending_batch.usteak_to_burn, + amount_unclaimed: amount_to_unbond, + est_unbond_end_time: current_time + unbond_period, + }, + )?; + + let epoch_period = state.epoch_period.load(deps.storage)?; + state.pending_batch.save( + deps.storage, + &PendingBatch { + id: pending_batch.id + 1, + usteak_to_burn: Uint128::zero(), + est_unbond_start_time: current_time + epoch_period, + }, + )?; + state.prev_denom.save( + deps.storage, + &get_denom_balance(&deps.querier, env.contract.address.clone(), denom)?, + )?; + + let undelegate_submsgs = new_undelegations + .iter() + .map(|d| SubMsg::reply_on_success(d.to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS)) + .collect::>(); + + + let burn_msg = if kuji_version { + let _foo = DenomMsg::Burn { + denom: Denom::from(steak_denom), + amount: pending_batch.usteak_to_burn, + }; + todo!() + } else { + >::into(MsgBurn { + sender: env.contract.address.to_string(), + amount: Some(token_factory::denom::Coin { + denom: steak_denom, + amount: pending_batch.usteak_to_burn.to_string(), + }), + }) + }; + // yes.. this will fail if supply is less than the amount to burn. this is intentional. + state.steak_minted.save(deps.storage, &(usteak_supply - pending_batch.usteak_to_burn))?; + + + let event = Event::new("steakhub/unbond_submitted") + .add_attribute("time", env.block.time.seconds().to_string()) + .add_attribute("height", env.block.height.to_string()) + .add_attribute("id", pending_batch.id.to_string()) + .add_attribute("native_unbonded", amount_to_unbond) + .add_attribute("usteak_burned", pending_batch.usteak_to_burn); + + Ok(Response::new() + .add_submessages(undelegate_submsgs) + .add_message(burn_msg) + .add_event(event) + .add_attribute("action", "steakhub/unbond")) +} + +pub fn reconcile(deps: DepsMut, env: Env) -> StdResult { + let state = State::default(); + let current_time = env.block.time.seconds(); + + // Load batches that have not been reconciled + let all_batches = previous_batches() + .idx + .reconciled + .prefix("false".into()) + .range(deps.storage, None, None, Order::Ascending) + .map(|item| { + let (_, v) = item?; + Ok(v) + }) + .collect::>>()?; + + let mut batches = all_batches + .into_iter() + .filter(|b| current_time > b.est_unbond_end_time) + .collect::>(); + + let native_expected_received: Uint128 = batches.iter().map(|b| b.amount_unclaimed).sum(); + let denom = state.denom.load(deps.storage)?; + let unlocked_coins = state.unlocked_coins.load(deps.storage)?; + + let native_expected_unlocked = Coins(unlocked_coins).find(&denom).amount; + + let native_expected = native_expected_received + native_expected_unlocked; + let native_actual = deps + .querier + .query_balance(&env.contract.address, &denom)? + .amount; + + let native_to_deduct = native_expected + .checked_sub(native_actual) + .unwrap_or_else(|_| Uint128::zero()); + if !native_to_deduct.is_zero() { + reconcile_batches(&mut batches, native_expected - native_actual); + } + + for batch in batches.iter_mut() { + batch.reconciled = true; + previous_batches().save(deps.storage, batch.id, batch)?; + } + + let ids = batches + .iter() + .map(|b| b.id.to_string()) + .collect::>() + .join(","); + + let event = Event::new("steakhub/reconciled") + .add_attribute("ids", ids) + .add_attribute("native_deducted", native_to_deduct.to_string()); + + Ok(Response::new() + .add_event(event) + .add_attribute("action", "steakhub/reconcile")) +} + +pub fn withdraw_unbonded_admin( + deps: DepsMut, + env: Env, + user: Addr, + receiver: Addr, +) -> StdResult { + let state = State::default(); + + state.assert_owner(deps.storage, &user)?; + + withdraw_unbonded(deps, env, receiver.clone(), receiver) +} + +pub fn withdraw_unbonded( + deps: DepsMut, + env: Env, + user: Addr, + receiver: Addr, +) -> StdResult { + let state = State::default(); + let denom = state.denom.load(deps.storage)?; + let current_time = env.block.time.seconds(); + + // NOTE: If the user has too many unclaimed requests, this may not fit in the WASM memory... + // However, this is practically never going to happen. Who would create hundreds of unbonding + // requests and never claim them? + let requests = unbond_requests() + .idx + .user + .prefix(user.to_string()) + .range(deps.storage, None, None, Order::Ascending) + .map(|item| { + let (_, v) = item?; + Ok(v) + }) + .collect::>>()?; + + // NOTE: Native in the following batches are withdrawn it the batch: + // - is a _previous_ batch, not a _pending_ batch + // - is reconciled + // - has finished unbonding + // If not sure whether the batches have been reconciled, the user should first invoke `ExecuteMsg::Reconcile` + // before withdrawing. + let mut total_native_to_refund = Uint128::zero(); + let mut ids: Vec = vec![]; + for request in &requests { + if let Ok(mut batch) = previous_batches().load(deps.storage, request.id) { + if batch.reconciled && batch.est_unbond_end_time < current_time { + let native_to_refund = batch + .amount_unclaimed + .multiply_ratio(request.shares, batch.total_shares); + + ids.push(request.id.to_string()); + + total_native_to_refund += native_to_refund; + batch.total_shares -= request.shares; + batch.amount_unclaimed -= native_to_refund; + + if batch.total_shares.is_zero() { + previous_batches().remove(deps.storage, request.id)?; + } else { + previous_batches() + .save(deps.storage, batch.id, &batch)?; + } + unbond_requests() + .remove(deps.storage, (request.id, user.as_ref()))?; + } + } + } + + if total_native_to_refund.is_zero() { + return Err(StdError::generic_err("withdrawable amount is zero")); + } + + let refund_msg = CosmosMsg::Bank(BankMsg::Send { + to_address: receiver.clone().into(), + amount: vec![Coin::new(total_native_to_refund.u128(), denom)], + }); + + let event = Event::new("steakhub/unbonded_withdrawn") + .add_attribute("time", env.block.time.seconds().to_string()) + .add_attribute("height", env.block.height.to_string()) + .add_attribute("ids", ids.join(",")) + .add_attribute("user", user) + .add_attribute("receiver", receiver) + .add_attribute("amount_refunded", total_native_to_refund); + + Ok(Response::new() + .add_message(refund_msg) + .add_event(event) + .add_attribute("action", "steakhub/withdraw_unbonded")) +} + +//-------------------------------------------------------------------------------------------------- +// Ownership and management logics +//-------------------------------------------------------------------------------------------------- + +pub fn rebalance(deps: DepsMut, env: Env, minimum: Uint128) -> StdResult { + let state = State::default(); + let denom = state.denom.load(deps.storage)?; + let mut validators: Vec = Default::default(); + for v in VALIDATORS.items(deps.storage, None, None, Order::Ascending) { + validators.push(v?) + } + let mut validators_active: Vec = Default::default(); + for v in VALIDATORS_ACTIVE.items(deps.storage, None, None, Order::Ascending) { + validators_active.push(v?); + } + + let delegations = query_delegations(&deps.querier, &validators, &env.contract.address, &denom)?; + + let new_redelegations = + compute_redelegations_for_rebalancing(validators_active, &delegations, minimum); + + state.prev_denom.save( + deps.storage, + &get_denom_balance(&deps.querier, env.contract.address, denom)?, + )?; + + let redelegate_submsgs = new_redelegations + .iter() + .map(|rd| SubMsg::reply_on_success(rd.to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS)) + .collect::>(); + + let amount: u128 = new_redelegations.iter().map(|rd| rd.amount).sum(); + + let event = Event::new("steakhub/rebalanced").add_attribute("amount_moved", amount.to_string()); + + Ok(Response::new() + .add_submessages(redelegate_submsgs) + .add_event(event) + .add_attribute("action", "steakhub/rebalance")) +} + +pub fn add_validator(deps: DepsMut, sender: Addr, validator: String) -> StdResult { + let state = State::default(); + + state.assert_owner(deps.storage, &sender)?; + if VALIDATORS.contains(deps.storage, &validator) { + return Err(StdError::generic_err("validator is already whitelisted")); + } + VALIDATORS.insert(deps.storage, &validator)?; + VALIDATORS_ACTIVE.insert(deps.storage, &validator)?; + + let event = Event::new("steakhub/validator_added").add_attribute("validator", validator); + + Ok(Response::new() + .add_event(event) + .add_attribute("action", "steakhub/add_validator")) +} + +pub fn remove_validator( + deps: DepsMut, + env: Env, + sender: Addr, + validator: String, +) -> StdResult { + let state = State::default(); + + state.assert_owner(deps.storage, &sender)?; + let denom = state.denom.load(deps.storage)?; + + if !VALIDATORS.contains(deps.storage, &validator) { + return Err(StdError::generic_err( + "validator is not already whitelisted", + )); + } + VALIDATORS.remove(deps.storage, &validator)?; + VALIDATORS_ACTIVE.insert(deps.storage, &validator)?; + let mut validators: Vec = Default::default(); + for v in VALIDATORS.items(deps.storage, None, None, Order::Ascending) { + validators.push(v?); + } + + let delegations = query_delegations(&deps.querier, &validators, &env.contract.address, &denom)?; + let delegation_to_remove = + query_delegation(&deps.querier, &validator, &env.contract.address, &denom)?; + let new_redelegations = + compute_redelegations_for_removal(&delegation_to_remove, &delegations, &denom); + + state.prev_denom.save( + deps.storage, + &get_denom_balance(&deps.querier, env.contract.address, denom)?, + )?; + + let redelegate_submsgs = new_redelegations + .iter() + .map(|d| SubMsg::reply_on_success(d.to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS)) + .collect::>(); + + let event = Event::new("steak/validator_removed").add_attribute("validator", validator); + + Ok(Response::new() + .add_submessages(redelegate_submsgs) + .add_event(event) + .add_attribute("action", "steakhub/remove_validator")) +} + +pub fn remove_validator_ex( + deps: DepsMut, + _env: Env, + sender: Addr, + validator: String, +) -> StdResult { + let state = State::default(); + + state.assert_owner(deps.storage, &sender)?; + + if !VALIDATORS.contains(deps.storage, &validator) { + return Err(StdError::generic_err( + "validator is not already whitelisted", + )); + } + VALIDATORS.remove(deps.storage, &validator)?; + + + let event = Event::new("steak/validator_removed_ex").add_attribute("validator", validator); + + Ok(Response::new() + .add_event(event) + .add_attribute("action", "steakhub/remove_validator_ex")) +} + +pub fn pause_validator( + deps: DepsMut, + _env: Env, + sender: Addr, + validator: String, +) -> StdResult { + let state = State::default(); + + state.assert_owner(deps.storage, &sender)?; + + if !VALIDATORS_ACTIVE.contains(deps.storage, &validator) { + return Err(StdError::generic_err( + "validator is not already whitelisted", + )); + } + VALIDATORS_ACTIVE.remove(deps.storage, &validator)?; + + let event = Event::new("steak/pause_validator").add_attribute("validator", validator); + + Ok(Response::new() + .add_event(event) + .add_attribute("action", "steakhub/pause_validator")) +} + +pub fn unpause_validator( + deps: DepsMut, + _env: Env, + sender: Addr, + validator: String, +) -> StdResult { + let state = State::default(); + + state.assert_owner(deps.storage, &sender)?; + VALIDATORS_ACTIVE.insert(deps.storage, &validator)?; + + let event = Event::new("steak/unpause_validator").add_attribute("validator", validator); + + Ok(Response::new() + .add_event(event) + .add_attribute("action", "steakhub/unpause_validator")) +} + +pub fn set_unbond_period( + deps: DepsMut, + _env: Env, + sender: Addr, + unbond_period: u64, +) -> StdResult { + let state = State::default(); + + state.assert_owner(deps.storage, &sender)?; + state.unbond_period.save(deps.storage, &unbond_period)?; + let event = Event::new("steak/set_unbond_period") + .add_attribute("unbond_period", format!("{}", unbond_period)); + + Ok(Response::new() + .add_event(event) + .add_attribute("action", "steakhub/set_unbond_period")) +} + +pub fn transfer_ownership(deps: DepsMut, sender: Addr, new_owner: String) -> StdResult { + let state = State::default(); + + state.assert_owner(deps.storage, &sender)?; + state + .new_owner + .save(deps.storage, &deps.api.addr_validate(&new_owner)?)?; + + Ok(Response::new().add_attribute("action", "steakhub/transfer_ownership")) +} + +pub fn accept_ownership(deps: DepsMut, sender: Addr) -> StdResult { + let state = State::default(); + + let previous_owner = state.owner.load(deps.storage)?; + let new_owner = state.new_owner.load(deps.storage)?; + + if sender != new_owner { + return Err(StdError::generic_err( + "unauthorized: sender is not new owner", + )); + } + + state.owner.save(deps.storage, &sender)?; + state.new_owner.remove(deps.storage); + + let event = Event::new("steakhub/ownership_transferred") + .add_attribute("new_owner", new_owner) + .add_attribute("previous_owner", previous_owner); + + Ok(Response::new() + .add_event(event) + .add_attribute("action", "steakhub/transfer_ownership")) +} + +pub fn transfer_fee_account( + deps: DepsMut, + sender: Addr, + fee_account_type: String, + new_fee_account: String, +) -> StdResult { + let state = State::default(); + + state.assert_owner(deps.storage, &sender)?; + let fee_type = FeeType::from_str(&fee_account_type) + .map_err(|_| StdError::generic_err("Invalid Fee type: Wallet or FeeSplit only"))?; + + state.fee_account_type.save(deps.storage, &fee_type)?; + + state + .fee_account + .save(deps.storage, &deps.api.addr_validate(&new_fee_account)?)?; + + Ok(Response::new().add_attribute("action", "steakhub/transfer_fee_account")) +} + +pub fn change_denom(deps: DepsMut, sender: Addr, new_denom: String) -> StdResult { + let state = State::default(); + + state.assert_owner(deps.storage, &sender)?; + state.denom.save(deps.storage, &new_denom)?; + + Ok(Response::new().add_attribute("action", "steakhub/change_denom")) +} + +pub fn update_fee(deps: DepsMut, sender: Addr, new_fee: Decimal) -> StdResult { + let state = State::default(); + + state.assert_owner(deps.storage, &sender)?; + if new_fee > state.max_fee_rate.load(deps.storage)? { + return Err(StdError::generic_err( + "refusing to set fee above maximum set", + )); + } + state.fee_rate.save(deps.storage, &new_fee)?; + + Ok(Response::new().add_attribute("action", "steakhub/update_fee")) +} + +pub fn set_dust_collector( + deps: DepsMut, + _env: Env, + sender: Addr, + dust_collector: Option, +) -> StdResult { + let state = State::default(); + + + state.assert_owner(deps.storage, &sender)?; + if let Some(ref dust_addr) = dust_collector { + state.dust_collector.save(deps.storage, &Some(deps.api.addr_validate(dust_addr)?))?; + } else { + state.dust_collector.save(deps.storage, &None)?; + }; + + let event = Event::new("steak/set_dust_collector") + .add_attribute("dust_collector", dust_collector.unwrap_or("-cleared-".into())); + + Ok(Response::new() + .add_event(event) + .add_attribute("action", "steakhub/set_dust_collector")) +} + +pub fn collect_dust(deps: DepsMut, _env: Env) -> StdResult { + let state = State::default(); + + if let Some(_dust_addr) = state.dust_collector.load(deps.storage)? { + Ok(Response::new().add_attribute("dust", "tbd")) + } else { + Err(StdError::generic_err("No Dust collector set")) + } +} + +pub fn return_denom(deps: DepsMut, _env: Env, _funds: Vec) -> StdResult { + let state = State::default(); + + if let Some(_dust_addr) = state.dust_collector.load(deps.storage)? { + Ok(Response::new().add_attribute("dust", "returned")) + } else { + Err(StdError::generic_err("No Dust collector set")) + } +} \ No newline at end of file diff --git a/contracts/hub-tf/src/helpers.rs b/contracts/hub-tf/src/helpers.rs new file mode 100644 index 0000000..b15b842 --- /dev/null +++ b/contracts/hub-tf/src/helpers.rs @@ -0,0 +1,113 @@ +use std::str::FromStr; + +use cosmwasm_std::{Addr, BalanceResponse, BankQuery, Coin, QuerierWrapper, QueryRequest, StdError, StdResult, Uint128}; + +use crate::types::Delegation; +/* +/// Unwrap a `Reply` object to extract the response +pub(crate) fn unwrap_reply(reply: Reply) -> StdResult { + reply.result.into_result().map_err(StdError::generic_err) +} +*/ + + +/// Query the amounts of Luna a staker is delegating to a specific validator +pub(crate) fn query_delegation( + querier: &QuerierWrapper, + validator: &str, + delegator_addr: &Addr, + denom: &str, +) -> StdResult { + Ok(Delegation { + validator: validator.to_string(), + amount: querier + .query_delegation(delegator_addr, validator)? + .map(|fd| fd.amount.amount.u128()) + .unwrap_or(0), + denom: denom.into(), + }) +} +pub(crate) fn query_all_delegations( + querier: &QuerierWrapper, + delegator_addr: &Addr, + // _denom: &str, +) -> StdResult> { + Ok(querier.query_all_delegations(delegator_addr)?.into_iter().map( |std_delegation| {Delegation{ + validator: std_delegation.validator.to_string(), + amount: std_delegation.amount.amount.u128(), + denom: std_delegation.amount.denom, + }}).collect()) + + +} + +/// Query the amounts of Luna a staker is delegating to each of the validators specified +pub(crate) fn query_delegations( + querier: &QuerierWrapper, + validators: &[String], + delegator_addr: &Addr, + denom: &str, +) -> StdResult> { + validators + .iter() + .map(|validator| query_delegation(querier, validator, delegator_addr, denom)) + .collect() +} + +/// `cosmwasm_std::Coin` does not implement `FromStr`, so we have do it ourselves +/// +/// Parsing the string with regex doesn't work, because the resulting binary would be too big for +/// including the `regex` library. Example: +/// https://github.com/PFC-Validator/terra-rust/blob/v1.1.8/terra-rust-api/src/client/core_types.rs#L34-L55 +/// +/// We opt for a dirtier solution. Enumerate characters in the string, and break before the first +/// character that is not a number. Split the string at that index. +/// +/// This assumes the denom never starts with a number, which is true on Terra. +pub(crate) fn parse_coin(s: &str) -> StdResult { + for (i, c) in s.chars().enumerate() { + if c.is_alphabetic() { + let amount = Uint128::from_str(&s[..i])?; + let denom = &s[i..]; + return Ok(Coin::new(amount.u128(), denom)); + } + } + + Err(StdError::generic_err(format!( + "failed to parse coin: {}", + s + ))) +} + +/// Find the amount of a denom sent along a message, assert it is non-zero, and no other denom were +/// sent together +pub(crate) fn parse_received_fund(funds: &[Coin], denom: &str) -> StdResult { + if funds.len() != 1 { + return Err(StdError::generic_err(format!( + "must deposit exactly one coin; received {}", + funds.len() + ))); + } + + let fund = &funds[0]; + if fund.denom != denom { + return Err(StdError::generic_err(format!( + "expected {} deposit, received {}", + denom, fund.denom + ))); + } + + if fund.amount.is_zero() { + return Err(StdError::generic_err("deposit amount must be non-zero")); + } + + Ok(fund.amount) +} + +pub fn get_denom_balance( querier: &QuerierWrapper, account_addr: Addr, denom:String )-> StdResult { + let balance: BalanceResponse = querier.query(&QueryRequest::Bank(BankQuery::Balance { + address: account_addr.to_string(), + denom, + }))?; + Ok(balance.amount.amount) +} diff --git a/contracts/hub-tf/src/lib.rs b/contracts/hub-tf/src/lib.rs new file mode 100644 index 0000000..72df73e --- /dev/null +++ b/contracts/hub-tf/src/lib.rs @@ -0,0 +1,13 @@ +#[cfg(not(feature = "library"))] +pub mod contract; + +pub mod execute; +pub mod helpers; +pub mod math; +pub mod queries; +pub mod state; +pub mod types; +pub mod token_factory; +#[cfg(test)] +mod testing; +mod migrations; diff --git a/contracts/hub-tf/src/math.rs b/contracts/hub-tf/src/math.rs new file mode 100644 index 0000000..d3517fe --- /dev/null +++ b/contracts/hub-tf/src/math.rs @@ -0,0 +1,293 @@ +use std::{cmp, cmp::Ordering}; +use std::collections::HashMap; + +use cosmwasm_std::Uint128; + +use pfc_steak::hub::Batch; + +use crate::types::{Delegation, Redelegation, Undelegation}; + +//-------------------------------------------------------------------------------------------------- +// Minting/burning logics +//-------------------------------------------------------------------------------------------------- + +/// Compute the amount of Steak token to mint for a specific Luna stake amount. If current total +/// staked amount is zero, we use 1 usteak = 1 native; otherwise, we calculate base on the current +/// native per ustake ratio. +pub(crate) fn compute_mint_amount( + usteak_supply: Uint128, + native_to_bond: Uint128, + current_delegations: &[Delegation], + inactive_delegations: &[Delegation], +) -> Uint128 { + let native_bonded_c: u128 = current_delegations.iter().map(|d| d.amount).sum(); + let native_bonded_inactive: u128 = inactive_delegations.iter().map(|d| d.amount).sum(); + let native_bonded = native_bonded_c + native_bonded_inactive; + if native_bonded == 0 { + native_to_bond + } else { + usteak_supply.multiply_ratio(native_to_bond, native_bonded) + } +} + +/// Compute the amount of `native` to unbond for a specific `usteak` burn amount +/// +/// There is no way `usteak` total supply is zero when the user is senting a non-zero amount of `usteak` +/// to burn, so we don't need to handle division-by-zero here +pub(crate) fn compute_unbond_amount( + usteak_supply: Uint128, + usteak_to_burn: Uint128, + current_delegations: &[Delegation], + active_delegations: &[Delegation], +) -> Uint128 { + let native_bonded_c: u128 = current_delegations.iter().map(|d| d.amount).sum(); + let native_bonded_a: u128 = active_delegations.iter().map(|d| d.amount).sum(); + let native_bonded = native_bonded_c + native_bonded_a; + Uint128::new(native_bonded).multiply_ratio(usteak_to_burn, usteak_supply) +} + +//-------------------------------------------------------------------------------------------------- +// Delegation logics +//-------------------------------------------------------------------------------------------------- + +/// Given the current delegations made to validators, and a specific amount of `native` to unstake, +/// compute the undelegations to make such that the delegated amount to each validator is as even +/// as possible. +/// +/// This function is based on Lido's implementation: +/// https://github.com/lidofinance/lido-terra-contracts/blob/v1.0.2/contracts/lido_terra_validators_registry/src/common.rs#L55-102 +pub(crate) fn compute_undelegations( + native_to_unbond: Uint128, + current_delegations: &[Delegation], + denom: &str, +) -> Vec { + let native_staked: u128 = current_delegations.iter().map(|d| d.amount).sum(); + let validator_count = current_delegations.len() as u128; + + let native_to_distribute = native_staked - native_to_unbond.u128(); + let native_per_validator = native_to_distribute / validator_count; + let remainder = native_to_distribute % validator_count; + + let mut new_undelegations: Vec = vec![]; + let mut native_available = native_to_unbond.u128(); + for (i, d) in current_delegations.iter().enumerate() { + let remainder_for_validator: u128 = u128::from((i + 1) as u128 <= remainder); + let native_for_validator = native_per_validator + remainder_for_validator; + + let mut native_to_undelegate = if d.amount < native_for_validator { + 0 + } else { + d.amount - native_for_validator + }; + + native_to_undelegate = cmp::min(native_to_undelegate, native_available); + native_available -= native_to_undelegate; + + if native_to_undelegate > 0 { + new_undelegations.push(Undelegation::new(&d.validator, native_to_undelegate, denom)); + } + + if native_available == 0 { + break; + } + } + + new_undelegations +} + +/// Given a validator who is to be removed from the whitelist, and current delegations made to other +/// validators, compute the new delegations to make such that the delegated amount to each validator +// is as even as possible. +/// +/// This function is based on Lido's implementation: +/// https://github.com/lidofinance/lido-terra-contracts/blob/v1.0.2/contracts/lido_terra_validators_registry/src/common.rs#L19-L53 +pub(crate) fn compute_redelegations_for_removal( + delegation_to_remove: &Delegation, + current_delegations: &[Delegation], + denom: &str, +) -> Vec { + let native_staked: u128 = current_delegations.iter().map(|d| d.amount).sum(); + let validator_count = current_delegations.len() as u128; + + let native_to_distribute = native_staked + delegation_to_remove.amount; + let native_per_validator = native_to_distribute / validator_count; + let remainder = native_to_distribute % validator_count; + + let mut new_redelegations: Vec = vec![]; + let mut native_available = delegation_to_remove.amount; + for (i, d) in current_delegations.iter().enumerate() { + let remainder_for_validator: u128 = u128::from((i + 1) as u128 <= remainder); + let native_for_validator = native_per_validator + remainder_for_validator; + + let mut native_to_redelegate = if d.amount > native_for_validator { + 0 + } else { + native_for_validator - d.amount + }; + + native_to_redelegate = cmp::min(native_to_redelegate, native_available); + native_available -= native_to_redelegate; + + if native_to_redelegate > 0 { + new_redelegations.push(Redelegation::new( + &delegation_to_remove.validator, + &d.validator, + native_to_redelegate, + denom, + )); + } + + if native_available == 0 { + break; + } + } + + new_redelegations +} + +/// Compute redelegation moves that will make each validator's delegation the targeted amount (hopefully +/// this sentence makes sense) +/// +/// This algorithm does not guarantee the minimal number of moves, but is the best I can some up with... +pub(crate) fn compute_redelegations_for_rebalancing( + validators_active: Vec, + current_delegations: &[Delegation], + min_difference: Uint128, +) -> Vec { + let native_staked: u128 = current_delegations.iter().map(|d| d.amount).sum(); + let validator_count = validators_active.len() as u128; + + let native_per_validator = native_staked / validator_count; + let remainder = native_staked % validator_count; + + // If a validator's current delegated amount is greater than the target amount, native will be + // redelegated _from_ them. They will be put in `src_validators` vector + // If a validator's current delegated amount is smaller than the target amount, native will be + // redelegated _to_ them. They will be put in `dst_validators` vector + let mut src_delegations: Vec = vec![]; + let mut dst_delegations: Vec = vec![]; + for (i, d) in current_delegations.iter().enumerate() { + let remainder_for_validator: u128 = u128::from((i + 1) as u128 <= remainder); + let native_for_validator = native_per_validator + remainder_for_validator; + // eprintln!("{} amount ={} native={} min={}", d.validator, d.amount, native_for_validator, min_difference); + match d.amount.cmp(&native_for_validator) { + Ordering::Greater => { + if d.amount - native_for_validator > min_difference.u128() { + src_delegations.push(Delegation::new( + &d.validator, + d.amount - native_for_validator, + &d.denom, + )); + } + } + Ordering::Less => { + if validators_active.contains(&d.validator) && + native_for_validator - d.amount > min_difference.u128() { + dst_delegations.push(Delegation::new( + &d.validator, + native_for_validator - d.amount, + &d.denom, + )); + } + } + Ordering::Equal => (), + } + } + + let mut new_redelegations: Vec = vec![]; + while !src_delegations.is_empty() && !dst_delegations.is_empty() { + let src_delegation = src_delegations[0].clone(); + let dst_delegation = dst_delegations[0].clone(); + let native_to_redelegate = cmp::min(src_delegation.amount, dst_delegation.amount); + + if src_delegation.amount == native_to_redelegate { + src_delegations.remove(0); + } else { + src_delegations[0].amount -= native_to_redelegate; + } + + if dst_delegation.amount == native_to_redelegate { + dst_delegations.remove(0); + } else { + dst_delegations[0].amount -= native_to_redelegate; + } + new_redelegations.push(Redelegation::new( + &src_delegation.validator, + &dst_delegation.validator, + native_to_redelegate, + &src_delegation.denom, + )); + } + // eprintln!("new redelegations ={:?}", new_redelegations); + + new_redelegations +} + +//-------------------------------------------------------------------------------------------------- +// Batch logics +//-------------------------------------------------------------------------------------------------- + +/// If the received native amount after the unbonding period is less than expected, e.g. due to rounding +/// error or the validator(s) being slashed, then deduct the difference in amount evenly from each +/// unreconciled batch. +/// +/// The idea of "reconciling" is based on Stader's implementation: +/// https://github.com/stader-labs/stader-liquid-token/blob/v0.2.1/contracts/staking/src/contract.rs#L968-L1048 +pub(crate) fn reconcile_batches(batches: &mut [Batch], native_to_deduct: Uint128) { + let batch_count = batches.len() as u128; + let native_per_batch = native_to_deduct.u128() / batch_count; + let remainder = native_to_deduct.u128() % batch_count; + //let mut remaining_underflow = Uint128::zero(); + let mut underflows: HashMap = HashMap::default(); + + // distribute the underflows uniformly accross non-underflowing batches + for (i, batch) in batches.iter_mut().enumerate() { + let remainder_for_batch: u128 = u128::from((i + 1) as u128 <= remainder); + let native_for_batch = Uint128::new(native_per_batch + remainder_for_batch); + + if batch.amount_unclaimed < native_for_batch && batch_count > 1 { + // remaining_underflow += native_for_batch - batch.amount_unclaimed; + underflows.insert(i, native_for_batch - batch.amount_unclaimed); + } + batch.amount_unclaimed = batch.amount_unclaimed.saturating_sub(native_for_batch); + + batch.reconciled = true; + } + if !underflows.is_empty() { + let batch_count: u128 = batch_count - (underflows.len() as u128); + let to_deduct: Uint128 = underflows.iter().map(|v| v.1).sum(); + let native_per_batch = to_deduct.u128() / batch_count; + let remainder = to_deduct.u128() % batch_count; + let mut remaining_underflow = Uint128::zero(); + // the remaining underflow will be applied by oldest batch first. + for (i, batch) in batches.iter_mut().enumerate() { + if !batch.amount_unclaimed.is_zero() { + let remainder_for_batch: u128 = u128::from((i + 1) as u128 <= remainder); + let native_for_batch = Uint128::new(native_per_batch + remainder_for_batch); + if batch.amount_unclaimed < native_for_batch && batch_count > 1 { + remaining_underflow += native_for_batch - batch.amount_unclaimed; + } + batch.amount_unclaimed = batch.amount_unclaimed.saturating_sub(native_for_batch); + } + } + + if !remaining_underflow.is_zero() { + // the remaining underflow will be applied by oldest batch first. + for (_, batch) in batches.iter_mut().enumerate() { + if !batch.amount_unclaimed.is_zero() && !remaining_underflow.is_zero() { + if batch.amount_unclaimed >= remaining_underflow { + batch.amount_unclaimed -= remaining_underflow; + remaining_underflow = Uint128::zero() + } else { + remaining_underflow -= batch.amount_unclaimed; + batch.amount_unclaimed = Uint128::zero(); + } + } + } + + if !remaining_underflow.is_zero() { + // no way to reconcile right now, need to top up some funds. + } + } + } +} diff --git a/contracts/hub-tf/src/migrations.rs b/contracts/hub-tf/src/migrations.rs new file mode 100644 index 0000000..b7ead8f --- /dev/null +++ b/contracts/hub-tf/src/migrations.rs @@ -0,0 +1 @@ +// no migrations yet \ No newline at end of file diff --git a/contracts/hub-tf/src/queries.rs b/contracts/hub-tf/src/queries.rs new file mode 100644 index 0000000..f544f57 --- /dev/null +++ b/contracts/hub-tf/src/queries.rs @@ -0,0 +1,184 @@ +use std::collections::{BTreeSet, HashSet}; +use std::iter::FromIterator; + +use cosmwasm_std::{ Decimal, Deps, Env, Order, StdResult, Uint128}; +use cw_storage_plus::Bound; + +use pfc_steak::hub::{ + Batch, ConfigResponse, PendingBatch, StateResponse, UnbondRequestsByBatchResponseItem, + UnbondRequestsByUserResponseItem, +}; + +use crate::helpers::query_delegations; +use crate::state; +use crate::state::{State, VALIDATORS, VALIDATORS_ACTIVE}; + +const MAX_LIMIT: u32 = 30; +const DEFAULT_LIMIT: u32 = 10; + +pub fn config(deps: Deps) -> StdResult { + let state = State::default(); + let mut validators: BTreeSet = BTreeSet::new(); + for res in VALIDATORS.items(deps.storage, None, None, Order::Ascending) { + validators.insert(res?); + } + + let mut validators_active: BTreeSet = BTreeSet::new(); + for res in VALIDATORS_ACTIVE.items(deps.storage, None, None, Order::Ascending) { + let validator = res?; + validators.remove(&validator); + validators_active.insert(validator); + } + + + let validator_active_vec: Vec = Vec::from_iter(validators_active.into_iter()); + let paused_validators: Vec = Vec::from_iter(validators.into_iter()); + + Ok(ConfigResponse { + owner: state.owner.load(deps.storage)?.into(), + new_owner: state + .new_owner + .may_load(deps.storage)? + .map(|addr| addr.into()), + steak_token: state.steak_denom.load(deps.storage)?, + epoch_period: state.epoch_period.load(deps.storage)?, + unbond_period: state.unbond_period.load(deps.storage)?, + denom: state.denom.load(deps.storage)?, + fee_type: state.fee_account_type.load(deps.storage)?.to_string(), + fee_account: state.fee_account.load(deps.storage)?.to_string(), + fee_rate: state.fee_rate.load(deps.storage)?, + max_fee_rate: state.max_fee_rate.load(deps.storage)?, + validators: validator_active_vec, + paused_validators, + dust_collector: state.dust_collector.load(deps.storage)?.map( |a| a.to_string()) + }) +} + +pub fn state(deps: Deps, env: Env) -> StdResult { + let state = State::default(); + let denom = state.denom.load(deps.storage)?; + let total_usteak = state.steak_minted.load(deps.storage)?;// query_cw20_total_supply(&deps.querier, &steak_token)?; + let mut validators: HashSet = Default::default(); + for res in VALIDATORS.items(deps.storage, None, None, Order::Ascending) { + validators.insert(res?); + } + let mut validators_active: HashSet = Default::default(); + for res in VALIDATORS_ACTIVE.items(deps.storage, None, None, Order::Ascending) { + validators_active.insert(res?); + } + validators.extend(validators_active); + let validator_vec: Vec = Vec::from_iter(validators.into_iter()); + let delegations = query_delegations(&deps.querier, &validator_vec, &env.contract.address, &denom)?; + let total_native: u128 = delegations.iter().map(|d| d.amount).sum(); + + let exchange_rate = if total_usteak.is_zero() { + Decimal::one() + } else { + Decimal::from_ratio(total_native, total_usteak) + }; + + Ok(StateResponse { + total_usteak, + total_native: Uint128::new(total_native), + exchange_rate, + unlocked_coins: state.unlocked_coins.load(deps.storage)?, + }) +} + +pub fn pending_batch(deps: Deps) -> StdResult { + let state = State::default(); + + state.pending_batch.load(deps.storage) +} + +pub fn previous_batch(deps: Deps, id: u64) -> StdResult { + state::previous_batches().load(deps.storage, id) +} + +pub fn previous_batches( + deps: Deps, + start_after: Option, + limit: Option, +) -> StdResult> { + let start = start_after.map(Bound::exclusive); + let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize; + + state::previous_batches() + .range(deps.storage, start, None, Order::Ascending) + .take(limit) + .map(|item| { + let (_, v) = item?; + Ok(v) + }) + .collect() +} + +pub fn unbond_requests_by_batch( + deps: Deps, + id: u64, + start_after: Option, + limit: Option, +) -> StdResult> { + //let addr: Addr; + let addr_clone; + let start = match start_after { + None => None, + Some(addr_str) => { + deps.api.addr_validate(&addr_str)?; + addr_clone = addr_str; + Some(Bound::exclusive(addr_clone.as_str())) + } + }; + let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize; + + state::unbond_requests() + .prefix(id) + .range(deps.storage, start, None, Order::Ascending) + .take(limit) + .map(|item| { + let (_, v) = item?; + Ok(v.into()) + }) + .collect() +} + +pub fn unbond_requests_by_user( + deps: Deps, + user: String, + start_after: Option, + limit: Option, +) -> StdResult> { + let user_addr = deps.api.addr_validate(&user)?; + + let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize; + if let Some(start_id) = start_after { + state::unbond_requests() + .idx + .user + .prefix(user_addr.to_string()) + .range(deps.storage, None, None, Order::Ascending) + .filter(|r| { + let x = r.as_ref().unwrap(); + + x.1.id > start_id + }) + .take(limit) + .map(|item| { + let (_, v) = item?; + Ok(v.into()) + }) + .collect() + } else { + state::unbond_requests() + .idx + .user + .prefix(user_addr.to_string()) + .range(deps.storage, None, None, Order::Ascending) + .take(limit) + .map(|item| { + let (_, v) = item?; + Ok(v.into()) + }) + .collect() + } +} diff --git a/contracts/hub-tf/src/state.rs b/contracts/hub-tf/src/state.rs new file mode 100644 index 0000000..44e451f --- /dev/null +++ b/contracts/hub-tf/src/state.rs @@ -0,0 +1,155 @@ +use cosmwasm_std::{Addr, Coin, Decimal, StdError, StdResult, Storage, Uint128}; +use cw_item_set::Set; +use cw_storage_plus::{Index, IndexedMap, IndexList, Item, MultiIndex}; + +use pfc_steak::hub::{Batch, FeeType, PendingBatch, UnbondRequest}; + +pub(crate) const BATCH_KEY_V101: &str = "previous_batches_101"; +pub(crate) const BATCH_KEY_OWNER_V101: &str = "previous_batches_owner_101"; +pub(crate) const UNBOND_KEY_V101: &str = "unbond_101"; +pub(crate) const UNBOND_KEY_USER_V101: &str = "unbond_owner_101"; + +/// Validators who currently have delegations. This can be different due to being paused/re-delegations +pub(crate) const VALIDATORS: Set<&str> = Set::new("validators_001", "validator_counter_001"); +/// Validators where we send delegations to. +pub(crate) const VALIDATORS_ACTIVE: Set<&str> = Set::new("validators_active_001", "validator_active_counter_001"); + +// pub(crate) const BATCH_KEY_RECONCILED_V101: &str = "previous_batches__reconciled_101"; + +pub(crate) struct State<'a> { + /// Account who can call certain privileged functions + pub owner: Item<'a, Addr>, + /// Pending ownership transfer, awaiting acceptance by the new owner + pub new_owner: Item<'a, Addr>, + pub fee_account_type: Item<'a, FeeType>, + /// Account to send fees to + pub fee_account: Item<'a, Addr>, + /// Current fee rate + pub fee_rate: Item<'a, Decimal>, + /// Maximum fee rate + pub max_fee_rate: Item<'a, Decimal>, + /// denom to accept + pub denom: Item<'a, String>, + /// denom to mint. + pub steak_denom: Item<'a, String>, + /// coins minted + pub steak_minted: Item<'a, Uint128>, + /// How often the unbonding queue is to be executed + pub epoch_period: Item<'a, u64>, + /// The staking module's unbonding time, in seconds + pub unbond_period: Item<'a, u64>, + /// Validators who will receive the delegations + // pub validators: Item<'a, Vec>, + + /// Coins that can be reinvested + pub unlocked_coins: Item<'a, Vec>, + /// The current batch of un-bonding requests queued to be executed + pub pending_batch: Item<'a, PendingBatch>, + + /// Previous batches that have started unbonding but not yet finished + // pub previous_batches: IndexedMap<'a, u64, Batch, PreviousBatchesIndexes<'a>>, + /// Users' shares in un-bonding batches + //pub unbond_requests: IndexedMap<'a, (u64, &'a Addr), UnbondRequest, UnbondRequestsIndexes<'a>>, + // pub validators_active: Item<'a, Vec>, + /// coins in 'denom' held before reinvest was called. + pub prev_denom: Item<'a, Uint128>, + /// Kuji version of Token-factory + pub kuji_token_factory: Item<'a, bool>, + /// Dust Collector contract + pub dust_collector: Item<'a, Option>, +} + + +impl Default for State<'static> { + fn default() -> Self { + Self { + owner: Item::new("owner"), + new_owner: Item::new("new_owner"), + fee_account: Item::new("fee_account"), + fee_rate: Item::new("fee_rate"), + max_fee_rate: Item::new("max_fee_rate"), + denom: Item::new("denom"), + steak_denom: Item::new("steak_denom"), + steak_minted: Item::new("steak_minted"), + //steak_token: Item::new("steak_token"), + epoch_period: Item::new("epoch_period"), + unbond_period: Item::new("unbond_period"), + // validators: Item::new("validators"), + unlocked_coins: Item::new("unlocked_coins"), + pending_batch: Item::new("pending_batch"), + // previous_batches: IndexedMap::new(BATCH_KEY_V101, pb_indexes), + // unbond_requests: IndexedMap::new("unbond_requests", ubr_indexes), + // validators_active: Item::new("validators_active"), + prev_denom: Item::new("prev_denom"), + fee_account_type: Item::new("fee_account_type"), + kuji_token_factory: Item::new("kuji_token_factory"), + dust_collector: Item::new("dust_collector"), + } + } +} + +impl<'a> State<'a> { + pub fn assert_owner(&self, storage: &dyn Storage, sender: &Addr) -> StdResult<()> { + let owner = self.owner.load(storage)?; + if *sender == owner { + Ok(()) + } else { + Err(StdError::generic_err("unauthorized: sender is not owner")) + } + } +} + +pub fn previous_batches_reconciled_idx(_pk: &[u8], d: &Batch) -> String { + if d.reconciled { + String::from("true") + } else { + String::from("false") + } +} + +pub fn previous_batches<'a>() -> IndexedMap<'a, u64, Batch, PreviousBatchesIndexes<'a>> { + IndexedMap::new( + BATCH_KEY_V101, + PreviousBatchesIndexes { + reconciled: MultiIndex::new(previous_batches_reconciled_idx, BATCH_KEY_V101, BATCH_KEY_OWNER_V101), + }, + ) +} + +pub fn unbond_requests_user_idx(_pk: &[u8], d: &UnbondRequest) -> String { + d.user.to_string() +} + +pub fn unbond_requests<'a>() -> IndexedMap<'a, (u64, &'a str), UnbondRequest, UnbondRequestsIndexes<'a>> { + IndexedMap::new( + UNBOND_KEY_V101, + UnbondRequestsIndexes { + user: MultiIndex::new(unbond_requests_user_idx, UNBOND_KEY_V101, UNBOND_KEY_USER_V101), + }, + ) +} + + +pub struct PreviousBatchesIndexes<'a> { + // pk goes to second tuple element + pub reconciled: MultiIndex<'a, String, Batch, String>, +} + +impl<'a> IndexList for PreviousBatchesIndexes<'a> { + fn get_indexes(&'_ self) -> Box> + '_> { + let v: Vec<&dyn Index> = vec![&self.reconciled]; + Box::new(v.into_iter()) + } +} + +pub struct UnbondRequestsIndexes<'a> { + // pk goes to second tuple element + pub user: MultiIndex<'a, String, UnbondRequest, String>, +} + +impl<'a> IndexList for UnbondRequestsIndexes<'a> { + fn get_indexes(&'_ self) -> Box> + '_> { + let v: Vec<&dyn Index> = vec![&self.user]; + Box::new(v.into_iter()) + } +} diff --git a/contracts/hub-tf/src/testing/custom_querier.rs b/contracts/hub-tf/src/testing/custom_querier.rs new file mode 100644 index 0000000..cdb8fb5 --- /dev/null +++ b/contracts/hub-tf/src/testing/custom_querier.rs @@ -0,0 +1,69 @@ +use cosmwasm_std::{ + Addr, Coin, Empty, from_slice, FullDelegation, Querier, QuerierResult, + QueryRequest, SystemError, WasmQuery, +}; +use cosmwasm_std::testing::{BankQuerier, MOCK_CONTRACT_ADDR, StakingQuerier}; + +use crate::types::Delegation; + +use super::helpers::err_unsupported_query; + +#[derive(Default)] +pub(super) struct CustomQuerier { + pub bank_querier: BankQuerier, + pub staking_querier: StakingQuerier, +} + +impl Querier for CustomQuerier { + fn raw_query(&self, bin_request: &[u8]) -> QuerierResult { + let request: QueryRequest<_> = match from_slice(bin_request) { + Ok(v) => v, + Err(e) => { + return Err(SystemError::InvalidRequest { + error: format!("Parsing query request: {}", e), + request: bin_request.into(), + }) + .into(); + } + }; + self.handle_query(&request) + } +} + +impl CustomQuerier { + pub fn set_bank_balances(&mut self, balances: &[Coin]) { + self.bank_querier = BankQuerier::new(&[(MOCK_CONTRACT_ADDR, balances)]); + } + + pub fn set_staking_delegations(&mut self, delegations: &[Delegation]) { + let fds = delegations + .iter() + .map(|d| FullDelegation { + delegator: Addr::unchecked(MOCK_CONTRACT_ADDR), + validator: d.validator.clone(), + amount: Coin::new(d.amount, "uluna"), + can_redelegate: Coin::new(0, "uluna"), + accumulated_rewards: vec![], + }) + .collect::>(); + + self.staking_querier = StakingQuerier::new("uluna", &[], &fds); + } + + pub fn handle_query(&self, request: &QueryRequest) -> QuerierResult { + match request { + QueryRequest::Wasm(WasmQuery::Smart { + contract_addr: _, + msg, + }) => { + err_unsupported_query(msg) + } + + QueryRequest::Bank(query) => self.bank_querier.query(query), + + QueryRequest::Staking(query) => self.staking_querier.query(query), + + _ => err_unsupported_query(request), + } + } +} diff --git a/contracts/hub-tf/src/testing/helpers.rs b/contracts/hub-tf/src/testing/helpers.rs new file mode 100644 index 0000000..ecfd4da --- /dev/null +++ b/contracts/hub-tf/src/testing/helpers.rs @@ -0,0 +1,48 @@ +use cosmwasm_std::testing::{mock_env, MockApi, MockStorage, MOCK_CONTRACT_ADDR}; +use cosmwasm_std::{ + from_binary, Addr, BlockInfo, ContractInfo, Deps, Env, OwnedDeps, QuerierResult, SystemError, + SystemResult, Timestamp, +}; +use serde::de::DeserializeOwned; + +use pfc_steak::hub::QueryMsg; + +use crate::contract::query; + +use super::custom_querier::CustomQuerier; + +pub(super) fn err_unsupported_query(request: T) -> QuerierResult { + SystemResult::Err(SystemError::InvalidRequest { + error: format!("[mock] unsupported query: {:?}", request), + request: Default::default(), + }) +} + +pub(super) fn mock_dependencies() -> OwnedDeps { + OwnedDeps { + storage: MockStorage::default(), + api: MockApi::default(), + querier: CustomQuerier::default(), + custom_query_type: Default::default(), + } +} + +pub(super) fn mock_env_at_timestamp(timestamp: u64) -> Env { + Env { + block: BlockInfo { + height: 12_345, + time: Timestamp::from_seconds(timestamp), + chain_id: "cosmos-testnet-14002".to_string(), + }, + contract: ContractInfo { + address: Addr::unchecked(MOCK_CONTRACT_ADDR), + }, + transaction: None, + } +} + +pub(super) fn query_helper(deps: Deps, msg: QueryMsg) -> T { + let bin = query(deps, mock_env(), msg).unwrap(); + //eprintln!("Query Response {:?}",&bin); + from_binary(&bin).unwrap() +} diff --git a/contracts/hub-tf/src/testing/mod.rs b/contracts/hub-tf/src/testing/mod.rs new file mode 100644 index 0000000..a059f3e --- /dev/null +++ b/contracts/hub-tf/src/testing/mod.rs @@ -0,0 +1,4 @@ +mod custom_querier; + +mod helpers; +mod tests; diff --git a/contracts/hub-tf/src/testing/tests.rs b/contracts/hub-tf/src/testing/tests.rs new file mode 100644 index 0000000..fb56dac --- /dev/null +++ b/contracts/hub-tf/src/testing/tests.rs @@ -0,0 +1,2018 @@ +use std::str::FromStr; + +use cosmwasm_std::{Addr, BankMsg, Coin, CosmosMsg, Decimal, DistributionMsg, Order, OwnedDeps, ReplyOn, StdError, SubMsg, to_binary, Uint128, WasmMsg}; +use cosmwasm_std::testing::{MOCK_CONTRACT_ADDR, mock_env, mock_info, MockApi, MockStorage}; + +use pfc_steak::hub::{ + Batch, CallbackMsg, ConfigResponse, PendingBatch, QueryMsg, + StateResponse, UnbondRequest, UnbondRequestsByBatchResponseItem, + UnbondRequestsByUserResponseItem, +}; +use pfc_steak::hub_tf::{ExecuteMsg, InstantiateMsg}; + +use crate::contract::{execute, instantiate, REPLY_REGISTER_RECEIVED_COINS}; +use crate::helpers::{parse_coin, parse_received_fund}; +use crate::math::{ + compute_redelegations_for_rebalancing, compute_redelegations_for_removal, compute_undelegations, +}; +use crate::state::{previous_batches, State, unbond_requests, VALIDATORS}; +use crate::token_factory::denom; +use crate::token_factory::denom::{MsgBurn, MsgCreateDenom, MsgMint}; +use crate::types::{Coins, Delegation, Redelegation, Undelegation}; + +use super::custom_querier::CustomQuerier; +use super::helpers::{mock_dependencies, mock_env_at_timestamp, query_helper}; + +//-------------------------------------------------------------------------------------------------- +// Test setup +//-------------------------------------------------------------------------------------------------- + +fn setup_test() -> OwnedDeps { + let mut deps = mock_dependencies(); + + let res = instantiate( + deps.as_mut(), + mock_env_at_timestamp(10000), + mock_info("deployer", &[]), + InstantiateMsg { + owner: "larry".to_string(), + denom: "uxyz".to_string(), + steak_denom: "boneXYZ".to_string(), + fee_account_type: "Wallet".to_string(), + fee_account: "the_fee_man".to_string(), + fee_amount: Decimal::from_ratio(10_u128, 100_u128), //10% + max_fee_amount: Decimal::from_ratio(20_u128, 100_u128), //20% + + epoch_period: 259200, // 3 * 24 * 60 * 60 = 3 days + unbond_period: 1814400, // 21 * 24 * 60 * 60 = 21 days + validators: vec![ + "alice".to_string(), + "bob".to_string(), + "charlie".to_string(), + ], + kuji_token_factory: false, + dust_collector: Some("dusty_1".to_string()) + }, + ) + .unwrap(); + + + assert_eq!(res.messages.len(), 1); + let c = >::into( + MsgCreateDenom { sender: "cosmos2contract".to_string(), subdenom: "boneXYZ".to_string() }); + assert_eq!(res.messages[0], SubMsg::new(c)); + + deps +} + +fn setup_test_fee_split() -> OwnedDeps { + let mut deps = mock_dependencies(); + + let res = instantiate( + deps.as_mut(), + mock_env_at_timestamp(10000), + mock_info("deployer", &[]), + InstantiateMsg { + owner: "larry".to_string(), + denom: "uxyz".to_string(), + steak_denom: "sXYZ".to_string(), + fee_account_type: "FeeSplit".to_string(), + fee_account: "fee_split_contract".to_string(), + fee_amount: Decimal::from_ratio(10_u128, 100_u128), //10% + max_fee_amount: Decimal::from_ratio(20_u128, 100_u128), //20% + epoch_period: 259200, // 3 * 24 * 60 * 60 = 3 days + unbond_period: 1814400, // 21 * 24 * 60 * 60 = 21 days + validators: vec![ + "alice".to_string(), + "bob".to_string(), + "charlie".to_string(), + ], + + kuji_token_factory: false, + dust_collector: Some("dusty_2".to_string()) + }, + ) + .unwrap(); + + assert_eq!(res.messages.len(), 1); + let c = >::into( + MsgCreateDenom { sender: "cosmos2contract".to_string(), subdenom: "sXYZ".to_string() }); + assert_eq!(res.messages[0], SubMsg::new(c)); + + deps +} + +//-------------------------------------------------------------------------------------------------- +// Execution +//-------------------------------------------------------------------------------------------------- + +#[test] +fn proper_instantiation() { + let deps = setup_test(); + + let res: ConfigResponse = query_helper(deps.as_ref(), QueryMsg::Config {}); + assert_eq!( + res, + ConfigResponse { + owner: "larry".to_string(), + new_owner: None, + steak_token: "factory/cosmos2contract/boneXYZ".to_string(), + epoch_period: 259200, + unbond_period: 1814400, + denom: "uxyz".to_string(), + fee_type: "Wallet".to_string(), + fee_account: "the_fee_man".to_string(), + fee_rate: Decimal::from_ratio(10_u128, 100_u128), + max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), + validators: vec![ + "alice".to_string(), + "bob".to_string(), + "charlie".to_string(), + ], + paused_validators: vec![], + dust_collector:Some("dusty_1".to_string()) + } + ); + + let res: StateResponse = query_helper(deps.as_ref(), QueryMsg::State {}); + assert_eq!( + res, + StateResponse { + total_usteak: Uint128::zero(), + total_native: Uint128::zero(), + exchange_rate: Decimal::one(), + unlocked_coins: vec![], + }, + ); + + let res: PendingBatch = query_helper(deps.as_ref(), QueryMsg::PendingBatch {}); + assert_eq!( + res, + PendingBatch { + id: 1, + usteak_to_burn: Uint128::zero(), + est_unbond_start_time: 269200, // 10,000 + 259,200 + }, + ); + let deps_fee_split = setup_test_fee_split(); + + let res_fee_split: ConfigResponse = query_helper(deps_fee_split.as_ref(), QueryMsg::Config {}); + assert_eq!( + res_fee_split, + ConfigResponse { + owner: "larry".to_string(), + new_owner: None, + steak_token: "factory/cosmos2contract/sXYZ".to_string(), + epoch_period: 259200, + unbond_period: 1814400, + denom: "uxyz".to_string(), + fee_type: "FeeSplit".to_string(), + fee_account: "fee_split_contract".to_string(), + fee_rate: Decimal::from_ratio(10_u128, 100_u128), + max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), + validators: vec![ + "alice".to_string(), + "bob".to_string(), + "charlie".to_string(), + ], + paused_validators: vec![], + dust_collector: Some("dusty_2".to_string()) + } + ); +} + +#[test] +fn bonding() { + let mut deps = setup_test(); + + // Bond when no delegation has been made + // In this case, the full deposit simply goes to the first validator + let res = execute( + deps.as_mut(), + mock_env(), + mock_info("user_1", &[Coin::new(1_000_000, "uxyz")]), + ExecuteMsg::Bond { receiver: None, exec_msg: None }, + ) + .unwrap(); + + // 3 messages. (switched to 3 so we can 'send' instead of 'transfer' minted tokens, so contract will know about it + // 1 - delegate + // 2 - mint token (to ourselves) + // 3 - send/transfer it + assert_eq!(res.messages.len(), 3); + assert_eq!( + res.messages[0], + SubMsg::reply_on_success(Delegation::new("alice", 1_000_000, "uxyz").to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS) + ); + let mint_msg = >::into(MsgMint { + sender: "cosmos2contract".to_string(), + amount: Some(denom::Coin { + denom: "factory/cosmos2contract/boneXYZ".to_string(), + amount: Uint128::new(1_000_000).to_string(), + }), + }); + assert_eq!( + res.messages[1], + SubMsg { + id: 0, + msg: mint_msg, + gas_limit: None, + reply_on: ReplyOn::Never, + } + ); + + assert_eq!( + res.messages[2], + SubMsg { + id: 0, + msg: CosmosMsg::Bank(BankMsg::Send { + to_address: "user_1".to_string(), + amount: vec![Coin { denom: "factory/cosmos2contract/boneXYZ".to_string(), amount: Uint128::new(1_000_000) }], + }), + gas_limit: None, + reply_on: ReplyOn::Never, + } + ); + + + // Bond when there are existing delegations, and Luna:Steak exchange rate is >1 + // Previously user 1 delegated 1,000,000 uluna. We assume we have accumulated 2.5% yield at 1025000 staked + deps.querier.set_staking_delegations(&[ + Delegation::new("alice", 341667, "uxyz"), + Delegation::new("bob", 341667, "uxyz"), + Delegation::new("charlie", 341666, "uxyz"), + ]); + + // Charlie has the smallest amount of delegation, so the full deposit goes to him + let res = execute( + deps.as_mut(), + mock_env(), + mock_info("user_2", &[Coin::new(12345, "uxyz")]), + ExecuteMsg::Bond { + receiver: Some("user_3".to_string()), + exec_msg: None, + }, + ) + .unwrap(); + + assert_eq!(res.messages.len(), 3); + assert_eq!( + res.messages[0], + SubMsg::reply_on_success(Delegation::new("charlie", 12345, "uxyz").to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS) + ); + let mint_msg = >::into(MsgMint { + sender: "cosmos2contract".to_string(), + amount: Some(denom::Coin { + denom: "factory/cosmos2contract/boneXYZ".to_string(), + amount: Uint128::new(12_043).to_string(), + }), + }); + assert_eq!( + res.messages[1], + SubMsg { + id: 0, + msg: mint_msg, + gas_limit: None, + reply_on: ReplyOn::Never, + } + ); + + assert_eq!( + res.messages[2], + SubMsg { + id: 0, + msg: CosmosMsg::Bank(BankMsg::Send { + to_address: "user_3".to_string(), + + amount: vec![Coin { denom: "factory/cosmos2contract/boneXYZ".to_string(), amount: Uint128::new(12_043) }], + }), + gas_limit: None, + reply_on: ReplyOn::Never, + } + ); + + // Check the state after bonding + deps.querier.set_staking_delegations(&[ + Delegation::new("alice", 341667, "uxyz"), + Delegation::new("bob", 341667, "uxyz"), + Delegation::new("charlie", 354011, "uxyz"), + ]); + + let res: StateResponse = query_helper(deps.as_ref(), QueryMsg::State {}); + assert_eq!( + res, + StateResponse { + total_usteak: Uint128::new(1012043), + total_native: Uint128::new(1037345), + exchange_rate: Decimal::from_ratio(1037345u128, 1012043u128), + unlocked_coins: vec![], + } + ); +} + +#[test] +fn harvesting() { + let mut deps = setup_test(); + + // Assume users have bonded a total of 1,000,000 uluna and minted the same amount of usteak + deps.querier.set_staking_delegations(&[ + Delegation::new("alice", 341667, "uxyz"), + Delegation::new("bob", 341667, "uxyz"), + Delegation::new("charlie", 341666, "uxyz"), + ]); + + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info("worker", &[]), + ExecuteMsg::Harvest {}, + ) + .unwrap(); + + assert_eq!(res.messages.len(), 4); + assert_eq!( + res.messages[0], + SubMsg::reply_on_success( + CosmosMsg::Distribution(DistributionMsg::WithdrawDelegatorReward { + validator: "alice".to_string(), + }), + REPLY_REGISTER_RECEIVED_COINS, + ) + ); + assert_eq!( + res.messages[1], + SubMsg::reply_on_success( + CosmosMsg::Distribution(DistributionMsg::WithdrawDelegatorReward { + validator: "bob".to_string(), + }), + REPLY_REGISTER_RECEIVED_COINS, + ) + ); + assert_eq!( + res.messages[2], + SubMsg::reply_on_success( + CosmosMsg::Distribution(DistributionMsg::WithdrawDelegatorReward { + validator: "charlie".to_string(), + }), + REPLY_REGISTER_RECEIVED_COINS, + ) + ); + assert_eq!( + res.messages[3], + SubMsg { + id: 0, + msg: CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: MOCK_CONTRACT_ADDR.to_string(), + msg: to_binary(&ExecuteMsg::Callback(CallbackMsg::Reinvest {})).unwrap(), + funds: vec![], + }), + gas_limit: None, + reply_on: ReplyOn::Never, + } + ); +} + + +#[test] +fn reinvesting() { + let mut deps = setup_test(); + let state = State::default(); + + deps.querier.set_staking_delegations(&[ + Delegation::new("alice", 333334, "uxyz"), + Delegation::new("bob", 333333, "uxyz"), + Delegation::new("charlie", 333333, "uxyz"), + ]); + state.prev_denom.save(deps.as_mut().storage, &Uint128::from(0 as u32)).unwrap(); + deps.querier.set_bank_balances(&[Coin::new(234u128, "uxyz")]); + + // After the swaps, `unlocked_coins` should contain only uxyz and unknown denoms + state + .unlocked_coins + .save( + deps.as_mut().storage, + &vec![ + Coin::new(234, "uxyz"), + Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + ), + ], + ) + .unwrap(); + + // Bob has the smallest amount of delegations, so all proceeds go to him + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(MOCK_CONTRACT_ADDR, &[]), + ExecuteMsg::Callback(CallbackMsg::Reinvest {}), + ) + .unwrap(); + + assert_eq!(res.messages.len(), 2); + assert_eq!( + res.messages[0], + SubMsg { + id: 0, + msg: Delegation::new("bob", 234 - 23, "uxyz").to_cosmos_msg(), + gas_limit: None, + reply_on: ReplyOn::Never, + } + ); + let send_msg = BankMsg::Send { + to_address: "the_fee_man".into(), + amount: vec![Coin::new(23u128, "uxyz")], + }; + assert_eq!( + res.messages[1], + SubMsg { + id: 0, + msg: CosmosMsg::Bank(send_msg), + gas_limit: None, + reply_on: ReplyOn::Never, + } + ); + + // Storage should have been updated + let unlocked_coins = state.unlocked_coins.load(deps.as_ref().storage).unwrap(); + assert_eq!( + unlocked_coins, + vec![Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + )], + ); +} + +#[test] +fn reinvesting_fee_split() { + let mut deps = setup_test_fee_split(); + let state = State::default(); + + deps.querier.set_staking_delegations(&[ + Delegation::new("alice", 333334, "uxyz"), + Delegation::new("bob", 333333, "uxyz"), + Delegation::new("charlie", 333333, "uxyz"), + ]); + state.prev_denom.save(deps.as_mut().storage, &Uint128::from(0 as u32)).unwrap(); + deps.querier.set_bank_balances(&[Coin::new(234u128, "uxyz")]); + + // After the swaps, `unlocked_coins` should contain only uxyz and unknown denoms + state + .unlocked_coins + .save( + deps.as_mut().storage, + &vec![ + Coin::new(234, "uxyz"), + Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + ), + ], + ) + .unwrap(); + + // Bob has the smallest amount of delegations, so all proceeds go to him + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(MOCK_CONTRACT_ADDR, &[]), + ExecuteMsg::Callback(CallbackMsg::Reinvest {}), + ) + .unwrap(); + + assert_eq!(res.messages.len(), 2); + assert_eq!( + res.messages[0], + SubMsg { + id: 0, + msg: Delegation::new("bob", 234 - 23, "uxyz").to_cosmos_msg(), + gas_limit: None, + reply_on: ReplyOn::Never, + } + ); + let send_msg = pfc_fee_split::fee_split_msg::ExecuteMsg::Deposit { flush: false }; + + assert_eq!( + res.messages[1], + SubMsg { + id: 0, + msg: send_msg.into_cosmos_msg("fee_split_contract", vec![Coin::new(23u128, "uxyz")]).unwrap(), + gas_limit: None, + reply_on: ReplyOn::Never, + } + ); + + // Storage should have been updated + let unlocked_coins = state.unlocked_coins.load(deps.as_ref().storage).unwrap(); + assert_eq!( + unlocked_coins, + vec![Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + )], + ); +} + +#[test] +fn queuing_unbond() { + let mut deps = setup_test(); + let state = State::default(); + + // Only Steak token is accepted for unbonding requests + let err = execute( + deps.as_mut(), + mock_env(), + mock_info("random_token", &[Coin { denom: "asldkj".into(), amount: Uint128::new(69_420) }]), + ExecuteMsg::Unbond { receiver: None }, + ) + .unwrap_err(); + + assert_eq!( + err, + StdError::generic_err("you can only send factory/cosmos2contract/boneXYZ tokens to unbond") + ); + + // User 1 creates an unbonding request before `est_unbond_start_time` is reached. The unbond + // request is saved, but not the pending batch is not submitted for unbonding + let res = execute( + deps.as_mut(), + mock_env_at_timestamp(12345), // est_unbond_start_time = 269200 + mock_info("steak_token", &[Coin { denom: "factory/cosmos2contract/boneXYZ".into(), amount: Uint128::new(23_456) }]), + ExecuteMsg::Unbond { + receiver: Some("user_1".to_string()), + }, + ) + .unwrap(); + + assert_eq!(res.messages.len(), 0); + + // User 2 creates an unbonding request after `est_unbond_start_time` is reached. The unbond + // request is saved, and the pending is automatically submitted for unbonding + let res = execute( + deps.as_mut(), + mock_env_at_timestamp(269201), // est_unbond_start_time = 269200 + mock_info("user_3", &[Coin { denom: "factory/cosmos2contract/boneXYZ".into(), amount: Uint128::new(69_420) }]), + ExecuteMsg::Unbond { receiver: None }, + ) + .unwrap(); + + assert_eq!(res.messages.len(), 1); + assert_eq!( + res.messages[0], + SubMsg { + id: 0, + msg: CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: MOCK_CONTRACT_ADDR.to_string(), + msg: to_binary(&ExecuteMsg::SubmitBatch {}).unwrap(), + funds: vec![], + }), + gas_limit: None, + reply_on: ReplyOn::Never, + } + ); + + // The users' unbonding requests should have been saved + let ubr1 = unbond_requests() + .load( + deps.as_ref().storage, + (1u64.into(), &Addr::unchecked("user_1").to_string()), + ) + .unwrap(); + let ubr2 = unbond_requests() + .load( + deps.as_ref().storage, + (1u64.into(), &Addr::unchecked("user_3").to_string()), + ) + .unwrap(); + + assert_eq!( + ubr1, + UnbondRequest { + id: 1, + user: Addr::unchecked("user_1"), + shares: Uint128::new(23456), + } + ); + assert_eq!( + ubr2, + UnbondRequest { + id: 1, + user: Addr::unchecked("user_3"), + shares: Uint128::new(69420), + } + ); + + // Pending batch should have been updated + let pending_batch = state.pending_batch.load(deps.as_ref().storage).unwrap(); + assert_eq!( + pending_batch, + PendingBatch { + id: 1, + usteak_to_burn: Uint128::new(92876), // 23,456 + 69,420 + est_unbond_start_time: 269200, + } + ); +} + +#[test] +fn submitting_batch() { + let mut deps = setup_test(); + let state = State::default(); + + // uluna bonded: 1,037,345 + // usteak supply: 1,012,043 + // uluna per ustake: 1.025 + deps.querier.set_staking_delegations(&[ + Delegation::new("alice", 345_782, "uxyz"), + Delegation::new("bob", 345_782, "uxyz"), + Delegation::new("charlie", 345_781, "uxyz"), + ]); + state.steak_minted.save(deps.as_mut().storage, &Uint128::new(1_012_043)).unwrap(); + + + // We continue from the contract state at the end of the last test + let unbond_reqs = vec![ + UnbondRequest { + id: 1, + user: Addr::unchecked("user_1"), + shares: Uint128::new(23456), + }, + UnbondRequest { + id: 1, + user: Addr::unchecked("user_3"), + shares: Uint128::new(69420), + }, + ]; + + for unbond_request in &unbond_reqs { + unbond_requests() + .save( + deps.as_mut().storage, + ( + unbond_request.id.into(), + &Addr::unchecked(unbond_request.user.clone()).as_str(), + ), + unbond_request, + ) + .unwrap(); + } + + state + .pending_batch + .save( + deps.as_mut().storage, + &PendingBatch { + id: 1, + usteak_to_burn: Uint128::new(92876), // 23,456 + 69,420 + est_unbond_start_time: 269200, + }, + ) + .unwrap(); + + // Anyone can invoke `submit_batch`. Here we continue from the previous test and assume it is + // invoked automatically as user 2 submits the unbonding request + // + // usteak to burn: 23,456 + 69,420 = 92,876 + // uluna to unbond: 1,037,345 * 92,876 / 1,012,043 = 95,197 + // + // Target: (1,037,345 - 95,197) / 3 = 314,049 + // Remainer: 1 + // Alice: 345,782 - (314,049 + 1) = 31,732 + // Bob: 345,782 - (314,049 + 0) = 31,733 + // Charlie: 345,781 - (314,049 + 0) = 31,732 + let res = execute( + deps.as_mut(), + mock_env_at_timestamp(269_201), + mock_info(MOCK_CONTRACT_ADDR, &[]), + ExecuteMsg::SubmitBatch {}, + ) + .unwrap(); + + assert_eq!(res.messages.len(), 4); + assert_eq!( + res.messages[0], + SubMsg::reply_on_success(Undelegation::new("alice", 31732, "uxyz").to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS) + ); + assert_eq!( + res.messages[1], + SubMsg::reply_on_success(Undelegation::new("bob", 31733, "uxyz").to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS) + ); + assert_eq!( + res.messages[2], + SubMsg::reply_on_success( + Undelegation::new("charlie", 31732, "uxyz").to_cosmos_msg(), + REPLY_REGISTER_RECEIVED_COINS, + ) + ); + + let burn_msg = >::into(MsgBurn { + sender: "cosmos2contract".to_string(), + amount: Some(denom::Coin { + denom: "factory/cosmos2contract/boneXYZ".to_string(), + amount: Uint128::new(92_876).to_string(), + }), + }); + assert_eq!( + res.messages[3], + + SubMsg { + id: 0, + msg: burn_msg, + gas_limit: None, + reply_on: ReplyOn::Never, + } + ); + + // A new pending batch should have been created + let pending_batch = state.pending_batch.load(deps.as_ref().storage).unwrap(); + assert_eq!( + pending_batch, + PendingBatch { + id: 2, + usteak_to_burn: Uint128::zero(), + est_unbond_start_time: 528401, // 269,201 + 259,200 + } + ); + + // Previous batch should have been updated + let previous_batch = previous_batches() + .load(deps.as_ref().storage, 1u64.into()) + .unwrap(); + assert_eq!( + previous_batch, + Batch { + id: 1, + reconciled: false, + total_shares: Uint128::new(92876), + amount_unclaimed: Uint128::new(95197), + est_unbond_end_time: 2083601, // 269,201 + 1,814,400 + } + ); +} + +#[test] +fn reconciling() { + let mut deps = setup_test(); + let state = State::default(); + + let previous_batch_list = vec![ + Batch { + id: 1, + reconciled: true, + total_shares: Uint128::new(92876), + amount_unclaimed: Uint128::new(95197), // 1.025 Luna per Steak + est_unbond_end_time: 10000, + }, + Batch { + id: 2, + reconciled: false, + total_shares: Uint128::new(1345), + amount_unclaimed: Uint128::new(1385), // 1.030 Luna per Steak + est_unbond_end_time: 20000, + }, + Batch { + id: 3, + reconciled: false, + total_shares: Uint128::new(1456), + amount_unclaimed: Uint128::new(1506), // 1.035 Luna per Steak + est_unbond_end_time: 30000, + }, + Batch { + id: 4, + reconciled: false, + total_shares: Uint128::new(1567), + amount_unclaimed: Uint128::new(1629), // 1.040 Luna per Steak + est_unbond_end_time: 40000, // not yet finished unbonding, ignored + }, + ]; + + for previous_batch in &previous_batch_list { + previous_batches() + .save( + deps.as_mut().storage, + previous_batch.id.into(), + previous_batch, + ) + .unwrap(); + } + + state + .unlocked_coins + .save( + deps.as_mut().storage, + &vec![ + Coin::new(10000, "uxyz"), + Coin::new(234, "ukrw"), + Coin::new(345, "uusd"), + Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + ), + ], + ) + .unwrap(); + + deps.querier.set_bank_balances(&[ + Coin::new(12345, "uxyz"), + Coin::new(234, "ukrw"), + Coin::new(345, "uusd"), + Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + ), + ]); + + execute( + deps.as_mut(), + mock_env_at_timestamp(35000), + mock_info("worker", &[]), + ExecuteMsg::Reconcile {}, + ) + .unwrap(); + + // Expected received: batch 2 + batch 3 = 1385 + 1506 = 2891 + // Expected unlocked: 10000 + // Expected: 12891 + // Actual: 12345 + // Shortfall: 12891 - 12345 = 456 + // + // uluna per batch: 546 / 2 = 273 + // remainder: 0 + // batch 2: 1385 - 273 = 1112 + // batch 3: 1506 - 273 = 1233 + let batch = previous_batches() + .load(deps.as_ref().storage, 2u64.into()) + .unwrap(); + assert_eq!( + batch, + Batch { + id: 2, + reconciled: true, + total_shares: Uint128::new(1345), + amount_unclaimed: Uint128::new(1112), // 1385 - 273 + est_unbond_end_time: 20000, + } + ); + + let batch = previous_batches() + .load(deps.as_ref().storage, 3u64.into()) + .unwrap(); + assert_eq!( + batch, + Batch { + id: 3, + reconciled: true, + total_shares: Uint128::new(1456), + amount_unclaimed: Uint128::new(1233), // 1506 - 273 + est_unbond_end_time: 30000, + } + ); + + // Batches 1 and 4 should not have changed + let batch = previous_batches() + .load(deps.as_ref().storage, 1u64.into()) + .unwrap(); + assert_eq!(batch, previous_batch_list[0]); + + let batch = previous_batches() + .load(deps.as_ref().storage, 4u64.into()) + .unwrap(); + assert_eq!(batch, previous_batch_list[3]); +} + +#[test] +fn withdrawing_unbonded() { + let mut deps = setup_test(); + let state = State::default(); + + // We simulate a most general case: + // - batches 1 and 2 have finished unbonding + // - batch 3 have been submitted for unbonding but have not finished + // - batch 4 is still pending + let unbond_reqs = vec![ + UnbondRequest { + id: 1, + user: Addr::unchecked("user_1"), + shares: Uint128::new(23456), + }, + UnbondRequest { + id: 1, + user: Addr::unchecked("user_3"), + shares: Uint128::new(69420), + }, + UnbondRequest { + id: 2, + user: Addr::unchecked("user_1"), + shares: Uint128::new(34567), + }, + UnbondRequest { + id: 3, + user: Addr::unchecked("user_1"), + shares: Uint128::new(45678), + }, + UnbondRequest { + id: 4, + user: Addr::unchecked("user_1"), + shares: Uint128::new(56789), + }, + ]; + + for unbond_request in &unbond_reqs { + unbond_requests() + .save( + deps.as_mut().storage, + ( + unbond_request.id.into(), + &Addr::unchecked(unbond_request.user.clone()).as_str(), + ), + unbond_request, + ) + .unwrap(); + } + + let previous_batch_list = vec![ + Batch { + id: 1, + reconciled: true, + total_shares: Uint128::new(92876), + amount_unclaimed: Uint128::new(95197), // 1.025 Luna per Steak + est_unbond_end_time: 10000, + }, + Batch { + id: 2, + reconciled: true, + total_shares: Uint128::new(34567), + amount_unclaimed: Uint128::new(35604), // 1.030 Luna per Steak + est_unbond_end_time: 20000, + }, + Batch { + id: 3, + reconciled: false, // finished unbonding, but not reconciled; ignored + total_shares: Uint128::new(45678), + amount_unclaimed: Uint128::new(47276), // 1.035 Luna per Steak + est_unbond_end_time: 20000, + }, + Batch { + id: 4, + reconciled: true, + total_shares: Uint128::new(56789), + amount_unclaimed: Uint128::new(59060), // 1.040 Luna per Steak + est_unbond_end_time: 30000, // reconciled, but not yet finished unbonding; ignored + }, + ]; + + for previous_batch in &previous_batch_list { + previous_batches() + .save( + deps.as_mut().storage, + previous_batch.id.into(), + previous_batch, + ) + .unwrap(); + } + + state + .pending_batch + .save( + deps.as_mut().storage, + &PendingBatch { + id: 4, + usteak_to_burn: Uint128::new(56789), + est_unbond_start_time: 100000, + }, + ) + .unwrap(); + + // Attempt to withdraw before any batch has completed unbonding. Should error + let err = execute( + deps.as_mut(), + mock_env_at_timestamp(5000), + mock_info("user_1", &[]), + ExecuteMsg::WithdrawUnbonded { receiver: None }, + ) + .unwrap_err(); + + assert_eq!(err, StdError::generic_err("withdrawable amount is zero")); + + // Attempt to withdraw once batches 1 and 2 have finished unbonding, but 3 has not yet + // + // Withdrawable from batch 1: 95,197 * 23,456 / 92,876 = 24,042 + // Withdrawable from batch 2: 35,604 + // Total withdrawable: 24,042 + 35,604 = 59,646 + // + // Batch 1 should be updated: + // Total shares: 92,876 - 23,456 = 69,420 + // Unclaimed uluna: 95,197 - 24,042 = 71,155 + // + // Batch 2 is completely withdrawn, should be purged from storage + let res = execute( + deps.as_mut(), + mock_env_at_timestamp(25000), + mock_info("user_1", &[]), + ExecuteMsg::WithdrawUnbonded { receiver: None }, + ) + .unwrap(); + + assert_eq!(res.messages.len(), 1); + assert_eq!( + res.messages[0], + SubMsg { + id: 0, + msg: CosmosMsg::Bank(BankMsg::Send { + to_address: "user_1".to_string(), + amount: vec![Coin::new(59646, "uxyz")], + }), + gas_limit: None, + reply_on: ReplyOn::Never, + } + ); + + // Previous batches should have been updated + let batch = previous_batches() + .load(deps.as_ref().storage, 1u64.into()) + .unwrap(); + assert_eq!( + batch, + Batch { + id: 1, + reconciled: true, + total_shares: Uint128::new(69420), + amount_unclaimed: Uint128::new(71155), + est_unbond_end_time: 10000, + } + ); + + let err = previous_batches() + .load(deps.as_ref().storage, 2u64.into()) + .unwrap_err(); + assert_eq!( + err, + StdError::NotFound { + kind: "pfc_steak::hub::Batch".to_string() + } + ); + + // User 1's unbond requests in batches 1 and 2 should have been deleted + let err1 = unbond_requests() + .load( + deps.as_ref().storage, + (1u64.into(), &Addr::unchecked("user_1").as_str()), + ) + .unwrap_err(); + let err2 = unbond_requests() + .load( + deps.as_ref().storage, + (1u64.into(), &Addr::unchecked("user_1").as_str()), + ) + .unwrap_err(); + + assert_eq!( + err1, + StdError::NotFound { + kind: "pfc_steak::hub::UnbondRequest".to_string() + } + ); + assert_eq!( + err2, + StdError::NotFound { + kind: "pfc_steak::hub::UnbondRequest".to_string() + } + ); + + // User 3 attempt to withdraw; also specifying a receiver + let res = execute( + deps.as_mut(), + mock_env_at_timestamp(25000), + mock_info("user_3", &[]), + ExecuteMsg::WithdrawUnbonded { + receiver: Some("user_2".to_string()), + }, + ) + .unwrap(); + + assert_eq!(res.messages.len(), 1); + assert_eq!( + res.messages[0], + SubMsg { + id: 0, + msg: CosmosMsg::Bank(BankMsg::Send { + to_address: "user_2".to_string(), + amount: vec![Coin::new(71155, "uxyz")], + }), + gas_limit: None, + reply_on: ReplyOn::Never, + } + ); + + // Batch 1 and user 2's unbonding request should have been purged from storage + let err = previous_batches() + .load(deps.as_ref().storage, 1u64.into()) + .unwrap_err(); + assert_eq!( + err, + StdError::NotFound { + kind: "pfc_steak::hub::Batch".to_string() + } + ); + + let err = unbond_requests() + .load( + deps.as_ref().storage, + (1u64.into(), &Addr::unchecked("user_3").as_str()), + ) + .unwrap_err(); + + assert_eq!( + err, + StdError::NotFound { + kind: "pfc_steak::hub::UnbondRequest".to_string() + } + ); +} + +#[test] +fn adding_validator() { + let mut deps = setup_test(); + //let state = State::default(); + + let err = execute( + deps.as_mut(), + mock_env(), + mock_info("jake", &[]), + ExecuteMsg::AddValidator { + validator: "dave".to_string(), + }, + ) + .unwrap_err(); + + assert_eq!( + err, + StdError::generic_err("unauthorized: sender is not owner") + ); + + let err = execute( + deps.as_mut(), + mock_env(), + mock_info("larry", &[]), + ExecuteMsg::AddValidator { + validator: "alice".to_string(), + }, + ) + .unwrap_err(); + + assert_eq!( + err, + StdError::generic_err("validator is already whitelisted") + ); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info("larry", &[]), + ExecuteMsg::AddValidator { + validator: "dave".to_string(), + }, + ) + .unwrap(); + + assert_eq!(res.messages.len(), 0); + + assert!(VALIDATORS.contains(deps.as_ref().storage, "alice")); + assert!(VALIDATORS.contains(deps.as_ref().storage, "bob")); + assert!(VALIDATORS.contains(deps.as_ref().storage, "charlie")); + assert!(VALIDATORS.contains(deps.as_ref().storage, "dave")); + assert_eq!(VALIDATORS.count(deps.as_ref().storage).unwrap(), 4); +} + +#[test] +fn removing_validator() { + let mut deps = setup_test(); + //let state = State::default(); + + deps.querier.set_staking_delegations(&[ + Delegation::new("alice", 341667, "uxyz"), + Delegation::new("bob", 341667, "uxyz"), + Delegation::new("charlie", 341666, "uxyz"), + ]); + + let err = execute( + deps.as_mut(), + mock_env(), + mock_info("jake", &[]), + ExecuteMsg::RemoveValidator { + validator: "charlie".to_string(), + }, + ) + .unwrap_err(); + + assert_eq!( + err, + StdError::generic_err("unauthorized: sender is not owner") + ); + + let err = execute( + deps.as_mut(), + mock_env(), + mock_info("larry", &[]), + ExecuteMsg::RemoveValidator { + validator: "dave".to_string(), + }, + ) + .unwrap_err(); + + assert_eq!( + err, + StdError::generic_err("validator is not already whitelisted") + ); + + // Target: (341667 + 341667 + 341666) / 2 = 512500 + // Remainder: 0 + // Alice: 512500 + 0 - 341667 = 170833 + // Bob: 512500 + 0 - 341667 = 170833 + let res = execute( + deps.as_mut(), + mock_env(), + mock_info("larry", &[]), + ExecuteMsg::RemoveValidator { + validator: "charlie".to_string(), + }, + ) + .unwrap(); + + assert_eq!(res.messages.len(), 2); + assert_eq!( + res.messages[0], + SubMsg::reply_on_success( + Redelegation::new("charlie", "alice", 170833, "uxyz").to_cosmos_msg(), + REPLY_REGISTER_RECEIVED_COINS, + ), + ); + assert_eq!( + res.messages[1], + SubMsg::reply_on_success( + Redelegation::new("charlie", "bob", 170833, "uxyz").to_cosmos_msg(), + REPLY_REGISTER_RECEIVED_COINS, + ), + ); + + assert!(VALIDATORS.contains(deps.as_ref().storage, "alice")); + assert!(VALIDATORS.contains(deps.as_ref().storage, "bob")); + assert_eq!(VALIDATORS.count(deps.as_ref().storage).unwrap(), 2); +} + +#[test] +fn transferring_ownership() { + let mut deps = setup_test(); + let state = State::default(); + + let err = execute( + deps.as_mut(), + mock_env(), + mock_info("jake", &[]), + ExecuteMsg::TransferOwnership { + new_owner: "jake".to_string(), + }, + ) + .unwrap_err(); + + assert_eq!( + err, + StdError::generic_err("unauthorized: sender is not owner") + ); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info("larry", &[]), + ExecuteMsg::TransferOwnership { + new_owner: "jake".to_string(), + }, + ) + .unwrap(); + + assert_eq!(res.messages.len(), 0); + + let owner = state.owner.load(deps.as_ref().storage).unwrap(); + assert_eq!(owner, Addr::unchecked("larry")); + + let err = execute( + deps.as_mut(), + mock_env(), + mock_info("pumpkin", &[]), + ExecuteMsg::AcceptOwnership {}, + ) + .unwrap_err(); + + assert_eq!( + err, + StdError::generic_err("unauthorized: sender is not new owner") + ); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info("jake", &[]), + ExecuteMsg::AcceptOwnership {}, + ) + .unwrap(); + + assert_eq!(res.messages.len(), 0); + + let owner = state.owner.load(deps.as_ref().storage).unwrap(); + assert_eq!(owner, Addr::unchecked("jake")); +} + +#[test] +fn splitting_fees() { + let mut deps = setup_test(); + + + let err = execute( + deps.as_mut(), + mock_env(), + mock_info("jake", &[]), + ExecuteMsg::TransferFeeAccount { + fee_account_type: "Wallet".to_string(), + new_fee_account: "charlie".to_string(), + }, + ) + .unwrap_err(); + + assert_eq!( + err, + StdError::generic_err("unauthorized: sender is not owner") + ); + + let err = execute( + deps.as_mut(), + mock_env(), + mock_info("larry", &[]), + ExecuteMsg::TransferFeeAccount { + fee_account_type: "xxxx".to_string(), + new_fee_account: "charlie".to_string(), + }, + ) + .unwrap_err(); + + assert_eq!( + err, + StdError::generic_err("Invalid Fee type: Wallet or FeeSplit only") + ); + + execute( + deps.as_mut(), + mock_env(), + mock_info("larry", &[]), + ExecuteMsg::TransferFeeAccount { + fee_account_type: "Wallet".to_string(), + new_fee_account: "charlie".to_string(), + }, + ) + .unwrap(); + let res: ConfigResponse = query_helper(deps.as_ref(), QueryMsg::Config {}); + assert_eq!( + res, + ConfigResponse { + owner: "larry".to_string(), + new_owner: None, + steak_token: "factory/cosmos2contract/boneXYZ".to_string(), + epoch_period: 259200, + unbond_period: 1814400, + denom: "uxyz".to_string(), + fee_type: "Wallet".to_string(), + fee_account: "charlie".to_string(), + fee_rate: Decimal::from_ratio(10_u128, 100_u128), + max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), + validators: vec![ + "alice".to_string(), + "bob".to_string(), + "charlie".to_string(), + ], + paused_validators: vec![], + dust_collector:Some("dusty_1".to_string()) + } + ); + + + execute( + deps.as_mut(), + mock_env(), + mock_info("larry", &[]), + ExecuteMsg::TransferFeeAccount { + fee_account_type: "FeeSplit".to_string(), + new_fee_account: "contract".to_string(), + }, + ) + .unwrap(); + let res: ConfigResponse = query_helper(deps.as_ref(), QueryMsg::Config {}); + assert_eq!( + res, + ConfigResponse { + owner: "larry".to_string(), + new_owner: None, + steak_token: "factory/cosmos2contract/boneXYZ".to_string(), + epoch_period: 259200, + unbond_period: 1814400, + denom: "uxyz".to_string(), + fee_type: "FeeSplit".to_string(), + fee_account: "contract".to_string(), + fee_rate: Decimal::from_ratio(10_u128, 100_u128), + max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), + validators: vec![ + "alice".to_string(), + "bob".to_string(), + "charlie".to_string(), + ], + paused_validators: vec![], + dust_collector: Some("dusty_1".to_string()) + } + ); +} +//-------------------------------------------------------------------------------------------------- +// Queries +//-------------------------------------------------------------------------------------------------- + +#[test] +fn querying_previous_batches() { + let mut deps = mock_dependencies(); + + let batches = vec![ + Batch { + id: 1, + reconciled: false, + total_shares: Uint128::new(123), + amount_unclaimed: Uint128::new(678), + est_unbond_end_time: 10000, + }, + Batch { + id: 2, + reconciled: true, + total_shares: Uint128::new(234), + amount_unclaimed: Uint128::new(789), + est_unbond_end_time: 15000, + }, + Batch { + id: 3, + reconciled: false, + total_shares: Uint128::new(345), + amount_unclaimed: Uint128::new(890), + est_unbond_end_time: 20000, + }, + Batch { + id: 4, + reconciled: true, + total_shares: Uint128::new(456), + amount_unclaimed: Uint128::new(999), + est_unbond_end_time: 25000, + }, + ]; + + //let state = State::default(); + for batch in &batches { + previous_batches() + .save(deps.as_mut().storage, batch.id.into(), batch) + .unwrap(); + } + + // Querying a single batch + let res: Batch = query_helper(deps.as_ref(), QueryMsg::PreviousBatch(1)); + assert_eq!(res, batches[0].clone()); + + let res: Batch = query_helper(deps.as_ref(), QueryMsg::PreviousBatch(2)); + assert_eq!(res, batches[1].clone()); + + // Query multiple batches + let res: Vec = query_helper( + deps.as_ref(), + QueryMsg::PreviousBatches { + start_after: None, + limit: None, + }, + ); + assert_eq!(res, batches.clone()); + + let res: Vec = query_helper( + deps.as_ref(), + QueryMsg::PreviousBatches { + start_after: Some(1), + limit: None, + }, + ); + assert_eq!( + res, + vec![batches[1].clone(), batches[2].clone(), batches[3].clone()] + ); + + let res: Vec = query_helper( + deps.as_ref(), + QueryMsg::PreviousBatches { + start_after: Some(4), + limit: None, + }, + ); + assert_eq!(res, vec![]); + + // Query multiple batches, indexed by whether it has been reconciled + let res = previous_batches() + .idx + .reconciled + .prefix("true".into()) + .range(deps.as_ref().storage, None, None, Order::Ascending) + .map(|item| { + let (_, v) = item.unwrap(); + v + }) + .collect::>(); + + assert_eq!(res, vec![batches[1].clone(), batches[3].clone()]); + + let res = previous_batches() + .idx + .reconciled + .prefix("false".into()) + .range(deps.as_ref().storage, None, None, Order::Ascending) + .map(|item| { + let (_, v) = item.unwrap(); + v + }) + .collect::>(); + + assert_eq!(res, vec![batches[0].clone(), batches[2].clone()]); +} + +#[test] +fn querying_unbond_requests() { + let mut deps = mock_dependencies(); + //let state = State::default(); + + let unbond_reqs = vec![ + UnbondRequest { + id: 1, + user: Addr::unchecked("alice"), + shares: Uint128::new(123), + }, + UnbondRequest { + id: 1, + user: Addr::unchecked("bob"), + shares: Uint128::new(234), + }, + UnbondRequest { + id: 1, + user: Addr::unchecked("charlie"), + shares: Uint128::new(345), + }, + UnbondRequest { + id: 2, + user: Addr::unchecked("alice"), + shares: Uint128::new(456), + }, + ]; + + for unbond_request in &unbond_reqs { + unbond_requests() + .save( + deps.as_mut().storage, + ( + unbond_request.id.into(), + &Addr::unchecked(unbond_request.user.clone()).as_str(), + ), + unbond_request, + ) + .unwrap(); + } + + let res: Vec = query_helper( + deps.as_ref(), + QueryMsg::UnbondRequestsByBatch { + id: 1, + start_after: None, + limit: None, + }, + ); + assert_eq!( + res, + vec![ + unbond_reqs[0].clone().into(), + unbond_reqs[1].clone().into(), + unbond_reqs[2].clone().into(), + ] + ); + + let res: Vec = query_helper( + deps.as_ref(), + QueryMsg::UnbondRequestsByBatch { + id: 2, + start_after: None, + limit: None, + }, + ); + assert_eq!(res, vec![unbond_reqs[3].clone().into()]); + + let res: Vec = query_helper( + deps.as_ref(), + QueryMsg::UnbondRequestsByUser { + user: "alice".to_string(), + start_after: None, + limit: None, + }, + ); + assert_eq!( + res, + vec![ + unbond_reqs[0].clone().into(), + unbond_reqs[3].clone().into(), + ] + ); + /* + for x in unbond_requests().range(deps.as_ref().storage, None, None, Order::Ascending) { + let rec = x.unwrap(); + eprintln!("Key {}/{} = {:?}", rec.0.0, rec.0.1, rec.1) + } +*/ + let res: Vec = query_helper( + deps.as_ref(), + QueryMsg::UnbondRequestsByUser { + user: "alice".to_string(), + start_after: Some(1), + limit: None, + }, + ); + assert_eq!(res, vec![unbond_reqs[3].clone().into()]); +} + +//-------------------------------------------------------------------------------------------------- +// Delegations +//-------------------------------------------------------------------------------------------------- + +#[test] +fn computing_undelegations() { + let current_delegations = vec![ + Delegation::new("alice", 400, "uxyz"), + Delegation::new("bob", 300, "uxyz"), + Delegation::new("charlie", 200, "uxyz"), + ]; + + // Target: (400 + 300 + 200 - 451) / 3 = 149 + // Remainder: 2 + // Alice: 400 - (149 + 1) = 250 + // Bob: 300 - (149 + 1) = 150 + // Charlie: 200 - (149 + 0) = 51 + let new_undelegations = compute_undelegations(Uint128::new(451), ¤t_delegations, "uxyz"); + let expected = vec![ + Undelegation::new("alice", 250, "uxyz"), + Undelegation::new("bob", 150, "uxyz"), + Undelegation::new("charlie", 51, "uxyz"), + ]; + assert_eq!(new_undelegations, expected); +} + +#[test] +fn computing_redelegations_for_removal() { + let current_delegations = vec![ + Delegation::new("alice", 13000, "uxyz"), + Delegation::new("bob", 12000, "uxyz"), + Delegation::new("charlie", 11000, "uxyz"), + Delegation::new("dave", 10000, "uxyz"), + ]; + + // Suppose Dave will be removed + // uluna_per_validator = (13000 + 12000 + 11000 + 10000) / 3 = 15333 + // remainder = 1 + // to Alice: 15333 + 1 - 13000 = 2334 + // to Bob: 15333 + 0 - 12000 = 3333 + // to Charlie: 15333 + 0 - 11000 = 4333 + let expected = vec![ + Redelegation::new("dave", "alice", 2334, "uxyz"), + Redelegation::new("dave", "bob", 3333, "uxyz"), + Redelegation::new("dave", "charlie", 4333, "uxyz"), + ]; + + assert_eq!( + compute_redelegations_for_removal( + ¤t_delegations[3], + ¤t_delegations[..3], + "uxyz", + ), + expected, + ); +} + +#[test] +fn computing_redelegations_for_rebalancing() { + let current_delegations = vec![ + Delegation::new("alice", 69420, "uxyz"), + Delegation::new("bob", 1234, "uxyz"), + Delegation::new("charlie", 88888, "uxyz"), + Delegation::new("dave", 40471, "uxyz"), + Delegation::new("evan", 2345, "uxyz"), + ]; + let active_validators: Vec = vec!["alice".to_string(), "bob".to_string(), + "charlie".to_string(), "dave".to_string(), + "evan".to_string()]; + // uluna_per_validator = (69420 + 88888 + 1234 + 40471 + 2345) / 4 = 40471 + // remainer = 3 + // src_delegations: + // - alice: 69420 - (40471 + 1) = 28948 + // - charlie: 88888 - (40471 + 1) = 48416 + // dst_delegations: + // - bob: (40471 + 1) - 1234 = 39238 + // - evan: (40471 + 0) - 2345 = 38126 + // + // Round 1: alice --(28948)--> bob + // src_delegations: + // - charlie: 48416 + // dst_delegations: + // - bob: 39238 - 28948 = 10290 + // - evan: 38126 + // + // Round 2: charlie --(10290)--> bob + // src_delegations: + // - charlie: 48416 - 10290 = 38126 + // dst_delegations: + // - evan: 38126 + // + // Round 3: charlie --(38126)--> evan + // Queues are emptied + let expected = vec![ + Redelegation::new("alice", "bob", 28948, "uxyz"), + Redelegation::new("charlie", "bob", 10290, "uxyz"), + Redelegation::new("charlie", "evan", 38126, "uxyz"), + ]; + + assert_eq!( + compute_redelegations_for_rebalancing(active_validators, ¤t_delegations, Uint128::from(10 as u64)), + expected, + ); + + + let partially_active = vec!["alice".to_string(), + "charlie".to_string(), + "dave".to_string(), + "evan".to_string()]; + + let partially_expected = vec![ + Redelegation::new("alice", "dave", 10118, "uxyz"), + Redelegation::new("alice", "evan", 8712, "uxyz"), + Redelegation::new("charlie", "evan", 38299, "uxyz"), + ]; + assert_eq!( + compute_redelegations_for_rebalancing(partially_active.clone(), ¤t_delegations, Uint128::from(10 as u64)), + partially_expected, + ); + + let partially_expected_minimums = vec![ + Redelegation::new("alice", "evan", 18830, "uxyz"), + Redelegation::new("charlie", "evan", 29414, "uxyz"), + ]; + assert_eq!( + compute_redelegations_for_rebalancing(partially_active, ¤t_delegations, Uint128::from(15_000 as u64)), + partially_expected_minimums, + ); +} + +//-------------------------------------------------------------------------------------------------- +// Coins +//-------------------------------------------------------------------------------------------------- + +#[test] +fn parsing_coin() { + let coin = parse_coin("12345uatom").unwrap(); + assert_eq!(coin, Coin::new(12345, "uatom")); + + let coin = + parse_coin("23456ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B") + .unwrap(); + assert_eq!( + coin, + Coin::new( + 23456, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + ) + ); + + let err = parse_coin("69420").unwrap_err(); + assert_eq!(err, StdError::generic_err("failed to parse coin: 69420")); + + let err = parse_coin("ngmi").unwrap_err(); + assert_eq!( + err, + StdError::generic_err("Parsing u128: cannot parse integer from empty string") + ); +} + +#[test] +fn parsing_coins() { + let coins = Coins::from_str("").unwrap(); + assert_eq!(coins.0, vec![]); + + let coins = Coins::from_str("12345uatom").unwrap(); + assert_eq!(coins.0, vec![Coin::new(12345, "uatom")]); + + let coins = Coins::from_str("12345uatom,23456uxyz").unwrap(); + assert_eq!( + coins.0, + vec![Coin::new(12345, "uatom"), Coin::new(23456, "uxyz")] + ); +} + +#[test] +fn adding_coins() { + let mut coins = Coins(vec![]); + + coins.add(&Coin::new(12345, "uatom")).unwrap(); + assert_eq!(coins.0, vec![Coin::new(12345, "uatom")]); + + coins.add(&Coin::new(23456, "uxyz")).unwrap(); + assert_eq!( + coins.0, + vec![Coin::new(12345, "uatom"), Coin::new(23456, "uxyz")] + ); + + coins + .add_many(&Coins::from_str("76543uatom,69420uusd").unwrap()) + .unwrap(); + assert_eq!( + coins.0, + vec![ + Coin::new(88888, "uatom"), + Coin::new(23456, "uxyz"), + Coin::new(69420, "uusd"), + ] + ); +} + +#[test] +fn receiving_funds() { + let err = parse_received_fund(&[], "uxyz").unwrap_err(); + assert_eq!( + err, + StdError::generic_err("must deposit exactly one coin; received 0") + ); + + let err = parse_received_fund( + &[Coin::new(12345, "uatom"), Coin::new(23456, "uxyz")], + "uxyz", + ) + .unwrap_err(); + assert_eq!( + err, + StdError::generic_err("must deposit exactly one coin; received 2") + ); + + let err = parse_received_fund(&[Coin::new(12345, "uatom")], "uxyz").unwrap_err(); + assert_eq!( + err, + StdError::generic_err("expected uxyz deposit, received uatom") + ); + + let err = parse_received_fund(&[Coin::new(0, "uxyz")], "uxyz").unwrap_err(); + assert_eq!( + err, + StdError::generic_err("deposit amount must be non-zero") + ); + + let amount = parse_received_fund(&[Coin::new(69420, "uxyz")], "uxyz").unwrap(); + assert_eq!(amount, Uint128::new(69420)); +} + + +#[test] +fn reconciling_underflow() { + let mut deps = setup_test(); + let state = State::default(); + let previous_batch_list = vec![ + Batch { + id: 1, + reconciled: true, + total_shares: Uint128::new(92876), + amount_unclaimed: Uint128::new(95197), // 1.025 Token per Stake + est_unbond_end_time: 10000, + }, + Batch { + id: 2, + reconciled: false, + total_shares: Uint128::new(1345), + amount_unclaimed: Uint128::new(1385), // 1.030 Token per Stake + est_unbond_end_time: 20000, + }, + Batch { + id: 3, + reconciled: false, + total_shares: Uint128::new(1456), + amount_unclaimed: Uint128::new(1506), // 1.035 Token per Stake + est_unbond_end_time: 30000, + }, + Batch { + id: 4, + reconciled: false, + total_shares: Uint128::new(1), + amount_unclaimed: Uint128::new(1), + est_unbond_end_time: 30001, + }, + ]; + for previous_batch in &previous_batch_list { + previous_batches() + .save(deps.as_mut().storage, previous_batch.id, previous_batch) + .unwrap(); + } + state + .unlocked_coins + .save( + deps.as_mut().storage, + &vec![ + Coin::new(10000, "uatom"), + Coin::new(234, "ukrw"), + Coin::new(345, "uusd"), + Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + ), + ], + ) + .unwrap(); + deps.querier.set_bank_balances(&[ + Coin::new(12345, "uatom"), + Coin::new(234, "ukrw"), + Coin::new(345, "uusd"), + Coin::new(69420, "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B"), + ]); + execute( + deps.as_mut(), + mock_env_at_timestamp(35000), + mock_info("worker", &[]), + ExecuteMsg::Reconcile {}, + ) + .unwrap(); +} + +#[test] +fn reconciling_underflow_second() { + let mut deps = setup_test(); + let state = State::default(); + let previous_batch_list = vec![ + Batch { + id: 1, + reconciled: true, + total_shares: Uint128::new(92876), + amount_unclaimed: Uint128::new(95197), // 1.025 Token per Stake + est_unbond_end_time: 10000, + }, + Batch { + id: 2, + reconciled: false, + total_shares: Uint128::new(1345), + amount_unclaimed: Uint128::new(1385), // 1.030 Token per Stake + est_unbond_end_time: 20000, + }, + Batch { + id: 3, + reconciled: false, + total_shares: Uint128::new(176), + amount_unclaimed: Uint128::new(183), // 1.035 Token per Stake + est_unbond_end_time: 30000, + }, + Batch { + id: 4, + reconciled: false, + total_shares: Uint128::new(1), + amount_unclaimed: Uint128::new(1), + est_unbond_end_time: 30001, + }, + ]; + for previous_batch in &previous_batch_list { + previous_batches() + .save(deps.as_mut().storage, previous_batch.id, previous_batch) + .unwrap(); + } + state + .unlocked_coins + .save( + deps.as_mut().storage, + &vec![ + Coin::new(10000, "uatom"), + Coin::new(234, "ukrw"), + Coin::new(345, "uusd"), + Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + ), + ], + ) + .unwrap(); + deps.querier.set_bank_balances(&[ + Coin::new(12345 - 1323, "uatom"), + Coin::new(234, "ukrw"), + Coin::new(345, "uusd"), + Coin::new(69420, "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B"), + ]); + execute( + deps.as_mut(), + mock_env_at_timestamp(35000), + mock_info("worker", &[]), + ExecuteMsg::Reconcile {}, + ) + .unwrap(); +} \ No newline at end of file diff --git a/contracts/hub-tf/src/token_factory/denom.rs b/contracts/hub-tf/src/token_factory/denom.rs new file mode 100644 index 0000000..e43d2a9 --- /dev/null +++ b/contracts/hub-tf/src/token_factory/denom.rs @@ -0,0 +1,142 @@ +// source: https://github.com/White-Whale-Defi-Platform/white-whale-core/blob/feat/tokenfactory-lp/packages/pool-network/src/denom.rs + +use std::convert::TryFrom; +use std::convert::TryInto; + +use osmosis_std_derive::CosmwasmExt; + +// see https://github.com/notional-labs/wasmd/blob/v0.30.0-sdk469.4/proto/cosmwasm/tokenfactory/v1beta1/tx.proto + +/// Coin defines a token with a denomination and an amount. +/// +/// NOTE: The amount field is an Int which implements the custom method +/// signatures required by gogoproto. +#[derive( +Clone, +PartialEq, +Eq, +::prost::Message, +serde::Serialize, +serde::Deserialize, +schemars::JsonSchema, +CosmwasmExt, +)] +#[proto_message(type_url = "/cosmos.base.v1beta1.Coin")] +pub struct Coin { + #[prost(string, tag = "1")] + pub denom: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub amount: ::prost::alloc::string::String, +} + +/// MsgCreateDenom defines the message structure for the CreateDenom gRPC service +/// method. It allows an account to create a new denom. It requires a sender +/// address and a sub denomination. The (sender_address, sub_denomination) tuple +/// must be unique and cannot be re-used. +/// +/// The resulting denom created is defined as +/// . The resulting denom's admin is +/// originally set to be the creator, but this can be changed later. The token +/// denom does not indicate the current admin. +#[derive( +Clone, +PartialEq, +Eq, +::prost::Message, +serde::Serialize, +serde::Deserialize, +schemars::JsonSchema, +CosmwasmExt, +)] +#[proto_message(type_url = "/cosmwasm.tokenfactory.v1beta1.MsgCreateDenom")] +pub struct MsgCreateDenom { + #[prost(string, tag = "1")] + pub sender: ::prost::alloc::string::String, + /// subdenom can be up to 44 "alphanumeric" characters long. + #[prost(string, tag = "2")] + pub subdenom: ::prost::alloc::string::String, +} + +/// MsgCreateDenomResponse is the return value of MsgCreateDenom +/// It returns the full string of the newly created denom +#[derive( +Clone, +PartialEq, +Eq, +::prost::Message, +serde::Serialize, +serde::Deserialize, +schemars::JsonSchema, +CosmwasmExt, +)] +#[proto_message(type_url = "/cosmwasm.tokenfactory.v1beta1.MsgCreateDenomResponse")] +pub struct MsgCreateDenomResponse { + #[prost(string, tag = "1")] + pub new_token_denom: ::prost::alloc::string::String, +} + +/// MsgMint is the sdk.Msg type for allowing an admin account to mint +/// more of a token. For now, we only support minting to the sender account +#[derive( +Clone, +PartialEq, +Eq, +::prost::Message, +serde::Serialize, +serde::Deserialize, +schemars::JsonSchema, +CosmwasmExt, +)] +#[proto_message(type_url = "/cosmwasm.tokenfactory.v1beta1.MsgMint")] +pub struct MsgMint { + #[prost(string, tag = "1")] + pub sender: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub amount: ::core::option::Option, +} + +#[derive( +Clone, +PartialEq, +Eq, +::prost::Message, +serde::Serialize, +serde::Deserialize, +schemars::JsonSchema, +CosmwasmExt, +)] +#[proto_message(type_url = "/cosmwasm.tokenfactory.v1beta1.MsgMintResponse")] +pub struct MsgMintResponse {} + +/// MsgBurn is the sdk.Msg type for allowing an admin account to burn +/// a token. For now, we only support burning from the sender account. +#[derive( +Clone, +PartialEq, +Eq, +::prost::Message, +serde::Serialize, +serde::Deserialize, +schemars::JsonSchema, +CosmwasmExt, +)] +#[proto_message(type_url = "/cosmwasm.tokenfactory.v1beta1.MsgBurn")] +pub struct MsgBurn { + #[prost(string, tag = "1")] + pub sender: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub amount: ::core::option::Option, +} + +#[derive( +Clone, +PartialEq, +Eq, +::prost::Message, +serde::Serialize, +serde::Deserialize, +schemars::JsonSchema, +CosmwasmExt, +)] +#[proto_message(type_url = "/cosmwasm.tokenfactory.v1beta1.MsgBurnResponse")] +pub struct MsgBurnResponse {} \ No newline at end of file diff --git a/contracts/hub-tf/src/token_factory/mod.rs b/contracts/hub-tf/src/token_factory/mod.rs new file mode 100644 index 0000000..396cc23 --- /dev/null +++ b/contracts/hub-tf/src/token_factory/mod.rs @@ -0,0 +1,2 @@ +// +pub mod denom; \ No newline at end of file diff --git a/contracts/hub-tf/src/types/coins.rs b/contracts/hub-tf/src/types/coins.rs new file mode 100644 index 0000000..073d86f --- /dev/null +++ b/contracts/hub-tf/src/types/coins.rs @@ -0,0 +1,55 @@ +use std::str::FromStr; + +use cosmwasm_std::{Coin, StdError, StdResult}; + +use crate::helpers::parse_coin; + +pub struct Coins(pub Vec); + +impl FromStr for Coins { + type Err = StdError; + + fn from_str(s: &str) -> Result { + if s.is_empty() { + return Ok(Self(vec![])); + } + + Ok(Self( + s.split(',') + .filter(|coin_str| !coin_str.is_empty()) // coin with zero amount may appeat as an empty string in the event log + .collect::>() + .iter() + .map(|s| parse_coin(s)) + .collect::>>()?, + )) + } +} + +impl Coins { + pub fn add(&mut self, coin_to_add: &Coin) -> StdResult<()> { + match self.0.iter_mut().find(|coin| coin.denom == coin_to_add.denom) { + Some(coin) => { + coin.amount = coin.amount.checked_add(coin_to_add.amount)?; + }, + None => { + self.0.push(coin_to_add.clone()); + }, + } + Ok(()) + } + + pub fn add_many(&mut self, coins_to_add: &Coins) -> StdResult<()> { + for coin_to_add in &coins_to_add.0 { + self.add(coin_to_add)?; + } + Ok(()) + } + + pub fn find(&self, denom: &str) -> Coin { + self.0 + .iter() + .cloned() + .find(|coin| coin.denom == denom) + .unwrap_or_else(|| Coin::new(0, denom)) + } +} diff --git a/contracts/hub-tf/src/types/keys.rs b/contracts/hub-tf/src/types/keys.rs new file mode 100644 index 0000000..5c2af96 --- /dev/null +++ b/contracts/hub-tf/src/types/keys.rs @@ -0,0 +1,45 @@ +use std::marker::PhantomData; + +use cw_storage_plus::{Key, Prefixer, PrimaryKey}; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct BooleanKey { + pub wrapped: Vec, + pub data: PhantomData, +} + +impl BooleanKey { + pub fn new(val: bool) -> Self { + BooleanKey { + wrapped: if val { + vec![1] + } else { + vec![0] + }, + data: PhantomData, + } + } +} + +impl From for BooleanKey { + fn from(val: bool) -> Self { + Self::new(val) + } +} + +impl<'a> PrimaryKey<'a> for BooleanKey { + type Prefix = (); + type SubPrefix = (); + type Suffix = (); + type SuperSuffix = (); + + fn key(&self) -> Vec { + self.wrapped.key() + } +} + +impl<'a> Prefixer<'a> for BooleanKey { + fn prefix(&self) -> Vec { + self.wrapped.prefix() + } +} diff --git a/contracts/hub-tf/src/types/mod.rs b/contracts/hub-tf/src/types/mod.rs new file mode 100644 index 0000000..238b36a --- /dev/null +++ b/contracts/hub-tf/src/types/mod.rs @@ -0,0 +1,7 @@ +mod coins; +mod keys; +mod staking; + +pub use coins::Coins; +pub use keys::BooleanKey; +pub use staking::{Delegation, Redelegation, Undelegation}; diff --git a/contracts/hub-tf/src/types/staking.rs b/contracts/hub-tf/src/types/staking.rs new file mode 100644 index 0000000..39cb599 --- /dev/null +++ b/contracts/hub-tf/src/types/staking.rs @@ -0,0 +1,77 @@ +use cosmwasm_std::{Coin, CosmosMsg, StakingMsg}; + +#[derive(Clone)] +#[cfg_attr(test, derive(Debug, PartialEq))] +pub struct Delegation { + pub validator: String, + pub amount: u128, + pub denom: String, +} + +impl Delegation { + pub fn new(validator: &str, amount: u128, denom: &str) -> Self { + Self { + validator: validator.to_string(), + amount, + denom: denom.to_string(), + } + } + + pub fn to_cosmos_msg(&self) -> CosmosMsg { + CosmosMsg::Staking(StakingMsg::Delegate { + validator: self.validator.clone(), + amount: Coin::new(self.amount, self.denom.to_string()), + }) + } +} + +#[cfg_attr(test, derive(Debug, PartialEq))] +pub struct Undelegation { + pub validator: String, + pub amount: u128, + pub denom: String, +} + +impl Undelegation { + pub fn new(validator: &str, amount: u128, denom: &str) -> Self { + Self { + validator: validator.to_string(), + amount, + denom: denom.to_string(), + } + } + + pub fn to_cosmos_msg(&self) -> CosmosMsg { + CosmosMsg::Staking(StakingMsg::Undelegate { + validator: self.validator.clone(), + amount: Coin::new(self.amount, self.denom.to_string()), + }) + } +} + +#[cfg_attr(test, derive(Debug, PartialEq))] +pub struct Redelegation { + pub src: String, + pub dst: String, + pub amount: u128, + pub denom: String, +} + +impl Redelegation { + pub fn new(src: &str, dst: &str, amount: u128, denom: &str) -> Self { + Self { + src: src.to_string(), + dst: dst.to_string(), + amount, + denom: denom.into(), + } + } + + pub fn to_cosmos_msg(&self) -> CosmosMsg { + CosmosMsg::Staking(StakingMsg::Redelegate { + src_validator: self.src.clone(), + dst_validator: self.dst.clone(), + amount: Coin::new(self.amount, &self.denom), + }) + } +} diff --git a/contracts/hub-tf/unbonding-queue.png b/contracts/hub-tf/unbonding-queue.png new file mode 100644 index 0000000000000000000000000000000000000000..6f39500ac49762ddf7cca84e2bd936a0b1854db4 GIT binary patch literal 98283 zcmeFZc{G%L95-A_+9YkVjgTlIWeI~6O(}%3Gs(WoK4Y0Sk!UO-dz#3;@7olzFUh`6 z7>wPF-E8kQq`Uimo^zh}`Qtg~Jwn8aSuj7U4O!P2yrB z%cG<6;M^?-C$n=B!dHZ^@W>rLckUe6$<$n0L-F?S?ZA~RkEM%?qqK;KySuxv`!!() zCkqi#DJdzDD_2FXUKIi;gq&gaF2){0_RhRJOm_JwnmL;|Svk5`IoO}0@il(p;OZjF z!$TAF=iiQ=W*%1mNV0eSZ5Cjl2yI0~RQQU>AKt)LFzv4N11k?RTU|vfJ2QJ{K!%*u zbusWZ{r|A?kHo)r>io0w+BNa(e{cP3>AzbaIh#4%ai@E_Ph74#`&c^d+3ZF z+<&M9qty(S_ea^}n#1_p+Xj*gQjPb@DlmzI`VI>NcQxa8#IjvqfxM@J_pD0uYf z5gZOjAP{ccxG_IJ-__MsS663fXn5z&o$2Z6ii(P%p&>0TEu_CQ7K@FKj~5jcWn*J| z_3BktR@Q|J7r>esJdb#!!UYin=byoteJ)Ya7` zBqT;gMtppHTwGkPUza+6{`|nefR&Y%w6wIBmzScVqNAf@KtO<|re;P)Ms#$viHYg` z`}dJZWOa3QY;0_6YpcJ%KNHh2V`Jlph=~0Bd?zO-Utix#mo9z$_%SId>G|{LHa0d3 z3ky&v)XdBbg+e7KCue787Znv178WioEtQm%%+1YtczAGfa?Z@mY;0^OC@45MIE;;r zg@%SYSn2BN=@E&<+}zxhloW4o?_a-u_4M@E+uOt8@U~GxaDa=Oo13_}czb($b8|Bc z2GiHqZ)|J?l1o)pwY@fZbFN_t`>h`R5!0Dp(Kej&(N9F^!V?>(nUM+;BQ^3;b7U|~ z?VfyhQ+iBVQEuwHRowUMVwVkd?_v9j{a@H=Ybupwg}FKyS(`nAyF5wwfKa|IQ&Sd; zj7_N@n1S57o>Kf>@rHQt8<(4MVh-?^zM*mXwVfWX-aQM9v~Yk8QVP=c?Df2MS5e`i z$H3g6yv-?vJ$0k~W_~4)`6#1oZwlS^GBeX12+pU||BRo?<)FQlJPk?<>}N?? z=ECPT)WM+yH)<}z(2O~ za~V|qbMgOs?tdRRRTW@sBF({opX+xaD9^*i!Lfk(6t0{{Mme zf4W%xe+9@QKM{jceIGzR6#Rmv`v;8P<;Z4~jU0kVv_3htokk?YJXfnk=UQw6cGVz;IqEOc8-5@8?Np{R%11G+W|wox#iLJoyHW`L)LBaBm{~B8hx8M(|94&vR1W+WW^P0Iu#93&hhG1aN%@Z+%`-cS`q-R2`kT|;Ii8hE$4F@eAv zHSZzA3(wv=L;-%pK8BfOreMii4+7hZfs zSbyTpdAdi=q3YB0t@$3srTK}#k~K9JKXWp#-FSH~;`Z?U&QQdsF{OPA&~0xeERmYN z+z3Nk`Isy?&P_39eyQMePxFF0dYg{ER<060=HZ~G;{LGQy%@o_sInA?z9<$=YH#AT z(E8YsprJk)5%^9Uxe;Sjf~*~nlWm8{Rb;byVxI%Plau3Wk}6%cb<svN7<3|4U zo0?;I4_T16N(B!#dbVqiH=w@%$cGyTXioOK@B2hODKwD#*i3d_?_T4YsOe|Sl6>2Q zt~ga(q8Pd5?AaVc4#a4CqBwwEKgrNYZWucXuzDe=~_<_b(?;5d&A^C+PZ%EzR)O4 zE0AZN#1eF=aqLF=n*J=W8z!@3e&lcxTM(0xdujNx=nUG?_JIIqSd|9PaY8n)Znu&9 z+^;gxM5{JX>r_>iTjBV_nU;%xIs4bL|@aCzh z%E~;@o7&=^0iS3?=rYwg%OXdDm52(JY^fv6;c+2jq9gdyy8@$V$Po2W5963lZqCRj!?fPa1Dt1c zg^-I$lvuTVA;cc{L+PZ!Fza-!MSN>Bz_b7CY^?}ncY=y<=`|YXBTe-p+qm+HyW#>>G^2k%WtRpw) z?le80=rB>fRAZNVngcnzzb?osvnYFa0{H%#BI<2*6MQ9OZfIIRI?HfiUxX?spu z4qa=qammX2_4mYgkC!+a%YU`0bD~J}w+0zHFBQKoKyk-|w;He}^DUMxRWp|z-dw44 zPZRH(5{&tExapM9dT6A>rGnA>_vU?8{EVu;U{b-#LEh*2Ip27m=lfD8`}HNV9g|$# zdD}J~+X0%-{-JMrwIpqj>7YrqdX6%sRA3^}M&ntx~Q=fW)SHRLOTCeo^7VZ!1?vUUbiQfx71 zA2KL0A^GMQe?vyk{YKl*m&4Lil3dhtraGFnon8(iDUogTRn5dHez4px^l7fds>s&s zV7sUS2(jS!h-t)QjNQ>Q$qPVpsYd-X(9eu0od$-K% z9TXvU7pK!VcQCHaP&5?7J>A2igz^;Dq%|jy8CAr5>rtYXcU;7dZz0TI7;pX@0XrjX^_5%)aLP&Kgz!7~9 zb}SsapUTx?;jJSeucEp%Z{*V>Ga#sSun|RfY-y>K^yDSi)mlA7*R#|ltRvf+0M5+m zb^$sq(qZ1S0^w8rOK`)XR>tb~g9SZ~c)rx(7;i4mm)8SS))+Rv7Ua_Hx4XM^Q({y7 z!=6<9sar!P62q<1%6hD)jjhsJVVR;9TjM>@4}49L>0fVMlWF#t5t*Bh*S8(hgf0D2 zZ~J-VqQzGOYSL$M z;dLX3aYRmlHS=C?y-(|1Son{$U-%iM!Jq~QzB#wD_dTOaApv5L2X@jF>1Qvc$XNCJ zP3;D*dI=?VO58vYc3Pl*JMUyd{}jw)>zk@W^Ihu`MDy%Wz}^qPO*7pVO@}T(>0;e zqK};I*AcH7WhW9uqs$k-!e89EDSijW*l!ToR4lDppDxc9uzvAfVD1qW_?Xlgf1Snm z%^2=xlwN?&!&&rQXx!d)uAPbERD8sZncz*Hn6n4-9AE{eNN%K&@|vU$()Jw$pI|O5 z*+fXWCoC^+l2?@nZeohr5P{3I&fP~A{P1|+RGiY69U6U=HVQ6QtIfs`J?8xQkaZVx zpCF7PAZG5=nRaG3oAG)ZiItyEmKLq76D?uBK{fYJDspP7Xc)03mG@%8GQTz59aJh@ z5_>3{$e*Q!5zQcN^{5+wIzF9bw@g1e1UAXKCXb}ew!pv_DI{H1HF3K8*aW8 za`e`V^3X+6M7@RTy6M$Mi>;OEq0>)rbfK7>nsLcR?ZMpJk`}f1Yk7UbsRA=039_Wm zZ`IO=-i?uVdfd}$O|ykV;ZRwzO5+iJ*K|mfo$*Qv)Lvf2`_j*Xgo##9*-%hxGdbbZ zV%I_hC@#_?Ewfx$BH8b-lB35Bs{mFi`z-Vz9GGO>HZU+BJ0*>|760&>W7cD8xRpDD z6gFj!A`b}$(e8NZ=Cr3HSTFWgqZOZ(`niqG@23;NmmM5$)SyqTT1LN)bHO*`){eLB z3=3TRUisG-B{&wfka}U&xDKIsTk*NCq#wf;)FFFul9hVIUb4uC+hY`#9$O(NbIc zm$sYvR$)_bUfi~y-+=31eutqi`+V7768&0?RFMK{Z+RQfh7N9eKTe|Go(P^+t6h94 zDcXVJA6>y^ol1yEbm;z$Fg{qi^%+7|rcN(RVi#sYidDvRizu#}OBRRWieY;faAWOv zqOSY^60U~e>}B^1+xxEKi0r54rr--cQTd?N*vj?Gac5*st~9hCh>R4_)I>ePQ+J1b+|Mt`|-weB| zXVS{f)tDQ=eC0kyF7(PU>xyzugeXns6rI3uDImdy*IL!?NjurS=EbRx9kiJ{ebTh| z?IC^AqvsN#g46{W+}4J#M7*|ItR?eW?P^M#xy{Umo&xf~M#}?3ue4)Tua;T1=hAwH zb`9)utVc0i?4&9ZI~b#)7jCRQT2uasqmeQL{`36Gk_A5=+W;hJiFFqnV+9fZ9 z=AAQVkiczdDIohgGCR1aVQW8KuOqe}C`s{CEZt*Wxj#_l#w!9zDglT=T`qfnxXUlD zpQswRY#E?Kywv(4+sZ-kMg!?Gw7y_@!p(yPz5)b_$&jQP1lqjNS>qi>FFTVu02hvP zzV-gCU@amlbAivq!%V+kWTk}9;8qEU{2dhEjd$2D+a#L)%KDvr=9`Cs|9Ijmw%jBW z0;$2NCv%`Nx?F5ZvTza;UoYoi2q;`;J^KIvVJ}5WMP&Icy_bz;6&m0RRKCmfDII zguQ%|+rz7O=A$7CLcIr7s5~*tZp+F3c}Pqh`7W=QD;Kg?;`CiRYWj`ROVaLM$Lh0n z3e(a=!j^45fxLr)4SGcaK}pg3R!1SoU;~C7w7z7rN=B0gS5kbCv+Ad5eqJKsB}wT{ zrDCF!@b`R~gSN;7-sz6h+$_RU<)$86H9ZS1@b?X89_pEzIS3QdeUr)A<_SiT!iOEe z%t)wi+7-*LK59Z#c&qGPxR|i;2cZ>e@<+(!lUGD8{v1Nx+%-Esbk^EE4Z_|r({Ye2 z1+%c5-}^L%9JT5nI(uo3(;C5Vdu=CA>G?|z+}}81k_{1S=*)PVz%vt4Ul!%OF>M*$ zK5+N7DiYM!BaV{BoHiaryDf%ZIVZ+Cy&)8p|E{7LO2rL0EKIyifuN$j@f)r{y4nmq z(RkJ5y7?P{Ua?Da{F_T_E4bKd3#D|3msGV~0rg84%Go+KK-Q50WFl37(u9C1EaK;v zg;)FuZ~esak)NwaR1B0snO50INkyNF-Foj=LHB9}1G^|iC?vyWYJdxABe0IN%>9(* zdx0ulBCdd3THTM{=aUqYe&Z|s;!WhSJnl~?qcJ$4E&A)y@bg5ZZ@(n4=PeIX}b9 z82-gJCF&_$w(07TC+!n4P2*jZDXg5yC=#=G?cF;K`uJ0;0wIQFSK(b0wYTt<^wO=c zt<8?$hk|W8f4kx0Cq2^lsc7(g_t{iL?IDYU?7^#VA8gE6uKN4HJJ-`dapJW~VILtI z@D~^fXp`XgZUe{f2))(-B!>7Jw%yuxy3h!w?WkqVus%B zFZ;k=-0I#rF%_mZhlt>wRp&bW^*SHIhg&byieuM@?C}wGxCq$R-pbaqMYUzj$2a54 zaH}cj1g1il)-=`2*=6ozdDh2E%??r7h#BL~&Rj$&$k-$0ZMGnHN%vq&Xj2%j?&4TSl_y zePt(n_mYHiTcy>HP*;yCAvazn5A2^_=3O5z-ED(j981nC_jI`lzK>oQ|6p3oYe2Gg z%&LziycYBRFo21?L_Xx02r<2`+eTJnIRSA~Kzf9JDwH2%PeF9jLM;yVb`IUQ0MmxQ zS1e?3k{cXH@-@R1T`iqHDRE|a@5 zKTyJJf$P2|)PAR+x``iI(;=G{7Z3w=l1r4~E!K7}9P@_Z6wA>s^HzZF)56-Zgm!%r z{zh5ijQZFf*^uIhQQ!V4@3v9l(EIWCjjI!?74(Y^Jug4kr2X|2{=o6P$5jfzd&&ztK z3cA_8_~K9$_0gq$iHlw-4lmThX^1(9S^sjwzfe9iMqa$5=%nn-D8gN@Du`ni*@cMT zn#{bsC`WSCUJ+=l(3i=XF(w&OL>fL2Xyc~#X6Y|vC+hQ4=LB(Hi}=EytshThr$CaG zSY2-w(>>3HU@T2boIj*xq>!iDj9on5Z{%MjDJ`xpI9mv7MO(q+zB3~e`iF+7`R-G| z2QZ9h>s8K$KRBPG;iSuP(~EikVV(U7ihG|=bkptsetPNVyNSS@%K-{XMhZ_-3m-Uy z^Uq?o*vhV5i;8-6?)(Qy%w`aG!b1GQ-K%PoxI6~VMQU`ZrU(^uf-8U8p0T9o@lRJ> zLKNfOXw?n(=L1KE?SjIm!r!j`te8W4fI&#LEAq)+vn=n0B#YcGVFJu%9-U8@n$fmt zdlT_&!(#OKSV!5`5!=@``n*6JgE(7;GqzCo~MKRikuVu}%u>NRa#~XKEJ! z<>%&4g+h^X=W!=0H)s?!pD_63ERv(!s%~sHApga92>C5c#`<;USZImf_?pOmqm|;j zWW=k!oWQA3VmmN?WR*{d4YCyQ41PfVRI_iM>adbAF7K@9gEsdg@PM`njw3N&j;rP;aj-3fAGGsR2@lOeV4{o8>zphk}gnb#xPYi|c_T6%T+2;dvy z2hHQ(z0k`^@OV%9lKcTOT7=f~>^h$0fzA2Ab)eD+*-7^Ec!exNg9bgY4e^c#PLJrB zN!IZa8&7UIt~jn!xbUS@LlX9AuNH$U7Ug+gDIzK)akA8znUbw%Qafeh?Trl zchG%(VO?CGl%jr|TDj(9M(gzt2;gFY~QD$-Urb zm=W zDCQ5AWpoCL*WK2j06gpYGccfD3WpSSTJ-ry`HT`9Y~9P}!gz>AU?7C=z~y+gVAe9S zNEQ9?TiG9lX@a8}6$%Z%++Q%=s9js{x+JM2&xj^hO781Db7o|TN;XoB{=A13xnfku zOLj9o@Gi`yxq0E^JQ1h+7DJvo>%c{#CT1^P@aD9g5m9M6Ha;vPSm^v^_JH2h88?Zw zENDx%A@-OddXcPQMy)SP?hFq?*e;^T-z&RQdl_bC$`jzP*DJ{)Smcch^H3X1#+- zJ5GgicR)nsQkywxiI&>+<4g;0ARbw~VQQJ;P4<+W)-qddt&38K9VSnq`vD#yoPEF!-vK~x_^!jASn%gyI!h3Pq zPETfp>X>#o#e2M}he&}=CwCXdFC1-=sC0IjFXo605-4%Et3i(qGa|1--DJl7w#9w4CKz|>Os6- z`w|%MZY`Xiz9{`K_GdSZ)y8Wk_!ei>Iw{yuLT*=fIMeNliPL|3Z^sgwqz>6FWy6O{TFbQt0EO@Su9;{tmog6N&^ybdoQ^wLk0brJbq#OnidhD z1Su|aO5{(xLT+FE*5+ihnz=woO3DSb%BHLl6(AVEegF#q{Tpn$9^IF>1I31JL_Mf= zgn_8)-^+)vk_WJFP^=TMk^a@IT(EyB@RTIWY?|jLR^1JJoO*oQR$8F(9$sS`U}n?X zFYbiL?oW;VmnPq-JWee(+BsG%>0Bqaw;+N&-gCfn^)#x5&)7Ki(bKfrxV6rt_^!W$ zReNh;rh+RIDkoU+fWfJhB{gwB?c#S)#+>`^G=f z*r-_-FOTBo=SirHO?(3`((Fte>ww$6Q$d>sGNrFJGfWmn7h(h{lB@!AV{u}J>bf!C zx8eC4Y4WD>m+2rr=d1L%G_RVsjEB^peMf4ZyPGr+M2A=OcMV&MoTsE;d|(4jzN4`|A9>9S-MuEA9|Dzy9WU(K#FMho*Y(J8QO+atA&e zH7kUhMXJs5Bda%@7cPhd>&9-@d93Jo`lKe_V7GSap*xK@BX}Tio{RbJOhii2&DFNp z3-h|E&by*FO7M}c@~d*m)TNDtwl6KG0wh!19TpE&L@|_+BqxrPgy_BJ;vXk06ho`X zk*1GN=BSan9q-y97Ai8F18EFvd4cra#yG?3VwOQ{MpY)5@K~4*eX+CSU9ueX%Uy8j zA{iscK>P}ML2!AUKXwMi?M+nLxah(2;X^^uW9byK^QRPe9T%-Q?CQnU^~D-_F5vcM zPonKpn885Q$HT>Xfp}bn4*tIO4Wt43^Dq0{vHOlhPsNb&P}gX0pYvQS>*5nvDceN? zNI6L($dzEyhikED9 z3lX;~aC|f*zkvWy-Yy?ZxJ7GFFnM0RX=H@2ZQ69Ez4v<43O+@bG>n&DTOg&rDQEgs z!ogbIAnPO*t0C+=x|Db@g`WAr2f9lIPH$cuJfV0tfLTA_*yHKGcQ3AS$}E8qHY#$M z^X0_h%e4$VzV1F3rHnkidWRd!@46MtqUf(JViv zEVSig?DJ|K(65n~a1hB46blk{dRE1AYAerOJ|WrrC(0hJw~YJw(*0IcR@d8s^G*}R zx9zyoRdbhyN=WRflzL&oF2Mv)p>8jgXAtms7z7JM4=9!!HA^v7lE>>LnU%ze8`tOE zZ_cbilHHyxwiindN^~|XqaDGf`Mz50k)F82k#~-EB+uk$z z4-;i7DPZ81b5pEWwwI`Vi#cVPp#_y$?>x|75}wH@MD;gFcDvZH^`bubwO@LvEzt1P zKp{Y|VYNWjfK2u#({Z(5-xPA~k&4-DpG$xwO%x{w&@Ji)!mzCbc^An7Qsji@rR-H_ zLD$2CB8+FB4*(f358z9JKLBWC(|WqMOZ&I>rh8zCk=bC#$gHHSWSPv7AV0d!LW7BP zXJ7Ge6AmHGU2Ms@aVB!_v6-a)<~3lT!7{^M0S*OVCqQC%b@H`mK&Q3!*o_Z{%`M|r z>lXxj)-URTDkJJa^bu%-1xfa);aKZ9#hx&2o4>38Iu;W~EiFz=rUJp2t&_KGC;@v& z5F0){Y2JiV+??(#{W{D-DX*D~vOrF2HV(9!|K&e02!X3oH}n**NQltBKd=cGLMqE@ z+m@wd-D~Gvhrkw!h6dk@{1nokj0a;rsR<`F{zvm*a=_f@=25aH?7NzqTzfQxYo;I~ zD&5OP-*CMl79vX^Atpvd2%4Xnz4t~#t`)vH1~vP){F16$Za)K-D5VuoM%q|(2sl6R zczi;^$neUFX0`*58w?vE(w13FTs+$FX3my{d$X;+?&h*O-|&Alc~Dta^h3u3%|dB$ zT{z_o;=?dbG~-Auw=H?1++d&OB4?J#nQhC4>~5PPs_!JuG6@R)$BW(Cjj98Ai>^pF zTdy}0wIjWBM3O_#VuM-A!E4S^BEv^87$-XaQjPivOj&mXGgzU9z6h8K`H>Ak>eHHb z$ktG0Onf+g^ny%sm2@Q6$a)h*aZ_ShL~=^q>C2@^zFD!6hLg9>K)Eql%Veoh>heU@ zEL57a^4D3l**G@Ib`(2=&b#^ zc`h~k9s}@UTsP#TE#eQf0Z{O+Ddq8x7idz1+#%Qb&kAoH zB9Sqml3}O$e$`n9?LjJjsG|pJ_@yZ1U-iPPwhFpdOK+HZ<7u4xk`*CTopHS_YWYr5 zPpI4*L)T5-*;5f@CYI7+6h(e4aB@a5WYLAOy;k4Hebv`GmRPzg_wb@dq3k$h(O;DE1vAQFwP>5cs*J(9!D;i*i%Sr zuh7nH;41^p`W<&7!T7cgkq;Dkhyc8cUoGpBIKowLQO}M}l8(i>J3`MN(uQ~J&5%2+ zoS%gV0voli0d4AnLo4cRorpE%C|A`ezKs$+v1gSXn8gu~vVG3KSGQMi=x#if$LWpq ziVCiO4fMogNaMnLcj=f^{{^>C6Z?Ub5GIUeskoZI3)m zhGJ%C3R|=c54bf|PzJA-z90N|;L%^HDQQcD$!HrN;4;ac8}r8<*u5k-C8iTuP2=T_M- z@r`(F+Ug=KB2jsE@nC)S;kN7_K-W-l9l6L^9syjF6n2f288u7@52 z`Al-!vv_D`^m%f)#3QY6)=9AP5!HgHD;-$uN;S1Ba;prK7B(>5Jbe6I-28w+@zs55 zXR@ZW=k6cvVL1T8`MZ$3w|4 z^Hb`QC-gI*eRu5NK4in1;G9}U!h@OvIEK)xGOW}34Y zY;lksus#7eP_kEd+>&Q--NSEa^RtLYQiU+ahhk;|=aik^e1YfPX*Syjnj%MiOmas` zEZl@06tUUsepSZW?#+Gxmtqk^(H5S39(~Y42vKB}o~j!UV>74BXA?Dpj>Ji=ZoRET z?lj+>15mcEZenLKDuLqIf@IA6V&A(VWJSOrr!}mc__)WIawtD6KDC(frCKsLVPG9f zg-)!%MwXV_=gZeJoH8sua76azYmLzBI}ZNG%8sW{sv%YV(^$$BZCJoEk3+T(KrlF$ zS24kwat1W#f?NhK=4>AXB(s%;H#P&=N+P`i0Wn@;|Vo8N2yFGw18=fyux#z;g(~vI@T}_o!_{X~rAo5|5DgAl+T-0S6e9K25e?@$#t1r?xpggMS0+vfrdP zte_qsL~nv^BRAGx)+d+(wN~YyT5HtI3;c;hUTm=$g=R(Hsgy_lp)&oaZtg@840}bJ zYPT&1+AwW21#0R}Y2AUq&Khl4qqS%(^Y5}t+rF{xN1MO4H*XUF5CX1t5W77k0;c_o zBduux{CEAY2$YuX*l$DN71b}U9AP&u_v^YnF=InPm;34X8T!tzibJEPBj|gcTJ_k@ ze`GSY(|B#8S0Q}Jlqooiwb@7Kp1{3myPTlcjAi18TaOXnD1~vS?nbD(f05CBR7tF= zLNl&?!oGBIcj&(=g2aZ9J&Zv8P!OK>o*wKxa;|=v&;Y!xF$KIe0lXt&Di6H1w-yGW zz1wF5YUCvXba}wrA28tU48k2yKXPg!gp8$;0(681AAoL-pGFGMg+h!F|GuWN{fB&q z?H}?Twx;ql&H$ad)537tx)1PHNIOl^j(!j8&|B5`$k|HFWGC7O0=zm1nDATrceyI( zOPh!uS!5_;j2*N22GEkGGVQfP5hQ`sk825Yhwg!OS;l%%ZUItg8UibTvSu5e#d7Qw z#1{=HtQS>Mdz&TTj2%1JR(qiy_PAjdWf0%g^s+82ER@0%$B@s+^{I;VNmj`Ird)}IN{8a=bE}PriQEA6*JKpz; zfgs3))Z4c}tL>%8k?k1FNut4e&GK*WHTzmvJE#PWzZ*eDB62Df$D2k|G z1{v!d`+Q@mn4A+TXayb_bEiM%@e5N|$ReBir8m4H2(61iWAwWf86x<-A%aNnZI2 zP1&0wy_uxZfm(?Ymt=&!+I5QyvDy8YDsS#_j)av(d3;B)Hra_2z$tOZ_fRTWPZu`b zuKyk8m^(tWJ5xORa{l?h#I-I9NVSv5FThRL+bhZVoPRBnmo1Z)Z0;mZc6775Ln>+E z-i7YV22?mHo0~og!rz9LoZL>N-%`1NFM(Xflo#?LL!DYVu*sIGeB=0=0RqW?ehL*TMDdTfXJo+m^fQ65{~~nI!xCA zl+P4KPj&bXTPVZ-X6P3ONl#zj(fGFpyFS^Kx$FHu1!adW6tbhz?(&~>-|^_3KS@ku zPOCG&s}s$sKtcJPYTH6+C2Y6)0A#zDJ7wZe9oa4mzjvAep zFskN*_}|$xT#Okaz2K?>(vAx-5Kn6H;zv@<&}mgM5F>do)FPn`jI8cJN3p369?Lz<0*6qUn zkCdH4{tv6|LjG@7rU+WJx3$RQ`X`ZgS^o+7?;i3SX8)5OlKr2a@;8WfA@CoB{AoEm zNcvy&kn?|f!Va51)%f@BKXsp`^A0q&-9)3NHND?v()!vT$N;$d`|>w}Iy#zBg{1tF z0t1hTGrfnI+Iy>!K!@}2F|P`PAm_BZ5j~B(*E6MOt%T9>Z}v*-m{FNj@>PrG0g>gs=dt8a1!XY0@G#d6x}j`w zFTvK~Z1;Ntn;5kzbFS-y&oM5L`O`~)x`4f4p$T!cz=1e7}MdE2u#(H*tQ@ca~ zbf`#w^3A7DM2JnZEH|QPh?w2)q$+a>?jx$-);|z*gzJXEAh50Ia6F<_!b$=rwlt7Z zJ^$mDPoYImZf`>WCE1zv^=pH#X5k~Zs_YIY=;kkSnF|tA9*y{Fw({cpA=kzZcg zbOQh720UpQ(*=BcZ+2ChoqaR%%dHlz^{{LDGf%s1&13Mk>RS|>q8I%VgoV#GMDv&@ zMTtx;EfMmAT7v82{gsuKofB$h=Tv)_=H?kGFd^#NK7@rn~zxF954hFlbU)o=E z8;Y2x(31%_Czjg*Ac-6)l!@$>+{zECK%j25ocY=qZHI$(+e%qn(QmeWWUjwx_mnYk27F{Q7f7~xelZ)j7puOmF29ObAyDkfnN_PeljT=us88bhm0Nnm2F3Cjsa8kw zA4LnTR#+FMn(Irp)%Ls9TgVtzvePx9RJnsjPSCZX8Z>=U`N>`NlDYlK33f9K{#OW= zOHn>l1%%Yv9(Cg)^BGZ$ZIYasO8aDKde@h;9lrpd9Y$uT&-5N@F)xC(oKazia8Ibq z2S+DPsV|CRUXp5;u})o7sCoLSJNPAAc1`H)%|;aEQ6<&y?x0_cP(h5b&4wt(Z_}Kp zZ1L7}9g}<}Ve*am)tLTC@Zh)!A>}5QZwzGqlRVpWpe(Mq)vd4p82-8Tb=V3vZG_0v zTGf6iefX$f)ghis>1!eVX>021fJ#leQYEsMPxD(HiYljP1Tw(VYb8reD8CR$NIfZl zX)B~Zc>#sXL2mAy=y<>--W}54;&!TiVxch8nfeG4ZXx5*QhaB#zl>?iJjSP}uHf5o zEzs&#%4tUstR>HIw3GhCM!!*+6@u)pYNF*8H3f-v1BkMsd*S_?6r^|e%_ z)KuU#uTgLURLdyJ80IwSVeZ1mB|3fXT)TZS7PgsuZ2AX5@i~0j z`ZfD(>2vWMT0Qh?$O10$13JjQ8{@-By>FBE1jUPn4rkw(R#y));_qq5{J~Vj`FPm~ zK2I0JWt(kno8d}$Js%7b-N)W{*e|o{lq?~SYN`Er)zW6mc;9)?E`>At-zLS=`<%*K z=u&=W<^l1|!I<$5zp*zS)w1|T}_w$F|gw~o7EAs; zPJXYuc&;i(bY)!hK#|5ugJ#zP>hTP7{y8~!uIH`%MFzz`_NyQRZh}ee@)C!&a!%Wu zvH|;I9Bo%|A?w2s8M;)6z7(Q$@uKbHWnZBw?(DDA>WsX;j{)-}gd|@3fRj(!QgRa^ za&CZ&wU9dJC)3*)Vnl4Em|*m(NNLuK`}K`;a|aID(qGmY-BVO;EU1L^%E&tM8C1xI zJaHjD^3C&B7Ks&qX)fExgSLBVPGe+U_nkil$6B}!r{kCvkc1$isndJEb@86jcmR9O zX#1E~u#^t);!FqcO2fpg)RV97aZCr=6_8$$$8pSk0y@4X<@*j~r|xBkzXByfa2=Cr z%Za~^=(j~)U-5q?gr=XByYh@t_9pTN@5&wdO`I59k#=Sp*Pa>l;xu4AyY5}4Tk<}h zzxOi(Y}8@QIe=)IIsom#?ad3m4~=Mk_`&;vCWc0q2zk|XgvJ42iE8}>;aj^nk~b`> zA8?PugbbhY+TNK`aS#Zf#P)d$+(k%)GCIAgL{G(cZEiF9BMV>{wTi1G&8ijkvYhrJ z1W0pj3%(>mCjn@)eSSo?i8@wln+@63E2RS9ox}WF$KQubA~fyy@7TPf{fhsNrhgd( z5Pu@jvG;ww{l~`b{r|fx0gwEan?v(UI19k>>+4+~(NxHmy&$?V(Qc>oKnvX5Q(jXyiBdM<= z;&F4;pB1h1RxDHPCRS@C5K<)4q~EY>p$t-<^EOhV7*u${NBQM=v4N`BJ;P)2CJ=!o zcmXjit)Sg*B7PVb5>wmUnWMC>^n*7$N*(tDH5YY6ck)bwb@OV&x%uz`F0 zu`_6q^-Xd|+0_e+@#i#$ zxK{=3FBN5HT1vut-hG4|gXvhMLqw`=j+Ac60bwO%L$uj%?(Ie0`*PX-Wl+K=h^|xf z{!I8$S%R3k$MsjB(w56`Z1&@z=nvwIG_GEzDZ~3SERM-GMSP#RZ+-e2F2pY0@KFDB zhx5sXecnJEvFi*R)5quykX295JsMsFqp!I-fS07;*qPQ#Y;}?A{1*BA9lT7_jENBY z&gY=bJe0(~haT1v(XqD!ZsY85>L6)-cY-P#XM(jTWr)XC-DM?Yh; z(r_`3O|w%6jPJft3fd!c!B)uEQZ_pM26E_v5un4JmJc#%>h#8+JA;#8@EA2&wtX?) zb>gVC##cV|TQ-3rx#3z+LdQ$|Ef?pvo`~{i9hRa?6LyWmRRYvmfoZF2YR{tSal=y~O&jBp%6y;6i3| zdr;gf4X%udZ#7kc?-o{?r1L!Ls-JR2vhR(t%Rt`g%8xAmI(g+uGO;J~^Re%BE~DsY zASu(YC5&J;p{o}H>mG(xGwP9Bt7=toRYR)x#uZi?>p1IT7rTamMsw|ENN#Sr=x1MS?{Cp)ux3~tI^NGUGEkQRy+ zABn$MwG;*LhLmRbZ0Nx#bSy?Gqq82WJ?s;*}2ZC-Le2QC2Ub_&ZM=3YselU*I8N}yI*A~JCu zGaC`#&%E|8lXd$DSvauv{}A`yQB8H-|KMX+KY(!0Uf*8Yqj=dXm1J$L+U|*VI%mw~CZ~yul`1Nj^ZI_c%6m8ojqAs74to&3 zxrZ9sL^-}nnIO{TvsV1{R(fC|*Nu?F+py#0>j77s8ak^J5 zjr=5(KFXE`LMD{gP^yJ6HogT*dz@(R88tHL^#D?|B$0x8!;HUMFP0v=#p@Lv=#jSlY6gB(H8^ zLJFAfO48XXR!{heN0R1&>(H4^pzsi*`!x?!$!!Eotm9ody;U59tgUKY!_^`OSfv^AbZyQ zaPFtXSLm}h_LSd2_EIXUcM66PGPT{SuFFREf*spJ@ZUjK`VpbAYX*poI!q`Kok(B-C0CdDf^R%b6vsO}B>F1S&(S-W z(BP5)$%Xx;loFQWQ~JY%8~P)wIcW|Pea#O-@30?mNvDXcD6E@Abo3*%>L4X$M>PQ? z(yw)@MFu+!^=C`zDG0Z4cue#b;T8^Fqem(|uuf@RxjP;8^Jg9qXOx@gK8z^)4$7>_ z_{vDg=T0fV(7X0V4E5~m8n*5(8FU_tJecIk{>VnFl!_`hD%)4|N_UrATQj4E5|@`$ zYl25uz4>KBf3GRIXYJ$}j!dN{TPBUDu@^=k{^APugr$C)Knz68(xTrI!o99(2Q4|E z+>cB5Tkl^x7f9_q!aQ5;k2LY1KL}qe8PezNL&U??4xK_oJNsDU>G?)iqC+N8k2GLI zs25vY9*Z&^wHs;l(-p2{w)Ya0`H2|b?fMz>KQ4JTi!2n3sMfaIPHTJFWA3*+ew{4;MYy`02ysZ8R^;#HtJBZQ$b)|Tb2ZV$ zk4Af<>D&A0Dr-v><@6s!UUsAPuA39DHfsc|66>~5EaEE@Xz$P>@PxPXJ{C8ycr@XH zk~yAPaZYtkU3oAcNm;Obv!zTDPI!#{pC*KHc8(4wx-F8(P&q)btlQ8@U$#N>kioVy zr>xs?PvM?WQ|*UONcK_H(N(&MOuk}dF>BoPW77oI9SL+BH=w<0rYNDun=4|L6;KI) z$xvN;8rN?*{D1Y2E8g9gH)`#)X?xkAZ!vRpF5in zt{fQ~W)j58EX4v@H0yIZiO6xQx7*Sbm58;iB^7gbPrXvR1Vnd2$WPWymU#>RGQLS# zOP>bYx$bYibCF%>`$ViQ!e=*wK(ElNabia~Yw!ALMLgmmiyl%@!bC;uy}w>at>@sl zOPNiwsOKXQYYoeyj9lcfdvCl*>#z)fc@4=-t4BjrvN0`x;^5!MIYKjnR7u3`KC^GU z)lIXDh$*#FmYsYQn|(tyL8j?{mYaHx+F?5+RGO zQdK<5S9HuXXqjBVG@IfxCg549fFGz6?mA;-T z2c(&wo^NUy+cQy$y-69;mF6uI^`Jyd>rz~*V{mJC@Gl#0@a&4{Er zr9y_Pb4m6nj=&m0pe-K2%}uY*0U&qsq%Vm`OLACplEnkut1OHgdFgq z<&)M`;jDl|xzW@i5kaUSGr)zTGT9;-Vp@>PRg%y9_?guD3tmgJX0kItLg)}5f)DcI zfnDymVHm&PJ?P^}*GI?n>`gxDcfLz9J_NY`th5|7sr;!!)I4|eK9Or(j? zXs(Dc3G!Bv@yJIp6*4{P`pSHNViW7j`D_NWEdYEv!NDManv6ywoR7Vu+cLF*kb1^h z;cvf-SxX#e%}<>*u~sbf+sucG1nk&Tz^X<`7m@TkIlbHjAe0A)Xe&rLJpu4N@%;ET z%Bi4u4MJG73b|bA6e?-=pmKD*M$Tth5XL`LxAVlwa+hY-AJ)u8n{u8T;L?xSp7d zbHv;BOgzKTptGf^`&`it3z1U4tfX|H9>k$RHdArFUIzA%qNV0z8mID8J=P~nowr;kRc_@j*qP~GoKvB$Zaxs zB@QwatW^@7_fpC!0z}GZ^LVPvDo{?!i2>j(>ovF%o>fD@&}*i)PT@Vfi_D;%bZ3=L zFInwk`eBRG4;Bn|+?;QdQP$GBC0z|O5n3gg$`j@}zq(nf#PmXF$zdHW*VxP4UwTRX z{NVL*r?g;0Rm#H_a)A3YBLEgmb~OGB8A{>r$U9^LYX*Vtv5%z4t>jw})jo@Lh}`8W zN@S;esEc+^jU0VWImntx|3>3>I#6-aHs+_{1CG}<*wC_q=IcIfPDA}78%g>TnL_?v z%l`=PUTH)?eo)*yWH?(6$$f}>G-m1F!V3SEyOu8t3neooAv%oJASsEf5Vq0& zwa}+tv&vHIFI!V7@z~#_)bkn)Q7$2Aj-kJKKMk3sLKeTY*}O$E82+TQE0MA*jZOZ% zJ=@ou$D(wH-AbQ$xKE<@(`4MwDuvEyOMTd%U4`%J3h^#^S2_66rJ%avZ+ES?|MAq! zM@4j#!6N35!^#;bs8CLBJzIR+4Dr2?ZqY42R!Zs zbjdHq#?sH!XZB}yT)n0cK>yEzmu|9AzSVsR=@2CCb!u4C1O9KVwQhv%wKGm;IC)>) zSy1A6uD$OU&_u|`VFeJ5Wy-R#1(Be(_|Fuu?lfI&?>E74B1rnV&b|Eo*2N+V+-oK$ zW|Iux+vqlpJ`8tQ5CduA!6zv}T@C;_%;Ql){z1$A$1!nc&EGRK zt}J+IH>iIvxmv=OHOQeoG14Jnq(yf!<7=+}giSUQ>ypG>$wQNKBm)4%n%#cgxxRKg z$9|u=9dJqjsjW2hBi_ATIjISO@CA)>EVf~mj_Z8pPf)X-x?O^V-DZ^GmbTtKa@b~h zT5W5?!2E|YJ-t}T=QaLs0~{_fu(vT`$s04^t|n2M&)&TfRkroauI=kEFn&C|Zh&4q zVdpT*QlB?tjtKgDGOqvXpZ>Y&P$6(v{>jqLhB~*{uZEdB?%^-ZL#t)XMupPf>7ZXS zhyl8%NIP(`y)!f3J7@3?;ak4?GV}fYzgd^2-m|)rT0#lize^!)Fev3^etFBJ97{M; zR{h92C2We^J(f3|S!C$f4Ud(dWd#BAS4!=%jb#NHuRo0|ew@q==N)7scE=jFiIddS zf%aWS{YuoaAR!3}s&Pbur-r-#25HOsaqTK6L7qlE>rzIs(N_c96Lhw7Ghs(m($^^* zWYnXSpN~{#DKf2>n&8o$7?FSq*_H9Br`Z))K$Re!4ZC)o*SmDA3c?QQuS_;1!qfG6 z<}GDgSqX2XBRHgY$zc)q0Nhz%OWYo-CSgZ%#KW{|$m%vF;UP|5K=g=qkW z3?7%VbN;^RH&fx@hZIj8fO7)MGvu0ggXljylaaxs*shT!B;~fHt|@w z*j>c>EE*%3K@gXwUFMn@Xj;}>;diF9cJg@YGd&yO5#V()A;t&yrS|M*ra|2$flp=T zBSX<`dicbulgVgNobxGf#~h`e{+O&O10!IoCBJ$6-H+GYi@A5&D1NO%n%eUCs8@8X zCnd|5=C0e>*Z>ei@%pp6tNOeHB8KTOk7B-pWuj{v#G|7FLCELx{e?EcDquTFoo+U* zvHmZ9Aij@h+ohZW$=;ZmEmbD8LL-9mNWy0`_?pY+N$@o@tVhdf6M_{6T4ez|d7oid z7LL>ygM6fWImukl%CE{z6S5)d}B>m1OzJ@ zY?lTIC{&c#&IMg4^I)>-N%5PA?%PK1sV5^_UAYOxOlba^rCO*Uoy(}dMV{t-xY|&n zZo7q4A{d=4AabZ6t@H*%y5&{^Xl@63nL3cZL!^%7jvyu^T@KqQ^h-+dTFik36+fTw7zl9nyL4olJ8N`39@5D zDw#<+nO7;67~u8$Dg3S;7To(k5%Y z-^;Kz3l_FM@wv7TmD4#b~U#REBIZP#$tEvKo1+RTF$X z1EMT$*-wcc%WW3Pi*@-xwE9}QN>}E&&!S;S-yP$cpzc+6WXr9h>bfzWCK`czcZ`X{ z`DpZ4gImmOb=Eb07r69C4k3JS64H_a?<}&g4Y505x`S)2=d{Lo^!nk?QL=}t9FkNs znF6yVqFtc{~H zi42jI1FiROO3hgtO7DPVA~~dx@<750z8`8=CV>m#dxZ29XZt9EEFr{~VBiCOP@^xQ z1l)lt#$6WY)iLW;+AoES9J|*{Q)Ca-Na;=8l#@=}85mt2W<#}sTyM4_r4Rzh+!FVP zZN6h^hYLkxe#ZKRkz-9G?`_?%(j^9}!-xi_AqM2hYQRDPDD*{21g3v%|E37&9Bo;q zVDSjKS0W!e=HcJHWlul3)ZDE4ClwnxibzUI3hmUkWhk1JAI+zjGyxJVHz6^bezqZG z`p<1k#&Vlz>@EUCHfilssSi{Az;2?S`jk>1U1w*5GiY&j5l(qEj3_r2N-LvEew*ct_XE*wQTS0C6sFnWw?s{|pQ<8@b`A_1hw~q&) zv%|fTg1u^L{q3>XE(7;g*jSeymHy0yD#nu?YPP;5;%tl(Y+YIB-up#k_gXRmb{MgU zK&64@xTseKxAsCm>@*=ck=G`{RPwCsIS-N8(_lL{h=MT#;}|~JP88XHE**WNl{E14 z`4|yFMPBmB4?eoKPLnchHDZrZ;g{U?zNc=<018G*r19ia-)xW+^c=TP5WhdWblH=` z3*mFtPXdgEdo=B@|LqBLneWjxgaJa|mzvAnK&puF93G*=K+#2dC-2Ssx2`{d^1oac zg#Tf>r27vKf%^ZNg$;VykYJegFlc3%_4Kk+$b}%n1nuz4E;NKRKJR|0;B(oB=HKb| z7B0Hh7)rk;JWeVd^|z(SUwtCaFT336z=l{;bzqB^X8x-k+uz;?1W^h#H6H&@{zHJ? z!g}^c$E{EfDXA&6{HahX+}6mwb?{&Lbs8uG*sG<{L?HRabEaxYS9NeZ>_X|&msnS6 zOwzgDJ}3Xhv!$~+p#Q;A$BiPqKmA|lt&62`0(Kt?8r&#C3`8OVuyE{?!3#u4I<6@}qc?agBD}zfZpa4mgbZb^g8uRdU zPw#K+E9Q!#>ldyO@bDUTf+zsNIly(X{}EKt{s*0N3Dicj`BE&mHG`$lLA31AjXQY(ELduXM%5aF}k@ARSEeK-6Rjk&khxVX{#7k6I! zxKh*2lM%2;pyBC*qw&p=VOYR}T#(m-Os$M(#Yy`t`|SD4zZ#0}p!B2?j6@FZE$Q_1 zZWnA=O6$!SSnU5{*hd_C9LVo=2jai}HCFX(bWN2PHGAJ<$!!c)WeEG(2@)Z#QdVNA~+MvMla!0D}B(h^S$>kE8tI2?~J?f(cT=xzS|lHav5B!SciY{ zuK^oHD<<2^eY_}HnWX86#bcm*wkI)&ziu!5^njYC=u-7}zqbVuSxQ-aBRjz?NME_e)LQO$?zFG`XQP^vd0!1?g6TiyIbZAD|P zooc3Q*3yZ>Yi-N*qVo&mcY^N1?VO7rM83+?%x(g z#dC4MGe=C;=)Xbd#s_|!IN2WG?qg^#Y7;Q8_%MptJgS-@PPzr%k@0l!vTsHCAPIT; zF9J|%@Sgj!ghN)h~FwaUgnS`wrACA9f?#!Z6R8OmACRRavo$zTwaFq^N z1=M7QNIh@ZZzb6|_5LTroE-|x9v?~Fw^jp2fpME7ryfGHT0}%q@tokL`OW$D%|g84 z5gdBJd3u?=p>Q1L*7#R+C)P*i00*x0|bjY!R0mnuvW?A zy9~6$>&Ej@;jrvhIiU}>Cba~J)I4?vP9;PRs;R#4(- z76%ZJbx}lyP#^)%G`qL7?;9RaFKcbo{|h}ZVv{Lf^ntFr5*lYCIv=^0^QkGQb;aSF zI@%s@tRc;KZ%}BkZ(j^oRdJ2Nzy5uY<7S)X>?UU2%Lj$Z4KJ^TMgYWL&eD;5v0}vZ z20mCJ?LI6BkrPtwbi;lKa>>{rR<0#kmGT{bSO@o_C18 zLwhaSJj33ruSYP`SO?RKFwHrqyOC2yxAuhFNPb2*94$Rs1rl6Ky9%>ceHCtpi~FWa z8d2M|bX>@ePZS)ec~Xp7a=eIq=S-_{W~nw!MaHn^k7hDrWeiA)2ZX|!%XTk&yF}7q z3B?jl=1<2+nT2azMa<4RGpn!m5$ZVXa54=>(Ngy=m2Ur(C6p*JTX)IxGOgMYyks!O z9D~C<=#gmoOtmRZj~6qVi8w;}3Ha$E)2KGRgG_m^G7}-$XMEN8FcSE_(+9-5`Y7~4_!arM5*SOHNW7! zm;iUEXYBOUy?kV7jc}GLDw7xsW69A39bA-rQl8IRoRUoTa8U6v`E;QQ_)Jbl=`?vQ z2uC>jo&*ixzvN`JvR|$R_T53J=Drv&TmN&X&`Y&FV`0XM_KPZ@0~kbWS%#0vN^*AO z@=}cnO05e?TRkjW4GSt-WdgvCQ93IP(S|-oeZcw1SueIjT~@yR0@r`FfQOtScfuj2 z-HPZP#S3Y?zdM^v-%9f*?@y6mxTIF+30Owz>*+0(OI1DBXM zC4{k93I@sS@+@s!x_^3qz0TR!=+b}=PL2vXg2PY87%mGTxlMPH`b1o+Y-H%2n?iua zIn!U0s@FuDhbAwy@M3Ra4JTaj+PW+xwNnMA)!ey%fj#PFVZzB)8(t>=gTtl1Gt#rH zZ^(d`${5L*ANR8o78VPDpr({e7MzMVNc%#KizB%S%?PwY)&-(>GV$;7A8ubDUyVT9 zpX#(PaapHm`iR%~M#-fH8Sd4@qZeKW);zy>sTMMd9e(y__QF1$WZ3nqg7*x&q>IBi2~cp%W6UwNtd9=|83>3ORB$xx&CJA{}he7V7@E}_wVq3 z%5nYu_ivT0|35`^_PQ~VEyS%%0y#t$y1=2Dc!w)8+GC;ES+|CR=Af~dfcYUH&~!zh z6YfFUO30M6F6itL4=wG{v4{(Z`Vy2sQQ8IGYJ#kv9=Qc|pD1gryNLq%e`gVS7UT67 zMm53tZA{JYG}dR~Co?_3*bIJ$a{|xw@^PQc#illi;dJ`pHNCL~$E(=3*pjcK+`WbR zzM@2_UA^%uD@TDU4%+O)EJNT3U`YX#O}TdEgdnrJduMKu;UYYCSh#V@)avOGXvqU> zvvRo6bNwpt-qMb0!1N40>&LV2z1L^kRVz1ZeDu$Q)g~~nG;-n>2t|T+BrfYaL27Du zsEL=J_g3J2!)_iw7zI;t`ZwHwXd1a4qu3u20iV=I&ocm1-SklU=5ROHy7h06Ht7iH z1t!v@FTyD5dFUSRmTHf;E1?g}O&QUt-p~HWo@aBL@JuGY=5hT?bU1nIAyX%(VflXi z)r9`AQPK-Y^aC!ezm17DF{A+;zQ?aDX7XoDai4~(MQr48 zekk96s{6m^jSt<4+c*?p90lvpCbrEc5m#5^5ox>f(Vo1bW&@7ihfT;j;L}9M^ZAQv zbzAiWBzujYG|5jgj>3Yn?oj{f4ltO;C;F%DQb%j*MC&{1fu+$_`cRJlBM-!-A4&RZ zW0X+pQx$w==H1vZb~Y*iB`EGLAMkYnvmVSec3R8x-0_^SXy^O4EqjMCCu0X*XR{sp zQBeJ4jPi{W#S;9Xi{TdDD7v?bwl*50jKtBF3|izR0Cpj83_+*l5&NkAXS}}&R#f4y zIca8T+MZcE-6L2YF>zXzZ`p$0phWF_Zl9$>otpF7;|1DpXz5c(k%NbOkN#_uUYuNc;5c#S7v z?R6oxd+UY=@;RuV;wBr_ayi$|J(?G=>y)&1&Uz@-SD%2if6V4)rN;2)8W6mCh%j3b z$4YUX$2_GguKba37kWh!Eb1e^__jJ?Pnaoi&>U%d=P<4Wig$px?)=GOGecD-g zp}Nk>{G!)3+;bQ|R(+Jk5yfdpl=JM)74C*S$nD!)uor)Bi;JuMc?_<5`|$lUW*!Bq z$5K>YyJTJNi)6}D&+{HL?qc;(depvdT2y6O2u@04zoqF@YHq!1 zonv)uYB;$@lxI?`)2MQCrSMHOal5_1614ItbbF!ri{biqUh^>eP1b@^#{_xn^VpFm zUET$zSl=7kUkHVCg7e}fUh2>sHxz<->tGbRl$g0(zp23K~=-=iRyS<0*&h1ZpifKEpE}k|>wGD)dW8pDpv<-pUlZz)J zGL(;H_TnD_?x8ZI=EG3uD*IeCCsyC!=W_eA&=(&zbJj+yWw9iw02uSqwO~5nls3F@ zPV_y`2YEFphv<+airmm`tM=4G7;SNzw30=9q^#^=a#RD9i3f6fm3Xkjy`6pU`mp&K zZObET8$+?)K4>Uy>iR0owBG6wovB0A(-!o8Xk+G~C2EbfPuZ`CCQ;S3YxlE*0RdU3hpfLWpLHS@XVEAz7Se`Ww3aw zM34S(iF5&ynNNturL)p377tq}8t044BLVC$f8d-w zsW-IL(INZd*ODxA-XNK_0`(NkpWtZL4`AU#N1{y8#V1|jOObVu&R!vyv43DGp}$J- zGHUtr)Y#wvSlw{l9XGhpE)(?$-Dq&APf~Z|ZBB3^PBUYHLbhgm!gK%7dcKzo;3S_o z;QHE$PJ2kZ`}s5Qw-P$@V>@d)JtR2vIz!;_Yk4wHeKJmeCd!@EbB~FZ{?Z#FeLsIv$D;XqN z=BCT`L#Jfv<|H^Ck^mh?hIwxvl{d_COm3{WjuCsPIb)4on?fx;||HMumsCydFo}?Ut z)@dmGr~q#@t&%Ls#FskZQx%3pxDC6mhr!O-Lj#*v0;h>h_<(~_A>@Fb#}DqT;1}*x zmSknXKtIX03^&1o*;c-^biGI@Y;_U{<)`p2|0Cv0 z3eH_PrdsuE7mZ?~v74j}`)cHa-~IInGa_H@un*7kgcZp~Xyi2ui=fpOO`T&(TdD;^;`~y6-WGNKLg40ofsgKWS>(fKh1O6{hcahsZrtZ>8KOVHsQI zvEt;fQ4U*Z20qJ(@`OySuyhqT7Xe5DOYau&1Jp#;UUpST*S|k$d7BFZGhfr)icmGq z`KdSq&w=q;*k-4HJ#p6yf-r+e+(k`$9wfP02F% zuR&jSLZ4+K7145bp+)Cr1dzJ%`a$&=BF{xs_S>2mkgo3iNQKoRlOMsichf)X6CkP< zwWoD%fVl8J-P!z;h7I20ZyTdc@CQa(PQTY*YR&%9GyzW-5t#j&&mp0Cuil{+3}9STVazdyx?v5vi& zT7>U2%24~B^r`9Aa&%CocIEkA`Jdk`HVQtd@`rO*Tby63zLY$uA=`CBr3IK0Q=sIt zpZ^ZxJL8g+OxmP(;pz%g6Amf(iM^|R(5)0vEc+y?gXwzg_i8g^A$<2ZIe+D0T6@$}IO+2z zYvbOQdE%P=33TZXR$iFhRNNZU`qn7;aM<b3Q87auz6~MeP;(+h;j{L0 z)t_tTWwi?}($7_Lsghg%dFi5uyNHgTJ>mwO3&*7BwUEFB+*I1zEu-siokAV=@1dAA zC(}CUDPDWo&015RyRa)SzGJZN9UVGB^N{p;#&xuO z?nXBe&zZeb8c_Lu?o)^{2i=9(=zCY)vx@=?OPyGQ0?7T9d=hXvmXx;s@ig0FZv{{m za@Ub2ypiAs5<(FL=WpJZ^je+}<>Man^4t$Q-+1zCW8MW*ze8s&%1nkdJzvU7d4JT! zTMh#&E?pI7d`MZENZ+fN86G}P+)w*5ACA{M!R0}|keDwEuUG1|T*)2O@O9z-!XT`5 zcOppdisa@=_#9?3JzY;5H3v8-O)ms1g>bKX-csx)JM~96zS_O&r<@b>cF=JCW^I9M z|J^~2WNRFbwe@V+PmK!iCmExXhOYP`IT9@Hop?sej^`FESN)P@t?#uYpf7bN9&QC- z7X`3Z17E5#Ij;z+!Lpc_9GQSK+Q8;NXgUcxKCI8`uR*dR)X~g46bEkek411U=6NG* z@y?Tst)_ZYCevM5fBn4CY}tFQve#qxrRsI=pqiJjEF`(dVvPFP5@wA~Q2s0U+%I(n znj5lqw6c|}CG6Dni5q|p*Xz19w~Q;M{L;`dk-#s_ z7SJoUK8KiZAax^|D@2O?~3bFF3)@M@TA`-A_3~tggG6JAGXUd+y7v5 z-n&1urapRIb9cd6L+VnbdX;=PCDj7}|X#x&;3^znAKvaN|h~5C9Z)+7D8b zdWqweIAFdWI$+(fjB~u|qt16Chi0?qUu!`nGq=RF_{(%-L*wwT5+*nrM;)IuprcT> zXtsAgneH)DC%XP=c9DGoU(H54b`?&JPhdQJ*?zitt2D< z?(Q55_Bh@F!w2{H=1Lhx6SwX}<2meW=#&1qfruBln3?4IMCBbVBOo`~o+#oMARJGx zUZzyZ^QjoTxashDMT~(Gt9;N0kQO`sbvjuZ`N-6@DSGpKpLL`cFI|$qEvRT^ya%DhrGr_Sfo31p}4*olpYg-Yp3j{0DL~ zcIst(S25pydjhXYsS*dBouCUbEH_Cr4HSnC&_a8a%!bgd+P@w%N!P3w3@!Q;mS9M9 z61~_)DtH1^YLW$giYQsRwOsm~Kq*L>kgo12dXyEC;6#1DE?N|(-hbQr95!GKR!K7JP#|mYy6q#q za#Ek>65Vj#y|0I3)_HJqRdP|spZDB9@(m_1n^I2+ba2L0kaq05KhyW7CkZrG(my9$ z&^X%C{L7%4kUy1c$*olgMzZUvjCXO57Eh0h zPze}A#`Tg;{k#PrC0PSCG{9lQjTKavxl$)NI*8ioC&ew|pYr!@fg_ z_&VIXa(?V%xLdb}bn?0PkNSBVFAaK8H=4Jn?wIc3lKDJJ>8EQ(w@DjMcx!pt`B#>P zlTZOEsD|x0YLK(zFJrkE2nNTGrLj1E=#hE>?r3k!OX+Z4#8=V`hPTJq$&%a-%R_mw$Aw|*q?XK~RbG)N(O zUGK^9YwjOrUcUqGKQV4&u1T%giz1xWXxp%$Y^ZW>clL6`jh&YY3jf?rj$Z}!4|9L3 zQSJ4q^*wv~76bnbu#0gd2{%{Hx3y)vH`Gv9=94bh?|P+Ij4Ohvd+Zm^wd|fp_>#~7 zoP8vpq-HT;6e>eE)v{+3!}D}Y;YtkYZ~DW#2wl=A>2Dk0B`ZiFvOmyB1fRl4InlClm*OlzJsLsy!k zoQsB;c>VBEEgargOfhJdNhrlWJWwpe#bydvB}yJD5wll4<%F5m*|Q0bI|26_Ivevt zYoSxIo_7Xa%IqrIOtC#f=1I-3;g4B8RJxI=O_rV52gmO!GgI6IYP=7BxCx|-Oh&Et zel!2gh&c7%@_U}KVI(i)p}9nUkfnd-UfU!^XK=q}F=y=CWUh8wQOEOD{|7j;*}k=P z#TID_CWY}z{$;=~@$`m#w)d}VKa7TrE8ed*`B5YZzfK}PX^LPj0$TjBXRr7cswm_f zOf5^CJ?npU`-N_+uN<&oeTE!#<8D)@W5}5MQ{TnC{htlrqR9~4Vea93)umi4%S+XV zLHREF4G1zK?RAgOH<4S@FI$2UGgVL9^~WXqXRKw0feJfs+yE2uilJbx{YKfosq*80)RWa2$pyoh z^llb!9NsLQG5+OoJHuT3$jz#g^eW6q(Px;;KQm1ryFroB*i!BQt)`$#^Lr!=P`S*s zL=>WI#saf@R1Eh< znSaCgkoIt#znhJ9@LdbBlzw9s=l)z-9M}yb+2j58VI^}|?yE((?DF1DI@-6~@q_m% zwQ%kY(Mgpqk%{l@cT2ZatdP3apr}MU#e)?Z3{~(siXpPM#@pj)RLlL~u=)xgYv1Ay zk~sHcdJMez{u1VmR3+RIcKtXZyKACZl^ovVN~sau^7B>b(x+*8vkZWnK6{saWTBQ= z#&sNu*gGg_LWFn$Xc+(ap3VF>AGzUL#5FhKj>DO_vO0|B>(l2S_vGJKylpz#h+nGE z<=b)(DEfA0e^&|W(M}q#O>(Y7JsC$|#8y*hjuw5D)0c)WrbBtF6V<&vy8G0}rV1zc z##=NJuQLIwcGV7$?_Cwhk5d{V=DUaV$s2$dCai)zijQH2v5Gqz;rfo14qe~zPk-6y zSl63H9Ft((*MQ1(;(aU3@ayY4+o1=Oa&T!~L3;}7d{Y#gw!No>#k0?~cJiE0x8|3Z z-4u6EN}B<;*A{`*uiHOqWjm@=kh)bB=)Nk+1D*D!ktBxr0FDn(FR~h2P-=Cf(t+Z5 zO>M*-mLQobo2YapT(AnM_y*-mr)L)Df0Pq9pr)T*?G;a@D5!~WSiC;%HX84@)DS#! z&@9{iVth9%YF*&qs<`nvYOdpD$Zg42Yjd`>7LZ_219Q(NQ_dP^x+cX7&wR}>oXhn< z#-@Xatk`c=j+XrH$ow}p=4oaVgyBZ0-j&T%?;VN0>#-8a0kR)FHh`pIXJDRu()+;_XsU><- zaVl9j|5a=lE-`O5QENra?_6WC+N-K|6#P>-IF-g}@Gy}zV}cFk!Lmf+4C?|2c7FL>G#(d!_qpgQh2pJWBTM|++MHT1=-cp+d#2Z zcwbW+_90(W^Pie}cON=}UKShEG7-}gN)D6GpUclRZ!pV6MU=#bn_FkU#yP4B<3h+b z=+x4VP2Ul}v^{VY!#$nFi?6cFa+nV9bXW5AvI}G|);H`(-pzWG!7C0`cm@!dcs$Mq zKrO`0H6J~^4`q1o$#{}MclZxa5A+K@5enZ{vj1w|CNQ|<`!q(FMZPX8@_gRxWpz=q zn}*dv7f|iLNDX@RiAo-1g1*OxzId3S@r5?_#iZ~9pH4}V;}QX> z;DhsjDDO>(k7?OzMPG+*KO50H3-lsk^NFE}f1otWRc`;0_V?(U9?MkY7Vk6lpYc1y z@qPUhH?OZ%j~(}IGSb8Pes_~q@kpj6qU||01dN73Z70NSo+%dHRH1bR)JFZ+y!mH; zZEf{X_+0(9hEWu_m$a)!eB|?~a$Y;tA$vG3nsxe^uGLXJ7eAhz4I}PZ0Sl79TZZ)Z z!INeR8y$%bfXz4)b;CXC{ze^{R6Zc-S2c~#g-4olW86JVw!Sx+DW6&~0>$X#o2S(~ zDq0~wZa%=*8QvnRV^H7*-j=G1Svsa;Z+J-Jxn;TS+{#;lO4sdOq%o2;34`I)?On5$ zNyGFr`h%7}_BH=b2u#gV`{~wi#vK}PRKcqMXA+h(3jY=XknOI&IRdX1GVl+*W`M5j zEjhH%iC3-+wk%Dv)Np=%1kRxi2CO!#2cCKw@M;2Eib&sloN#$_g#+04&3*CPN|%O5 zw#;VNQ>hFJEp5ULl_FSxJ_A_bQE0q=*XuTNDa5Jj<-%3$V`6oFxescu`i#RnzNBvk zkGP8vjw&i7u$xnP5|pj8ZQv;y26X(oxBSV;wZ$+g`tQ1_0}2Ad$wfr_MP{^QN|UI` z?DD<4W5@>EXQ8e+avtZ70P=`*sEQ z9+I$CN9|U8-ouscYBorgN8%Cf2pb#h`RC78o?hQHd3x4M(7h63KUBy|H^3#m7Qs5eanatX(6Zk6TqKRXGrb4)#9%4 zSal-D8T|fBjcpaLakH;PP# z;ph$DH)3MJq#C7;ncnYbzI~EcTP0p?89TkIG&JJjCC>68TEy|EGnLFEV^MkP^FiO* zqO8I9Ksz$Qt+VVts&-P%Y&)3R`IB3p%h0P~tO4im z8Ul!X@n5NMjQRy!nkq;!TK4(PoxO;F25L^dB2m}r8ZowUb z4i?O? zFZrkV2x-nQ+5lDlh-{6HAl=Bp|9ygO_VI*RhAq10{Lq+nEs4&yqUk-szBI^%?)yv(;6Hn^tbdHDQ?Cq&inx-^xh=mB z2-6)&yak(JE z(H@QgR#hxe{z4G+HVns@WHQBF4C>o@Cku~pvpacIpY~Tak8MyvSoB}1>fd9hF?=|H z(W$39Q{-!|(#M9dt@&Obz~*~Lre+WJjx+3swmcMQykj`w$&vi-YlKAeoox6RAcEJyQ&z0q$p1{X4e3qdciw%UW+8PJ&`;IG)3Rxv%5 z!W;tioBr5^7Hn*>&Ha$k^BUcE1d+;B^VpWf;4H{YHblHtN`#JhnFV(r@OxIuz>V#d z9Tr9uD@IhIhPkg2Cf1B4`y4`@pmOIMG9S@(MUOtAlm*&X@nNKdZ!t^VM{(YyBvMZ~ z?URhRR03{F8K3s+oRecqoC9u`lBh2>ieluCX|)!*t0m3>KdSfPPw5T!xn8OARz3ve z+EZbkVapN$a0(qYauOmtwVTtZ>q-wQuqjj;)kB^T@Fox-Vu>Y66A8LZ5)+;U64*~8 zHe$s_%(RbxpqZ`=xzYH9j$eGplefhH0X zroV^9JDhDM{Wy-oX*E!T`sB0dnlbGQ_jnP(4~el3e<|O5af}`2-UlLtdn&bB(C9bR zr(~|;*|_mTR^nK~mHZ!`RjPw`&Ym?wRhzF$8)P3Of|JXJO7C;$lIlv9TP;+;&vGYb zu?blh6VvYpyMa@kzK;DxN!BhZDDgZO-zLymM0J|W?l0brcMNmva5c=O@f~+Xj zcLJNPfh6dFmFR7yAmaF`LGHBi+A3mK{AE|_h}wk^;P(%1c*{v%+;*OKz4fucpxK}| zuji5|zQ*n}M7$r^TIE;zk{;^0$GaeGVwf5SNB#CGvIPsSeJbgUun$YztE!~jtfhBg zJB=rgB9&ur@czp5#E6@whMGOd$mHutAtP!NJ;R~A213J%u~84e)%JNc6#3bzw`sj| z)mePAUq(E@D;T9fr28zPpkd(jrRJV!s)PyYRMysGO~6~!GV^wO3EI7N>yVhhHI3$9 z``}j^Av}!ai=g5;j;x&slAc;$viGjXxzlZ9BF^cTg~t#MQU|6tA7l_D%oSWcE?{ew zcS~}H`1YvRl`Fuvzf^5+vX|BzE*tLSOn&$TZAlORer$*c%@p6IuWt4t-WHk;?idt6 zc->zL_m7k15cQ-^Bs_nC1b1I>i2NFV4evCaraVCN)}N*#Ks@3>OD2#uw~1&KFyL4^ zi+IHq=lxNS4q0>(2jFP0%X33gwC?Md@~W9FA8a^gSGne>n86Z>6B-3Tw z#z+fo(7Ty4u0WADk#=Ax~9L98c%2k-GO=*hv7hOh-gcRhp}pKN;`8d>+&{skZ38N{=*?-A+|xx;s!J z(~uaHE`YJGY2?eb1E#6r(PZ>t5qP?mE-wHTR`a$Gj` zm@Nn;Q(+#IF==|=k67yMmp>G)Z&$ijn=BGl@LqL(6WUQ%#vI}%mfa_&o zXNN+7b^FyOZ)af1G+xdlMjove+*wL`FLHg0D?ybiCQlYXjH zHfYNN@w83@$Go({XnRw6TDT|> z+tsP}W;TBhYeUTfq&mSTQbtV}raYR?4LGw4N0TVEe~q`-A+3bJrc>qKM@vvo-qU*o zM+i#Q2P3E#J~zK#{(Je^c39Lww1@9@L@3hE4A#8R0D}wS6a?6JW~|Jow)I~r0<4*t z-rX#SRPI0}K9!l@ZF0#1BQZ_~02!_C)t5;PMe|GRZc)22U*0A#Wp@P$ z{!9`lw-F%xHxDR4mh}y_DWLZDI6nqNKy!#cdwHw7xxZVt93{Ov z2TUPqW@jVs;UrE`ZL45R>eUQKYG5DnbWALh#{OZ_jOW|ma99%mU zE@v&65Vd`yx$=SZUnYg4s-A)vDD6v?W=w{bX_r|*%@g2q>~)4&3-zf`SD&buu*0{m zwovXF4>q>0y!A)u&qtaB$6SzL(NJQ8M(b7OV!q&WMc)^eJ&K=ZofA8-^r}9@2IFO0 z*4wGj?d@b?L}s>1tVW3jpE1YsVLNWj5sTycD-83^Q+5ckUELn{AfjN*?B0{b$~@-+&}Xm0R}%0cL;nq+wXOT zi|9Y3uT+&SUQnHJlD)>DIl!;%a`4t+L#;d}vNrM29bzThEU6qKlGOEROHVg>f$K&! zMrGhN)*1bqtW5l2%%4W|Drx$XvN>(8d(vOoj389hG^(tA8{}3e!9Cr zJt`>e(sNj88wfZvTlH<}j@Qk5j%{0kcT;|t67kYYtYh7&wm0)Q=hdEkJJ8DMskcUx zR7#wXuU#HHhL-+U=c4E$7qfISpQ`ViL~P?Qua1$&w?2yIq~<;sjY}BzBoV4ltBT4V zNJ=l@KIYT4qRecDeVt!k1yuZXEi+VQe)mj8{?84$f;}&)D zLUj5fc>QB2P+HphIg1QKWU!m!Qlsc z10XE!*Kn3H=jl?>J{*s6+1PYOxg~@|Awt9`QdoKq{L9VNc!*6`WqaMUhyHOL zfe4Syj8?FqI0^hoZDaoGGb0o4x@){gb(Ls4Q!dv8`E5^^I(!UyErEk@J2bIB?$THW zj}uY%&Prv~=PZu5HkDOPsp)2+kMu1=S~8MKVCX|ucU3`U%Qcl=fu$gV>YD<)s1pEq zGS?OW90}5Wiek}Ivz;Dev#eIQrlzPFR$6YiuiH)oqk4>A#>ZYjzl=8}<7eqR! zI!tfxW>gc!{v6OFQwBxUXNYMDp2U)1B03FfWs5PoSZ#VvyB%(bC zNqS)Vr2ymJYg)w-66sIsa)^}LJHsc|{FK{Y;)Zx*`3zsDwmwYV9+s(Lr!~S0AHAr+fQEQ?b=o&GxVa<4hdLX8N99aYyuSd9L zfLgYzZuvv0UBz}H1hOT&B6tK&6mj4#Ydv|iCnXdBm7g}#|FDu$*$RXv8aMY^@RxJ< zoQ;=wbatx#s(5_$-`AQweRhc^j+D}$K{_U!PaK<8R!YhSjly$1fwuv^vv zG-Vu7)%q)q=V$vwf~_ENy{0?Tb>l+ybO`1LaE33mswVo#A?HwuipX&5{(jWb2wP^x zPh!1GgXbgwQ#47D0;yUw0bo+t$3r7jdHJBk$~5TgfZgx%;^0zY^%6@@`p@B|DYKFb z3FQXiUU;d?!s|PGRUnc6T};Kb4$&9vP^ZjzL(v}ko>7_xTzQ~!Dw*jusuc7m701nT zPN62zFnF|5Dz(S>I`(@~VomqJSH)nR8i0v)h9y!T^L z(ndi_CK3Ra(F)4ocbB(`iG|BCvf~PK=&;U{<D&?J#G;L-UBTwMR1q6{P^Rx zv2XIAf<6kNtFonBEh}7Gq^6DCVl(?DL;n&Bn%l8u0##f%@Fi!}2(2l~0gPMPN(0fh zIv9u%okv8FgZ;j>SxTcOsCba~o||J`lt{SnW31N0sqNb=l5ra1H_SKE5|yun6~k5( zJ~%Bvu^&I?2H8xmzokTcl9NmgflhA&Pgo3@4#?2gTIWB!!)MYIhCD?F_ZlmElDulF z3!ss+<6Mv*He~uhA(BwE&g@IORiZkrsTSk}lqk>nRQXD(EUt<3^747NZs;ZF1Dz2!$ftW1(rKdea{l6|67=6TU{_HMu( z?+;J=TBE_j<@#l3_wL>+T+T%c5)bs%G6TdMqFtDlbGQ?z`umc*%Z^}54PaeCvNNkXX+GCg5PZ~GC0wQFvjS+jh_Nhnm*DRrVrgMM8V?%_u6 zULO)nzpeR#NGicILdi+O0rIrYEIQ09KWQj+g(i(sHt8U*Y}5QOpNkG*b4)Pfz6YbiAmyqJbS5FEf7^FLwz6 zmkbHYWOJ)=&68S;Xf4^c z92|_|ey`ELrXNqHHBI2Ncj8Wb8WH==eFE?H^D%y;oICFCSuZElYRKItISdt&9O?CVBnUen+Zh7f(uu3Rug{w9JXDWD&~31N*AdxNioB!?^fZYvnVD67q08(HNl0NgiRIT@a_iK8v2|7kXJC z0s-X;>)$=VV|I}KOvF#ZELEtTCO+4#c`$S+@Lk1sEix(~I8R7^3B@=*a`2@_SwT4zcAieU&$a8lCQOH@%tq?+j z+OHKq8dws`O`hajb35pDdHvnpex-=GB3#2B73kc@_7m4ve&42sQLR{HH5FBrvuSUCA$@X+mx4x;YTdsm^d>bfmhvH=lAaMtg0S zBjl*gtsvQx$B+rGy${x9mVPw##F@yjn|m1>3m@g@!j zKfRiVEIZf*9Js@wqGdw(siHwVnDd`UmRl$w)4$}`MwDTBd@}Hl&6K=OkTaE>?r4z> zUo)tU-vXvMdjDIzzAiDcy=0T~7K7XwM-x+d6ETE}$=aTPP-!TGM_eU{B!b~Sg&OD$ z36e7P4(vadIe^&g5V4SQ8&3@~5B&JBj!eY`PZSsdk@fue2>*>%mMpFxop^W-EWVN1 zIA^@MuIR-iPS$<%u)Laql+MvyntfoYhEmz!*3S%|t&qBr-Pbnrrgr@)_QuORCH0zL z&Qz(>OK%tAP0>h+t^Yi8Gg6jftrofgW{PcjL)KZ@!Vj+FeLF2Oi2!cT6VYY8Yo$@X zr~W1;oXw@z=RzE?=^%mYpr!KRRSO zp?gXpAtLyfYGfYd*bV$6_eE_$^%!vn3H6Cf0WeuO71X)jfZc{Y2jmF9yto1WB;`NN zv+j`IUuS$CjEIK$lR`FhZdNH_T@@<#;#-&&(B7B?&Ojg%iW(QY+yl5`6u(8AH=NNI zk1;etH%GtTxA#}yw@a~kZqc*J;%TzWOgW^d4YXp0x7G$o7zBQf!3Z94L? zE@=28ZdJ#827d9$_a7*&zq3CLRldoLcd3Q$%@${O(+*Eerm%z8KTt0x1VYX)E&8+l!O77Z1dvvVp8sh|A^*7mta z=a(|wi{OD~Z7s(6pP22&5F1plNHd?z2o)!73_;~tgTGFX>l0d&qpc#5#%`Am9?7CX zZKgNBxl0tPA5(EdM*u3rd*)HUS_yqa^T0DKsE$Pr0u`dn#6X3=!h*Zl8lmLl*LXQn z*FQcvve@`~>30Z4PY*R(I}vz}TP#1%Y=1n@Y{S-8K5P+o0#I#g2R2soS8nmX#=fZB zp_FSRo88JRA^{T2==_ln+nSF8aI>~?#*&#qE0r-h4?RgCZtfkL^9KQs7%|lxA8SC? zxstF-EpvbP%vzIYg5razT(797qGp=mSY?gs}82fL3(jo1F^k{)&6^ zWaf{vE4YU^N0tlO(2zVXaSZ_k*5g!MpIHi-wZGonPYNtr zV9`Vk8g|ba^7dL;YSX#DMQ1h5A=7C45GB+SY3LPp=?X={F}5rqv@f(}g2B$y;EZ=n zTgj^wJFcF1GB1>GHo36XCEBq2%np9OQCn@<#s}7`<3}v`xP7DwV|he$uO*8~#j{!zLK@7us3vCQ z+Fb5tq}oM3~+naqSuy-_keL_P$l}RakJD0E>#51OT1%_fd+TDBu^uy9Z{rT z5~?}KdU}If!B#&^Oy{BY(&--taqC8Re)@Ym(8NGnl~dob17*0y0vSLI!PZu0Vk2{) zF@Z5itEyU?_vAejgxRjf?Cnk456;$`acy<{mfSvU0^(z*d&BnfR#!rk-FuEbR*h2< z)B7T#VZL>g_d^e~n(s)J`wUQ5d9A)Hy>3TNZCb;-LCq)9u{=5Ypf(Cf-T&2I2yg1a z;>If&5Bbv|QV%nj|I0Z0ZSbLAtSw3SeJJGvcFmQrjNv#?ukc{LbM4db>vw-d3;Mv6 z!k;={qpMP-*Sch&V!4a#Fp|gSEf_7S)h6J6E5E)eq|xkvWfXxXeh)5jAOThVcYz8e zUNp&H;xGj1S(+nxvy!&<%e!cH-y_m}%y00BF@Ex=X-<%EhsO*eu1Kc$xnhakCoK{< z{q`h7PT)qRDeE#VU!_y9lT_Cu8xe5vuLlspFY}ITRS5>Jqoq+5pas1~Ek=1_K%sze z_Rz3vTfg!uC*C?j;_#<=dX4Qtq+L3uad3WecARnc9svOW>jwndSXm8qFw|$|-;dCNn9+wfZ;Bc`gF_mB8J8V4K|DUv!dOFz+o46$f-uf6LCndM3FU(L(7>uoOkRZB zoD8M>fT`0?tm;rbbSX}KkKb<+Ru0IyR5-syWEPj)c;4ryhG_)se{{zuKhfD!#LLcl zB$g~*cs{FVt}4slQa>P5^p{HwF^yp~j)O9rQtv^8<6DdO%1?gOd25UZKt56G$T~Ew zoxK_Y!@Ip+Yx@MANv)EiRC5Vt_T%V|T76y|?M~IP$NQ4lCgbR<%8d)FD>HiQ=ef0P;i|Q`)BHg(;v2oaT>*FpBz^TrcHG)DtUAOh) zLfR7rIjI$cC{b3I;wF_?$%}*VL3Yq!G`EturIgStb7 z?K~W{b&-S^oe^ayHDi}bOwI=pnp;V0B;9&5*V61lESP4rdI|zYi(c=MEnx2%e24|W zzv#9eW08y!K;=#b-gNn3(OvG7{y$sUh>6e z0jwyq747t~9}lTBvJfTgkq7g7g*z&sVe0 z``UPLFdZpoDwDe&QuTh!Sf^)eJV6;12kAP-RGo9rMK*3OmH_c|-GZ{p1pQo!pPe^e z4sRIJzGESL@^FOJ6KczCk^$ytDK%CW$BEUV?uYSDTa zLoy2t)!#m^7Z$YQTvt{_B0MQfjc)APUT=B^L>#;h#ZVjJ#oJ%}OHbZW4Xj^@M`+^?e*w3fLBlq@B=pOH#4dSCoV0*R>qwcPM#q8MS{y9Xm*=xBS3wrX1 z$DP1{hT2(~i>))iqJ2TSTu5aT%kBzIX85fEBi_EkG>g|kxrYYrl;!@Nfoa*5c}gGj zGq(}-4D#PX*tO%%q4qoa8t7VIXBvDfKo^rfDLI5`Ry8 zmjL&W`#7q~?2(JE^(Xq#0)^~V@dF#K1&Q=vjZRmkANg5C2t>)z-{V{S8C{epS^@Lk zz_e<*a(pg0sixHFnf=e<=5ZUi+*IS!N5Ah7z5=CERO#}E$&(8`WEU2N-;ajoxps*= zb;?)3Q4)`DkL|R7SwFF)zHNeA_jMZ_pDQYRnlg{wAtLjhDk9H$d%PUXEPwOjLldVC z`K`7QugQ9kJfpNFRBTT%euYlw)ajL|{i~eJvI>w86)Q|}XuWK?-@(R`G+fT-7&E^)cZ6O7s+eCI znw!a90Ig;Vse6wpNB7#d`j@ufQvlMa=~P1B0TfaGG`q!$>|@shoAuxX1LZSOY!n(# zC-OLC^d@r-Qx@+bwSrgnq3l1cE{&dw@&Br;^eWdN1Bp~?KYbi5CmbyNu(lWQxGs+Q zq3o~V)q&LqfQB%-0?rq_J)94pHV56}D&kV4_Urn?u7$O#uE4c-2+5AiJZ^PEFpjI{ zA#=R%KuAvVPmWCln0c@2$uw-^-bmX`IQb#!FOI*H!@qU@O+1dkh_81^`odOkd|mKy zHVRRyfG$AaS&xj^<{K8bHX;p{B9Hs|_5BfP(UrG2Tavpy2AU_z@ARzo};^saYx`e2v43QUXUdcv3U2yCpU0&Lp(+-9FIbex|(R zHf#UA$Vxi{t-ZYNWemraW+R3EQ*_r94wP(V;LVGJlc=x9^VNm^fk*|6e&2;shd43< zNF|iCSaCN8wAHV{!O}*L0yDJ6=8O&h2 zQSEc<+4%{UR#9Zs?FS%b5a!z#5iepk0Ed&EFRs^HPepUpzob9CE?pSb3pqV&$r*9c z4q9y!Fu3Rsl+!y$hjdS9+7$1uDifss&XDiDVPvC6el2+BoQ&Rhc#W^>1~gMpq;7n z2N2(?jj3&zN1l{!{bRGsvu;V;&2k;LEA^rdzh?foH5A`aN$QQHaNj|vPlBsCf9S^s z2xNI9x5p_ou=Z?IJTE^0byZ`;TSq4Ft^v%6E`!&`dx#VLCndE(+?Z6sfN*Z4G~u>S z{_(6=Z&=2EN{DORd?J1q(2GKd^Q^+kyfYCTrq-*avRrfcXp2@}1Hs{!J7*-75*80kpw8SZwSQpJ&kKwT01 z^tmloi2ipwWRU1@4==FMiC}DTc;CqUg>11Gp&s&e$F6YhiM7Ur_2;c^=d5!bnCfle z?8LL(N3ucyuB<0dk6lEF57n1@dE6UdeYilZYu#!YrD`(nT-UatlIqLP^re0S@_1!l14DIG6=~qDvWIogb zknsy*)O`PRQ56i9mz36?hFH~xuYX69@vyC9uc?RB!UVxxG}S<^Yof~F_+<_0U4ljh z3*%il0h1Nc9)@6x-4^=MYEdzWY7Z=;gUxl`8+jH2+qQmf!*aIy5(a$#c|~IAb`)ya z;JTh?2dMX`kw46CTi8@);As6Jy_5O%iYdxUJGritB^|__n23wwu1q4kgz>vwmX^_5 zyh7YPIZ2}ZO_Rwrm6r9WPyUM=(Ejl_*X;ORhU~|l>3#ZKeqs8t z5*f_wPFV5dd+>l2C5jEV{4(i1G&|s~Be`ww!ZD)TmbTAt@Kd-jjVPTh+1pV-@vg|~ zcq=PV%=01J>u_m2*<)AVQ~Gg4PnKr)miTE8Ly9kXP%@i@K`Q3)_(bIg_q5ZIvFJP- zC%%*f8kKjYC z2;>e>*aTx)k+KBmWH9_PXE)yg#n}Gvyw&WK#es02THZw2qSpXido83Vdxl*RU##W| z=yjjnhe@Xkbtnla+l|Je}NN_DjDl8SYA zdIyN~jc>9%BDUaE{7qv~X_QF3Nt_|`-*?#8`70ZQHGmf^{8H7+8WKC|jaX-omKX47 zo1YafWAJ@D8{?O$#) zu;vZaUU2(dzvn`R`n@)X?IW*6kKR`!ClYFDg2yr|9yFyJTPF+WU%p{5Ci%7Lvgwc0 zXt?R9YhyHOMpgaHFNt~~fZscl;P8VI^*>Zs(Ih+cK%2wgtn|FuAqhmEELpYR=ZXAT zs=^?W{u9g=DFDf#F74}XBx-Mf?IS(RE^)pO6->@tZFsS!Wl&^?`29j5soN&5dV7{9 z$7KOQhlQ{n2=0f)e?xt#*H6?IK-Gj^+5^G9T#fF26h=DredRl@lw-5CuM#kAR)0r- z@1K%6#)!qZuZgwqx2FGiJxmrarT5f)^$ba6^9N4PX0Gd+69wH;AxwRvIvM`_tJad^QT^CxQh5S zx#XRyHzUxcZ@o2a69-e>#Wa-7ICGf8AYR__qV8p2Cd+v2jdu>t6^Dz(m8rQw$_vtu zf9(sug@|JpPuO&@Bo6TkbPicF(m3NWM_rtJMb^VS#)E`zLgx!q1kq#zCx84t3{OlX z#Ov*=mnha*^Avmh4;vD2+b6O#;dB%L;(1m{cZkE68oNHFRnvRdBX#w)StXi-sl;A zm_x)qD82wuWy~IXG^u6hV%N$(>alYd;!EdmZr*CLwr}`el)sIto(m4Gv@Ln6Du9uw zsR7H)A1U{PV0b`9)xUpKC)c?=gO*?OBnBYW0ekEsf6861@cqGYUiSYC0TpLc1(;;$ zQtzhP6(sVhLvl^!`{IuKPlQG0KN$L{w9vxDI2Fq zkGPE52k+BQ!Z?x7~#L{9mht1y>sL_lChC9 zYbO+4kZVyV*t>ZxiR`7d_KnGim1T%Q`uix*Mf#1kr~GFI>G~*JSwh ziBIN)78Whyx!7O!(++6MD0n&Uioie%Ci5QZDS7!-(c#vhJHWb8+q56nuXlU;3^DHF zrw54mki%tU{>sT&PA2D9M_y@@nZ7w-)N3(L5Llq@-nq1J87ls3HWrbLN~U_Pb1W?E zxwaT82S5~)j=|bmulAD-dGhL-{?m~u0elK6$)h_S%qqkpFTAtUOIrIGE5a14Rwy0( zQYHrP^!C=}nNu1i=MZz8jv@NC`<_Q9$nvx>5ZYpqh@%y$cC>H27NAp7re;xb0^)F7 zzH&~0QG?85@A8I?GG>jwwI~Lbk;`PZKcBF4V%2l#*X0LNvQ=(q5cqz&nT* zY3>VA81ar=ASw4_hS>S@o7cfk_gi;S1Mq&d5(SrIY6V!H=7|;cCiX9{F5;9j`|T=Z z%dKXewwO>~p-68N>vJT#`uvjOTX||{6H1=)KL}+1FVLH_iL3{vRWKQH7nU<~Z3dq{ zhQD5@@-qU@8IkhWGNrOrEBVhGE*|S(gZ}#keg1)8=AES_w_8L` z_+w7+(4Pu{3&>V-RYcq7IGnxtD=-`;ysGHC5)fX{25uh17qd0BRm|ouOUPHMZ{)9`UX0cJ#>wA(9zw)AD0q zORXirp|SP?B5R%fz4uPLe8zx_jn3oCpuGLJSXYs?kEg?Iffz)X7}v#fO~olLysO_j zYCf(dG>hivVW2i*M*~k7fSU%VpIqd2sHsyE!H= z!%?bFX?=gW3c61A%eg->Uh1dhBH~&o!sO;lWw)`GZ3(J_?E(n5hgvZ|E%X^s2u&A^ zQkm}$#`Pg=Q*prl181=xJ<-Okm0^QUm33U@w^Dr=4gfotC@rF4q2+pBG4^1OozZh# zD=UwIVgF;3RO}t&5UuR$5Siq7clMz0^({)R{#W(oSB5M}VP@O+N}_MPT`cY|9$boM zG`sgJ975&}+VO9wrlASWTUROlfBn?JWx?wd-wF;H^@Z-pu-g;IbaSONE1ZYh+0uu# zhs^#=IzNtc1RKoqe25Hmz<^xSugsWo(?O%PS2yNW3+hhe`d-7aZ%YlaDyw(M@|e#< zr3Kd3$z*}0zBd|~Z(#3w`qrP<31^Q#6c6!#_Xc#8k$aJNc|AI5dDkX394>n|1_0~+1rgtiNDrpQd<@i75+b=3-J@p@)hZgK}h|73kX@Ob&j? zPrl2*roEBYNs+ny*Pj%XK0j54km8gZVq;kO&~$ZHJd@77EnX%5l5+ z2;15sg@6kd@kiv*h(+tNS8#TNxR@`HJ9*cc=e$mvOY)d%w$@u=+)H z`&doHscY;*pUzg|I(Hj4qR!N8-*!?tz1a6*QtqDDaHC8PR7sW5zGsk@bw_*zc4l#e znd?3lFKV(@4_8vHa{%|As-AW2x2laDRHbVx=kUTgK2X3#1nT;+S2}PnIvg@Vm+Jh|6!r5pW-(~sD{nX^7tX{FzMT;>)P zIPbV?uX&&MNX4@~f7Z}5e!jX4_|6Ix zZJEba7b_EM8+)jo52~Kt&x;P*M$9cOKi(|<-Du20zVTp3;kXhzj+V}V>G5bE$3)+A zhzWhq@N}bB(5%{5Ge5sZt*o5)qP@J8Q#E98P&x>;qk>HP4Ip&&=!$1X4VRZ;k40C~ z0paG;>%gyyv09cT7C!9(sMwS~t~oHZC&D`fK0 ze!xHXjP{hxhANZ+SnDV@9-6ynmq&KN@Wia2f=WYC-<`;h;7lGlo>t=SL@ZveoTC=LX2%BSz+jZ%(dPkh= z`JKZ_A|cG>|MLXy%W3Od10ZbGpyZ1=}<(r>{%b5oc7hp~INhA4ZL`0z%v2qjkP z>1SB}8?4UEh~n8$%tnQBgx74|Ny!-*6x(0}D7QCU<|l+rwYf|2FAC2_+kXGh@qK6; zyj}keBT6MF75g?~{y#Q6`C7!jbk?D=&vhsN%IdeXDW|M^D=9h^Y^Sn(Y?m;DX<2`95$ zjDLR9F{+OSAcGv+snw5|!4dH`h=Bp(<7Wo$4LvFX-d^pG`&wVh5*$#2gq#rnxOa#% zg87e+d%OJDeTg*dABBxZT7~{>(xp)ZYMtW z`^8{!?~K8~8H*KkF#apR^;;F{3N(-(()l5YvRN+iX0McS?U*Zj5SUMz6)N)a{uiRm zINky+c$6!we*2HCSx#qI_4{e)Cw;p{{|7HtTv*iKSU0%U;9}~|U&t?RuS_F+~KRa`IkJHE!Ab9-1&Zl6I@HX zr}w(pCepYSSSrQsV9x~M=yra)$VdMQh^&DBhpnrC|L7NikvC$fHNu%WET`hE+T47H zW*|i&v_*db2k#TE1ElerdFfX8bB48+sQ*iOfc*Z-6(`LVR-XT{jUEWjaS`%*P*lcw zoiH(-B0P<_(|4X;4)YZv06+gf5?@?dBqy%%Mps=n{jZLQmeT)`2g@O|aKs@W9nI(! z%-%bO)$3t_tT+T~2#}jeQ9nRdDZlroJUCDMLo;vrzfYdDz2WvQK+)@P+b0~J5wyK< zTiWm~8z@{p!5bvT+aw1m$|ck0ejjZsyyult*tAZy2Tby1^t6U|O>yGEsykNU(|RKJ z-Y7ub1?<{~UAEQNNUL%=jhAk(T{Q=36Wm|=MdlI`XIrIwXX^ds=`oZrk6hkFJ)SLW zTTyrK74}qR9W!dED~4a%Wc1S)#cvdE*NB3}Z}bC87*lzB36o4kf-0zfBJWCHMk@08 z>%W3Wm|TZkD_x$$UE5B54@Y~vA*>A03z_Cr#fH_>JPPh!e*c^>4)9Nv->I8WuZ->Q zN==|owNCsNSFtA^kL(M>f-f zKiw+WM>kaQDcI#Ck7%b*)5Kzp{(-|GyhRPvZ*f#b#V!(M^UnX@Ii>vHFE@VjLX-hz2fajsc)QWz)_l@X)#r(0v~B3ve7&f6_42?z?Uqv%gf)M- ziot3c8>d=XKmUr50Mh^$!%sV_080QC*D)ep}15ekpx|A3OyC+7jOF1;Kbz?g3SI9F;#= zubJl)>6@pGm)rTbv6~wfCUy@~3-Uf{Vf+DRLi zXZ_H|e0lPQuJ$F1hC35&5DZesm_0W&9v>YViEUqT!D$4OG?*0Eg_TE?GnZ0U-M}-c zV@>-_rW@HPKcN=32UctN`94cIwQw2MNG>s$A$1E++Lmglh!0#zZ~43&`x9P4SOjmd zhE0?G$4oJPMnq(+Mky=W7-qzqaG#l5_dk!}NuT z3L%9(E|cC1oCeqL^76@1 z-a}MuR!IJN#3124RvYDf01eMXx2k$LFrC!B%G6YXYh4aOgv-c>9ACtBfm{W75QicO zdu1N_4aj-u$m$9t8L7#ND9@Wc?;>ufYmx#b_KTkw@dVHL>5hS`OW*I76LncQ_ZC0N zP5vY|%XDK9F4vdQS)yvE&S!IxH)>gf$ybnz*EBe%I0XFY zU6(c&pWrX+Fx-vrKJMmTIUJeSh(>Frn%sJ zs2g34sL27XjI`O^-ZF__*=hAea z?!S8Jq@}_>l@C^ZM1<00Dkg!gXx8Bsy)9pakk6M{#1zSZ#&4ak!BTy|2LAHr@^1S(6c_qcm`h;60Tu`# z`#m`KR)`Ulfc{%Xf%ylWf! zT!f~0_FtEtV1;!4Z2(`TLr_r%(Q_&3(`QNBv#r2BqhmOAEapU z-d2NWk}gM7bjuI0TYGi$(n!`>zjti+Ng;(i$x{>t0GTi5H^zUksp^(8EojI`a}=X? zMJ*mY@4Sev`B6Pk_5ZHQIs5sJIoJZ3fkkq3U`H+y5G+k+kQ{r7!LIcV5f}I_{NPC7y!} zOBi$vLbw;7%!Wb==xW^w->o7$YQQ{;9uwz+0#JBswRTu`r{r zax1xW%HlT%rSMz37HGO_2SZ~2_X-&4preA{7)E@7-pHTP&RSQ?N!jsuvtD&wV5@4Q7ytr1+}N~G{};sgc!q26$As~9q??uSieu9(3vCg7 z27+u6)iXMsWnt*)=efu#?+(T@oVOhsl2}#0@F?!wr0a%~?&^Brv9GJZgb&fOLGi?Y zpu%bxvE*teWi#SG;5dYcvq{Ttc2=w(X`8N1A-z3iq(xyh%vxXNG_Bl8<>o(iL}?8H zM^evAS5=iA%!jvynY`DksP`Xwa>T?t|M*k(dnTQu|SAD*R$dnaTYAv0X7@-K< zJq^Tlzy@+JnD>DbV- zf=m!K#wH9=a2p2~&0quyzpd<5GMWG!0u}Z4Fe|=Ni21=4Kfc^e0+IywHrc7)9%Op{ z_nHKnvMy5-1I;3EQ3eyYrrT$>S5K)t{urn1f5!#6yM3)Mw0VNF15u|WJc6~Ayx+1K zyw~7xh;A;2S}?10J%}55zTTvFE;5Wb_!fWFtL~Bqys4=?i6j*1jN?A`dq?Ncv#50a zD6}G$8(2)k0NhtEwIaWkHlV>7k~!d{Te*#z<7K}r5{opzNP4|) zzc0vcqU3^mdj2X^)vwHkyvZ8@+-_lKY=9-22jk%fa_rhR3a(x|W`${Q zB$(1yB=ui#Va?3I*6dk^HgaY-D>F#BDecxP0Q{udhJ^toHQ;&K$;n`{oi?||bv0;k zYot5%u~-#|{8u6N7NO4+wmEyR=wr7W2b)p)Pu2oPFOdBauWC49^?!vh=w2&7JqKL+ z?>T%ZQ|kAFG_69>G2nGajl%rkuuCi z66Wm2OqPten2$%Jv+8+)c6`B6o9hZT^P*p2sXjw4s$2U{c6LZV21pZ+l=&$hv*9s@zX zt5xf-tfmxhKuDO#n8J3kuMtV>=NaNskQ+NN`&J_stgEi_Djnp?qDv-;%ubtOF*%Ce z_T}$+XgVh`DVx=9NbB|)vLWEoD?w#-x__f)zE|kxde0-5kB6cIv$5mF$4{<_goC=b z_-wcaam?3-;Kg5XTf;6%zs!piC4%|9qyyOapI824VtK(a#cp!2Bg260 zx4>6>C6{NKB9nHWeNG><#*y4#R93OZ(oD)gVC&(|-96f#=nvLbP9hWp0w^ zZI)+B%&x>F3rub9w?imp*Fr}ypyQm3_mD+M%}l>CQX`0a*T!WyRwZf1@quUbeRsLP zZ>Jd@NL2e*{uV7PGED!!-Wc5b0HoS1uFHdsR`yvfKQFZ;&V-tMyVHmVq}OO758x6( ze!5!&K>MX`zZv3&-_-5s%4%aQl$Qa9Ct2&M##Sr!4a9Hu>&Jjg{eq{^=ckK}W^e8i z<++jEkHnRoy#*#eOdN{gThHf+wEVB%PO$mWIXs5v_o|8fWBT<%Th^98u0)%hr;d0T zeSG;UjS^p6K##q;ooU&BqZXasG99owdw^u9B-~>uGqV$-5^3ee@iI`oAR^@bA;VN1 z@dLd!j0S~3?OiA1OH6{^XkWuo;?@9Nde%jRF|UL7ZpUf*o#x@`N=Ml!FH5igs)V4! zueRc1P-@tNc*kk@&An`HX+#XjTvn2(vb>?+qg3Yh{hw@EbZ5LE!@Nc1VH*3XR8nW; z3WSrqFZQQ+GYt<=#que>2La(UUwsvUdG3jS^Im4& zH@?U%Ou4Q$vOx0R2uGCw9AG8T>W6tq6M%yF!hZ~F+7A6ZdXat|(fhgM1Obc!AVf~6 z{)P49Sm}zU3*y%NWJ16#X%@$g>f3^Ql?|k_io|9 zJ}V24DQ;FJ)odme-K_67>k?rQqP<%rnQ{Uh>(TFA1#bp z#aivMO%t6*N1d2DXcj?}2rYSH3~<~rJjQmbxF4KK-1aWZ*j6@i8Q8w2a`$h8=v?B~ zF*G`r##UDbSfaSo(Ky^FMIdNI`i+cxAE@s8RJv@G$wUyaBi zM19_1$a%ex2$j|zn}auZlz70N4Zo87U+aoynVML3oK`<)+4mik+X^OunSSG+Rob*V zHzN775jXdB<;jG2)PMvkLwx=GJC4o(TxM5<+9uJy+DC=Sr%tFkY&$rD}ll{!mI%+oB=c#N;kb}?glidnrM22H6>xx5{Cz`!*8;jUijj)$y<61xM5_c}__rp7~bMCuF&Jfx&Oa+MV!tx=l zj~k{%-Y3!1nW&`#s(er_^R71SzYd>YHJxYmHh@&uzm(e=l-Tuoh1~-K>*5+SB;><` z!&qm@BGz@~PHx|M&XMgFBnx4%T0bMSCPGVHr0S`HR)Y4REo@Rmve0iD|F?QAYuv!Y zui2t-YsM~*{VFSoyzSVy6K*bu#PkRIE8gw@(|@(HAi&;1zg%d~R;V;l#5G1&_1@^? zAZ#L=Ve(cfnH<=Drm$d*98yCvP)qo27#Op$Os$%dI8y3fgUmIkb%fUA%I0v%Q`0-C z^;awkh{|)6I4cLlWSbigipV%a-0C9(_7I1&RMq?Z+f+C&x+0Fl+)=2aa=4BzOndr+ z;~@F;OxF%Iqtw_;H>N?WmkOa9Z0T>kx(PM-azC##*TIp~+{44rywUY2d&I(5cbzRF zX@bbIJx+66`)`m+;Ui7zvIw)YtjK+9+L2=tjK~Ua>;w4^k6*lf-p@cXY2eZAG%|P! zFt$z$SJ^z?d6d|OMzb3CFUVu+wlCr+xUBaU-eD+v!@4w0C7!+akc0^5-G@4c=ZkFh zf5ulcgWw_k!+qmgDtVx1U17gHxV>pOR6=b4!KOZ54#SeB-}M;6e0dBZp%)+7-b5bO zM97eAnbN&|{l>r2r_dSgKGI8%h2l<=x>NJ?AL)4A1R_f7Cn0Sn(F@fm&_D8M^SFU( zT@+9%ZR=*ib&|z9Z9*cx(LepiHJlbLa#$XcUNNq$giU0V>c7^3GgXLg_r%7_n3z24 zck_#SAyqv1ifaiBJ;965ye)=k?dOI5+DTKtDQ_5*LmJ@I8;1rEujhkzip9}hyUXeS zc&~k$FvEKoLdKtuJK!cBAap<-pp!U8gh;^smd*P|PHQ#B6*A90g)^2CrmX33Gj25W z(bqm|ed~)kv#>wk4ERV@q~V2e?%;X)))`FobqIf%q7a$*B1?bStiM3ALDO26+YM=- zhJ`%G72DTS>NWK?MQ5L8Pp!j^N}S7fX>DRNeZhAlkJL^-lRrbT;Cq!S&3_0Yp0|HNP7}Ok9{)F9wEoIrFLGrSnDR^xui-ZzHd%0iNNWww= zO@%qJtIezjd}@0PK0YIM0%hNnkz#%K3mQEB>#({w0J}!%FV(Dy@m%~Dw!~M3jkpX( zR>PbxwH?W;O|hq!T{4jH>u3FS8V8H0JHcm_`0ut}N*HU6$U8WaX zdRA&qy8IYk%OtUd2T%#{yMa$8ap z`ZrL%Pck+ymuht8)Kt10z5VCqqe^fVX2hdN)J0@V(}F4-abzh3&jdPSTOOCTf1@u2 z(kLl>YzmLCDj?b-+~Rigr)x-Alh;VCS#57S zL!RIkGykc>;3zZNVQ^8skKD3#>dW2lXT81HOqr1uM#3T-IH^dJGj&_wP+PV*|^(`7be?ot`95?8vfH6>y+>i(Ed?e?m^O z{N!IMwu$K{mtaXCF7nu0gLow`2kqQ1|HEh`LJYA;DwFtfX1#l^oGnl68#?`X6^0Rm zdpU=4LihgV2&b2YjzI^@CCEh;@h_@HE9D0^&&OoXb1JnH*h0~e40mA;wQ_BDb3uk> zi&Sr;aEC;%Uzia3n80)}C=(Rf7;}K<8G59UNt+WUHDuZX zUl=cmW!(;?@ILB){mgl*Ar3@0cW#MeTdqTPK>z3W`9x+Goy4}6O(8&89gmTkn&gY3 zSk#$ur9FR3qwM1-R%H~s9+FcN){mF3;n1tEM9^%ZBV_+elxsM9g3b2tz`7$bd%O2b zKy3@p6KBVj=r!B~f6G+=8-;WwOL}s({o$hrYgMK+n*qm9%8=)OM!`C}q6-64*n8ju$v9v z2~9kHMw&tqx7ij7uSCA=MSb!1obLk2dRvG8$=vCwn0;t?_8qKv55|wNGg&avsd*=S zP53dQB*xD_ow6yC#%`(So4>IryNAr>cXhd2Le3RJ{Nu_jC1W|uUs6oE zn7kmZD-VCn=9GW8*JHs+r$Cq)NTA39;e?H44M$g*5;?6Od__*`Aup(ztSTqDSRb|i zPs!uK%d6_0Dsp$?RQzS7U~7}t!vweO1}VKi$GdXueM$H&MKGC!5cY`z5PnxMECU#2 zX%#hanJ~|=*xJ>St_?{shi{Z~3ZngW&{Fr}~W&RFJ{df)nW@Ryx-lM3tu5 z=KR+i&Uy_O?*&KyQ2GJ=>(>42Tl;%AO##xs39|#usRmigLSf=I(Lh3aD*6NxeH)_x zp1>e8U}(Enwge(wlRfoG@y06h^E1i7qYq_jEd>7q8Knd#V{^1MX73P&Gmr0}hUJxA z7S+QiX2nI)?;*ta=DmH5MP$^r?G2*2#wtQ>4{2z=oQDIRSCqF%Z9tC4? z*HAmrd30AMG6^Jsm-()f(q|sMU#>_1NOel)(Iqwt9h%1IU@!ZaZ#U~7x2TjSW!M#pU%6Q z8MeQp{9R~bf$(U!Sb1=3n3~qbraZ?tl~~9q1vqY})?KA3#v04+dsmZ=BsT@Ftvt|t z9Ff}98Uj$&B&h)1>$7UiGmeP|Q$|ioXSiU5R&y-PjOUE7OaA*!`GVS-lupjjlAk0w zSAVWP=9&sf98i+R(^LZs_hUFykR;7d%ow^rAz;L|;GJG+jlAJYsrs~c(S-HJi0EmV zD}OksQq_x~fwGO`QlRPcT#wahq0lnr7!-M8 zNh}9YKU23)S)<%;#`9Ea6Wb5I89o-GlPq4HbdZ?@d`wSVG9Ws$Pznc*?Le`rnhl0q z^^qoC?WDn1{h%_EZBB&d`%2xn^*afKjE3(3XcV$8(3zIaW3OAziGf0Et&9%CkiKnjc#=O+(lv$5AK_6q+9 zzOB)Nuvpt)B+sWsF;NZU{5%c5kn-%3dk$uw>>3|B0HVxjddZZ@6szN#()tsSC*b#05S1eK38}>u*Yrw)xFxPBW6+EtQ^| z1e*A|C-K-be5^i^9c~zJ0HTn`HZ$QJfx8HB8o!&t3&WX{Y{qS0`1uK!3CJJm`&-qYTij>r|KEBG^!HKfzR7%t+DFmgv(F=q3c42 zcKHWP)IGP)YF28F2Xj5M9N&pU!P5XI0nZ-=)2nwLdvkQIi&Oq;Dw{jBb>lnl1}Fb| z!UN2f-#@Qa*BN}v@q>LQG0x)zRT=&cIE;OS4dk_tmTC?XK%Wp)vq&5tGE|AjbV-)*0-_|G_l>wC93_IZ``#IN9S2l zL!(Z7J5;|I2p`KHM+=4sI{aYwfD#Q0_9-i;qHxf6BdZrv4#Y!gLHKSJ!2_Uck=?8x z3rzOgl5_nw>CI~fjPD(c|)rAA zS;qPwr&S5pvTm9>nVN$bV?}ERmn$q(L>s)ihCGd`x5~UDNe7Vn>#NW3L$jtD8c=d` zo8_H?R&a@*{ZABi8W9Voghw%kp?Dmnk1C%o(6me9mg?i)u)XQpHqV|nwG;ci3q8iC zI&st7&ACq%wEW}#wd3gc;UNEp2X0ykKqU;zOK?9cK8+%dl6i-kVN}#rWj?UpD^3pz z>jsXVU;94b4d5odMnQV?VD9npf%`}BzD4)CyUWc5cUsPZk=8EUvpVjjc z3UgQi7n?R?AjPt&yov6wOkzzP?TnlL{neJ@JbnFg`Y(-iR-PI=A5f_0eA*n|-p)Ly zI4faDp~B<1WVwG^33;ZrjLam+D8KI0#hHFRVEFIL@L5}{`GvRa20>hQ*qKH{m(>O1 z8{$lBxNg8f6v*G4s>|?%&2Db<-r=R*Bk@9rb10Y!9};{8jO>SYlNtyR?4O(cSteZ= zN`I?DuRlQS+r;vdF#GD%#1#;Ok@>0Ser1gNU9VVHwBS)!n|M09yJ4FC3il)M1TwSyVqk7F-1d6qt;KTDVxdlNABF>5k*IG!m1WA|LVLY`u{3?=aPY(2 z)RYVukW0^0*_m*rFFRvO)I3E`D$ z@y8m`KNU|xUz%LrXP)ogQqH?F&@pw=FQR$ZOnJimf{g1#fL!Ub7`iCs_Vc!>` zNxu}w!RXc97N@q`4@2bGsIjwcsUi&XRgs7hH<$RMbEvZCh6MQiFEf~> zL_oBuk6_M3KMwH;^at?!bsGl;RS1dXC?h}lBjXaO7uC>*i(4y1nd)(UewuqN89Pi| zhhWMmENA+e@8AeD)2(t_GB$c(oxIh@g&W6Y`O(Cl(BclWLfb6SRan3XZx?uBB}>W{ zKdy&#|GNa9$y09oe6{Yi5Nqes^7n@EQe=b9L=tvrK4;afl6Ftjl8i zVI7r@9f4NuTl4@?jKcqW_{uR4-Rvmyg=pxOzJ_(gs28gg_l$3;z4%AWwRxg;`DduAj zx5U{>6bU!x&L%wqyZU)4+2DdIrq{+{IEsL}Zei<{r5+M=pJ^K)LSM6x zO_@;FeS<}Bg#FXr`uW@AR)GCo1-spT7!r!O=nGSEQgWxW};8d6e76 zLpb@!C;q*YU&2QNMbAAHs{n^iCMfvpX{Nir8rFXN);p0!<1J^v4z0y*@=&7kW`xAv z;Pq7!cV7MpJkVFWL$m*Fjsn8jkPxokj#(kXx(_w&e3is&TaJATNcoM@u6*>&!M;}g zmHH5p2T3(TIE5hyqJNa9?qiB5wE52iXZ^Nox#esf{gh&)@jkB(4t_(1{|6Q3*y#SV zx#|#28t}mmOD=7P-PgFamUpCS;D;@p2kE9B-L;z|_uBXoTd>#l19n&aiRq_rI~2vM zeWNG{e1*^Iumy)?MBMa@XwP#torHa|yZ|;Q)QSudyK9uYB z^Ekio$Rry>3!@WaQ;iDw#@~jL7PO#hf4%Bc&fvUCphjA*gF7xi4%PmuqQ43ElfK!{ z)VII=h$3K~R3!XbrSicvt_pWVGd26B4ISeF@VMC@=}@&|5ngrNpXhcGjIpll2}-8G z;daT%KRNV_TJiHLUuXAsil{)D1z52cc%-kwe`k}D{?f_I(o611#<$lccW5d4i%H;* zChajCvXD#dhS!gV+eVVS*?T!x!O8Td}qGex$IA;$P0s3FKU>*a;uSxz*;w{yq+Ku`m zXOMSfu}6}ub;@&qws3eN9oO~_D9P>p|8Vka%I9b$v#b&Eae#;X)#QL1 z?Nx}G1{p$=T(Y>RcL3d+t{S7$a?ClNG7^+UMkROiD z2lna+>E2Jf7^8MXPbYUswt^#621mUG)^C-LdQI161z7W8tOgew4BfyAGTTa+`Y8Sq zu2RUlFo|j)Y!JtXa6sL1tQ%K6`qdcbM)d+k-n1u5LP{t3S)Z2I?lvXs+aB(IHEXLB z4q^QOp_KGrQV(Pn<#V$J`RyF(%fac9s;$qiVXEkW`{ zQyjxJtCP`Pv7MB$ursXj*=gD1ICFdT)wu8jgMoQFIb)Hl1uK_*$LhWrFjqaS=RKx+B?$Y8u4mykj* z$3MjkA#|`@T#Zc3(og+RZZup+ELa$`;0U~#GqOEJB=RPVUzOWkZolNuupSyX>SQ+zjg)c!DUYS;wDVd#(DW(KmBvoBDvt)HA^vnBTD2(i<+HIGO;g!i;=qm;BIe`ydk%v&CjTPiW&Lk(MStkKBPu-9 ze7@+$TcUA*A9&EguVf|oGvD&@0~`r{G^~7D#3$unxPFiSzV)$l z)cW|NeV%PWddrJgNtPWSd5NfE5HS`v^>7}5BmKHy8yb+J?pkR}qGf{qOEnTjRj0GR zA8csyFBvr<)CuFm=n`RO9FOG>qs-tBdYh&C-K|N?$V|ed58svdk)trLP-I5HKnj;) zMAh(p+^)wx)%bcUdQ?LbvA{QXxRvG*Nt+@HY+0!;_bn_2rW&D<>A#jGc>DraziroZ$2$9=@8I`w6L zaay?Y?!m7U-Y<3q`kPI8UZ(p z3y~6CLY%5{{ydY&ffEjtbl#Pm_iVjp9;aW`r5oyeMod0Po5p(Py@1VPioP=>jgXjc z!4VYB04@BLOqR0k=x%^b6!H47BGHeU;DKipjggJ>J1fftLcZJ!z}(Fl=j2!%d08O) z>(;Rv@Z%y++C^;Yrk>@y1?f&gBh7f+>3pR(b)9I*mPh9;zi%rJ<0!!TgQ~xZ_`V0< zc?Nyq**2?wS0CIE_IfGyigx!xAwm#@zh~;QRo{CCD5jd;UQYOgTXclTvfAxX#-B@S zd~S?aOc`*Fts0HiO-%!$Y;iiuaX-)Su!C4T(Q=S@uAKX&@Lc3@&Kb$&Bi(z^z6woF z4U7V1NgdjGEJ;c7s_$`tK`jpNY6Q7)z+d*^Cx8D4b8)X%{udgobq?`J{H73s1YH-4AXgdBvo+MnyMcQ3XfAVc;*aT$3mxkFm# z!TbMq;sM86I8gXiX$j^>}(fFV>G-K?U2J zNilO>BRQ;v_gt`nhOP(59Yvk}0ceC!)4R|Aa(=xc4OhZ6;nCxnH5`r*qp;>FTW~}X zLZa#FtR0;8*mB+}X#5!$` zjcLx0^$w<3!x8knu!@>00~*Xy^}W?S4&X5G|ncV`CfF0MA<0dRq7`Smw>@>&kv| zhZ}_~0%xw*qv^bhygl#lK*0G*>k^Fry0U75Tls4s0<)uqaogRd!b9a5G*s?X|E84YWogc-(9J*-iOpe=vIbUnJRU?n-7LoE!dGRL#y){`7Sx!_XWy?m5&4~1Pi03R56t&2Yz~X7&|02hplq&-_G4{8(=C7}92wG23 z$&Ol!*LXJ*f91>MXI#WD;q5)-lt&dP6QGfA<_NVjkT8 z3RduLD2#YNpEwf2uv@&(s$sw=!z(f-?JFqd*Y}hIcccMa!ZM?N*cs6%jywe4D;zK$ z+PQyASzo>-nmM=Yu(7mB5Y0od${ym+fbxb0xZf<+4mrV`T-{l6$0S~)986yi(0h(w zVcbGtt!MYnbnvW-$lME!M~^CdPJNsCGmpPAvhA2x(1;6#4B+`Q%6%TanTtn2co4kH zvuXUxzhX4uD>#w==G!5C!9i9E2pb2DJo=NTuLX9oSnoaWN7(9Df#LTy5wpO@@H*yH z3`aO{$w9^leMYAzkk2!8bF{*Cnp=B>ozEZxI5z|+?P{Dq2cCjz@9@6W^#e?4t3-z5K_`N1fs1@?g@@cdncX4Ydo5fR-O%0xF5=~O9GFP70TM^Ex_SUW$TYa z*XKt5+Phn|PaxO?U1eVASD3jmeOzj?NHZsEsIgy)*~80dEYtLc<*z`crLg{yOq2Tt zUQKXX!A=gI2`Su?6Y-7Qd&lemzu};?bcY>G0^v1^{S0I$6&2OX_sHtN3%!5!`tm4C z)hFc1t8-M>H5Tf;QSA{-6LLqqhI^uRgK}fEZ{FhNSs4>*GdxCxbOlG3+jVb#Y(K8! zTq(5OaOID!^3{K~(w@X+;@3U9a5AqpHA_Ic#=HpG63dC@o@hV^yU~*x zwNl#@XGP6}6B+_|(*1T8uk`xZ)^YGR7(W*;!)7>J2d|)4zP=Hc^MB+Ny1k{AzN$Zb z$ig2I)c@hd5Py3IojTZLL|^B3{lk0dXn8}squfkg{?{!nWff|r zw6%8z_13e`e@he&dnuHo=#^LF&1tm*gNa*gP{%&|_vCvLn2MWL^y6wEvr(sb(cS^z zpl2v!*eI2M7ufL*s1sjfFW0rRT_*o8HH41bT|E084;oWmh=}l4@!A8p$#KR;J}=>& ze)hdNJWJ#=y}_ql;d=C`-4Kj1vY^F3M*FYR`1r&Fly|mMG5sbr8cAlJ=bf=#?2Cvg zq!rkcnX~2~JF%;1$iwLmfO6orl-V@(yKZgD2JXp>3XM7T3fv%UF1vCHVR8RtiJ!i3 z(bRy5@vK?<0x*(zmu{?H#Rn-i3yDgi?Fwr>t}vxNtA>MbD-o=}&9N}khepjU@c*!x z8RRf9;QMmrVh^j*sNi2eq|E6N>84$D=L?kVqkHg2M6g7>KvsB+mmZ!z`AaH(cs-^* z4vV%{chkEQ`84TrAyhJJ0;bcq<;Wyg|9p${f5~dAT~0r75G4KUI|F|2`%U#q zesC+|_YF<>xl;jQ#5mi(pknOfstuS&D21?%D77 zNJ^_vu2s#A52C-}!f|v_tiKQYV&^Fc!)t}DT}?0oDqjYyhF+&IaQpB?rs_yu;T4w- z1R-LxrO;}JQJe^PrnbZ2R8}<}-|y$b6=TAe+YFykEtUOAjGVO8F|k&6jaMoOoJ#^&ns`uyA`g}4p1`3VytVkPm~+8Xc^52xC5*P+w{jt zvnDxBNt!`Kr2h8#pK>qlR>-w-7*}4CiIpL$iNm^Dc5BzqNUK)=Apj+3WhPW%^xT-VgmlzIpo^WsIgYM z2qmK?WYD+p`Uj}dTWh-<2V{GOe|-ufVEej;#*r)b<`am#;OgMV8t>-db3VEhKB@0N zC6HXdai$sfL|&PmjF}xIQ&r@t&Hs?xZ5LV?yDY?sYg9D*Tbc?IG}bsJQSFn?5=8QY zPF+NiAH4Q!)aw^ve;snisW(VJcX3t@wTU@m^MV=(#(C>g!U2DCFzPrh-=T7xwpov>BFMiK1r-UH&rD?wv?B z8?lv3n%NUmGd)zkV8L=D6MB~b|Id5B8Jdg+) z>BdIEdq?O!rF*Y#$<})2-`0dhe?gB$Q*cdrJ6}J9@KfBf^VgeLxs%Gxjj%a0ef7sJ ziAV5NHPlp*n<7P#CC1;LpK~_*38>vrE>=f6eWza1vz{0il`0p0>k54(UbKgEXG=PP zyHbE=o`&y7vXS_NyZ4KPg)E$WH(DG+yQ^3m>F?@#Od{uhveWA{3~g#<*jX=y**L_O z9IcA$2$-iiGh2@o0R=U)mt%s#tbycvWyv3X0#`c+R=21Tn5IVu8 zQ+L&d-lAw@!UZ!1{&s7JM41*_VO(09KHAzzlXi9*%jO_MPbVD*4o_65)5MFC2Ob`P z?DuS|*3bN`c8+Yu#!5n>FTm$W`2F>Zw?{sZW?%kDZu(biii+?}t_ZZ;&rG6QG3SE=Tt@qwG$X5j2`l8c|{x5=5@?ReN(ss|l-8@|r5&C>o8 z0Wh9Z;O$EbEE=(WG4Oec{q*jcA!O=p?+%D8*K1RUv22I)#&;@yeT>^=nt}OgtA@f< zvw}3a|Nmj^Eu-S-nziBJBsc`O;K2#*g9J|?1QOhX6Wndk;1E2xLvVMQ;O_43?lSg~ z`+m-O&in6MtA5N{UAs%Js@gTZdRISCH}U8p;{AB@-TXKvwt@3ad6*fTw-kS2tmr45 zQ|@y#<(^{1_dQYX+#hw`Q49cUMVW}-KT;v_TQ*DeWbXIxXu&(k#6ak3{92sQ(T8Y;`B-OVc(sLIV_u!JH7LoEjGQvWmit&|$ z*c7yYF-@V)ABz*{CCRp#=FPSrUaQ!gK^?VeX`%*f=O&a;L*DCcb{)KgY6M%z2|44` zwNgX0Hv!_ouYPAl!nwGs_=v+w?6m(2?nzP_ zy-{#?3tHn2pIo-?fa$9pOi{5*K3AS=|Jrtp2wf^;H?0m#!ia#mYsnm~rw)WES5bV? z#2qAyWU;4cU%=V*sxt(HzELZ@xN~9x*K60=r_YP&Pi8Xn$Sc(J$s*qLrf;UcYjtNB z?H8P|KH>j3KTaYqyZ3NjTN(TdM8)bL99vm$U)iluo2)ak&vg@|HXa23pxfI11|hb|CmJG@0*cw~ za)=_gE|PD85IyDJ`z;MVuonytj-SYlyN(|)D0YJ3+dJ#DAAmpD!ldmT(nPZnjQ6*m z!{8KiBlB+K~IYl>F#=K^^qI2VLg}_fw zanEm#CxWEk!3*Dh()>xKwcDyLuCN2&M4`3q-NL2;e0!s77g|SIlF9C z4UBTVS<&>IvP$$sSiU9f_|FE1fYGn+&oWT{z%U#k?J%yVrBH?6`hEbKa0q6Uc z@JCRJif?W0M{jY{#1FA2e`u~gCTJkq$P2=XzRyuYh58`u4DOCIRl$+p8Vj~w=v_u1 z8bIWI^XgIEYUK_9-(N9YP*DPc+Q^zae=BZra+)N`oYy?}DgA?XOPC^X>Y!sDBorQ7 zbuWJk?wHNzh`>6L_v>0%yph(#La3dhwq}TZo-D1peYGskx91T9EH;NXAK#}u=5QJf zF-#e)_=o79?uMKYw3!5661^HzntIQ41K@Bb@>;2Aqvly#P?i?cSnM_VdHpJTH#roN`WE?FM;TjjFM91GgB~fa-u|;NiVuCYl zFCGJRri1Pw(&bB>9|T+lbT^5vg)ki=Hp}TheX4-*oRuy&N`yyDJw161Vf!Onis%3% z>nH;%D7|4VvDZjmYv?f&vywA$YSMwIO#Q+ z6{=q{jF1fuj-j?`Hr)_HtL~HeJtN`v$?vV%!M6=G2F?03mV0GaWObmjQdy5tW3suT zAb8;;4F=J?bF5D4)fbc$*%JK@DQilfTZ<27T8~)P4VMNK(gJ;G8P+k$M+b9WFtnZw z6-a_kPu+lG{PpY9^f+Q7HF9A}X!W9F?2A2Ze}`r4;u*6m>M@Di1tLRsW{tPdL2262 z)U2_+MLDCM0}wf+wYbDK6e?4eAo0L?^{w!2b$$M08Ilr$#^va9MRD3$eW~x17yYR5 zjkyfz)DT4F$%pZ%?@BLWbYk;FQ$|$RQ+CbVh6FnxJ`Ni^AH%k{9;}*wSd~7m5zRa# zCh#P2AD&AQovD8HN5>(oCJ@Y7neLc9$taw&eutn~b>QER@26a?__x5|Ob34dLXZK) zyx}wY@TJ(rvjwLvjHW567w+=9z~CsP_Beh^?xgoMAgi6!Dm)c;H z7-2+%mt($LDf;4Yw(|N>?vmd&K18_S+v4c_9!_TF*+9TR_l@lOfgV$T`Dj8FLA8r% zclMB#xR9U*4jI|VebqQEY+KDZ&fX_`rsoZ9%M;j9V*B{|DgItlpFvM@KPJARbx_-2 z2l)J51GU%v1UNMs_6%qS6*~^EH)&8prD(#XkUL%>)9d?Rg}!28Qjx(`55T1gKzNyy zUGx)!Z${E|<3W1mbGnE;h~MH7&M!{m2ET!-Mjtb**dFgIog#4$RTa ztE|W$p8B^}_3XUvc5t-BjXKUl=AQO(_|wlhf3Q+}TWO<{qaE07X1{QS=J-#xc6Y}B zOVeEwD)mU#DSTR69S^o~Ox5rB66#={ZN&%Y%kaI8))e{UiPkb1fyb-MA9I^HA9sNT z@Lu-&5U_xbFsSE&y!MIg=p6eDRd<)C4=|?d$efrNeTb504?rxd3zJ7M~XZXtECQAd_GLb{PsR@YpT`{fS4r9@N+dzMv`~epD zH6?LF!rEFj$PY&OnLDmv6Y*CjmN4^`Ac-hiX;LWSG9a&L$30B>h`66pWAWSjmjJwK zk~%H6?3$`^fqhNw?keP9`W8-fnwecbf3&e|_#ZH*H& z>qYr6;&)Bs9&j00;{kIs%ZWJ5V<+lM*EngjQ4!X{J5~HVpDnGaw_;9{3xKy|(n1Cs z?g2wi%4+TwoqI7_Z0~mROdYv=%d!q#vfx5s21$jVngenpyXO~p_w*090WGf)6(OHz)gpK)pvWKZ)45Z^&CtZwLZU3n^FOFcE!MGNrjjLZSSF;TgC%>b=l8$Q^;HUmm3t{D9zc7TxKj zH(YYCgnIJp?PT@rmsVRAynu9O0ID8sijjy-sOMHpYHzMHj5ne)EmP-t#pHQK;((M9A@IYqpAH52$RX@y9Z5~P|?Rqdd`*nYQb^ebyCtpWRH%v_}mbN#@(^EJ4Ga4XalRArZ~0SsPLh!TjHPN!Xs5^Ch%j&HNX04k3mC( z?u87eq`klb#*ioie0vUF)%JR zdx!C6Rdb&GM}%li#ol=&+a{-57JR;p-fq3nC;W{n`MYG$xGd&?9r&p4>&z3gA8Yvd zEB9Y~4YD`S@6^=ezP1#}07TV686j_U3Thp^7EB0F?03x8@|+^f&+da2vTEzY7& zJYQa?wQF5F>a|?U1XuZqv)?Lr{}&Mu6R^A`_uHNtLi~DR!B!>|TTeeo$Sjmh-6a<# z*J1=!q3&;i0*svD@Q{e0LAN+^Jlt|bvTrI8-tSaur3Q*ibE_G7f zYW@#O=HEYzRBWToG~-`+PUnXpRpq_fs~Av;X+p+!ZTbr6>7+yZ3sCN;u@^q$ea@(H z7$;4uD;@T}ovt>UNFPL+@gbeO-x6uCCmSu(J&8Ow|X7EhZ|Z-v%kR^lz)hdsifu zyi3i9w}`iUudDeIhxWUQH%Q+k4WWXF=H2`5D0ef#NRJYeZ+TNLeNaXcQ5e^r%I zujY}xaC&!gU%o?}?{yu|z`6N~luPNy66wds-!Ze>@BU^ZZOAo!clt@){Bk9k657|v z>YN2eBm0#&GJY!pWdlG~SaO__$t_5)27}W``3On$J<-PNfcVcmpMK$njwSxeqAluO zZ}|2hp1DFjbgDxDqlf^5N|}bG$PwwMN^8uaLA}Y_ZUi{{Gx+a?FDA~0wQBPSdNyxY ze~prbC1ay!_U&hZcr$SIEdI#flKBrvyizsTL*kLH;!9x5_*F-89iw{DZafLE-?kiL zQdZT+^es3W*?CkwX!r9QeTOzBkYTSbtpAS$Rii zCPK6yvzWdcfyc(zZssnClVner!0aO>B)^fKbVaKf)(9Agt%D-X0<~yUzdFmL8u_2T z2;B^tg=3Q6dlWwmSIohGLe>uaxBIRAlG;L*91XUZ+&#n{Jv0r}uIQ4vBCABnfqcfq3i#rGS+&&$%=i8SgR2Ntc)C~V{>5$2~gn6HV_XoYM;6)Hw+~`12u*a3bXR4-+ zwYHj_bu(Jca&wR!J&1vG4fl~MMKt%b`Rtg*kcla&;){hFJvV6X+&24~!ezm53&*-6 z-myo#OqxwBH*qf&uoC|TB(G+6NqPWXcnV{I2 zaNPR-wladGB=eR!Jt4}ZrT4wkKqEy^?bdN`k7*Ng)@0?tP#H^tEk8m3(9ZgJfz%s~ zd#2NfZHQ7~U7eRHacM7ZVDhFN!fPESD7k}{@Tq4ELeM6r0dPb+_mm~KiJ~{Z2q~F_SUc+M5+{w$>eVmEvqEHGNFoJu$#niWqdHC^d-;K9P~m^dER6~ zsSK;3Nr~erThwICFSdzIU`0aZo>%4k*~mw8awALV)YX6Lgse%_+5%Hr_F}!$C|#vI zAJhlu*F#5N|Bw{~vT&j3%GFePpGEC;*XY5R{RyC1-AYuy!%bSXF{?> zYF-undqr}BBNu?gBZG}?nEt$h>dkdG_RJHHYr&E{`?exi$F+90V;n)O3*Ve4It0hy zgS@9Xv-8!h=LKd{Ak5@~>96Itn(lTAtlrR!+_?Z(Xt?4+DemWvY6g!qc>%@(bjVCa@ZgEL{hqN*UQJjB^7ZtNR(*B=VTtKp)= z(RgLhYW$&Fob!E#sFp<=>b?kE!Q=Eg6Y+aod}-1(wfYE8`QrS#h)TxU_c=lVkL#8# z>##irKi7s|d$OSDFFBxiHOo{1U2)?!4?cXj>6wa_0V_!&0MJdWY^#XTzE}^u;u}mT zRpDs4sh|H|$anei&ft6Mv~2mLnot(B&5F6&QJL32@1vI<<-P&Pi)Zu=_F3N)Y0}^#ghxC(89UcIJ;B{W< z{PBC1mG$|m=OHwXW1Pu1WPkcKyq?uqyY9RiG5%;4MEV6dck{XO?s&cM`=c>C{_P?s zl<~&~)|?BcY-I{?-WLzL{S@QEm@0?rfn-;(D)2Ed;gs^6d(`EPEH>~Q(tj-qdY4xn zmNlS~HUzXt!5pyaMS}Tyd4KKswRpfevgfwN;ey$BVxb3_l||ZZ*vJ= zE?&`h`!9oLoyXstc8l3ZCD|Ya05c@rR9@%?PzETNK$=yu@Y?U=fEs4fL9)(;< zs8wPzv-WP1!z^J(&xek}y~kUv^hZeUTR%R^#6-$0vsNI#1sx=GoOv|5o9L$zRn9xZ z1dhF}vw+mMSf{H=*Yo0FLO=!3-)&B4b-9ey_G-_iR|!HKPfYh@-aI9@&iA?i40|z5 zYZ+griD<69C%%za@R-;UvrEZG<8I;col62bPxTP zl1gGfWM?dKJ|_&z>dQY_=`3i@xGI7WC6E1FzuP({rB3l(Y#M`z%Rw&CPJp8gu!+k2 zLIRNV%~DL|bdB5N_)%-EOwl0^=Gns?<8<7mF^RyWt&VRPtijT7ei{7>plmK)x7)Rmz`s9Du8` z+uQ9*0HSkv8+o#vw|(WuWRp}?t`-H*xH9A-_R#vqzz`ftEshE6N_fPI;Ti40iRq80 z5}Jb3+9JCj=s)OreOC7Tz4OOw7-xOSVFuS)MsnJa=s1%oCy^&w6xNV*Dxe;e%~ubifi2 zo^*v2mjHh){LA={RGaDpU4y^|aqZ8ns~C3s^}SY1U%lbJg`;|D+*U6db9tlj0RIYi zpQ7u3aUem4Mod-)88G};&#~LAs1u8-%(LLedppE3 z#M-(sK3lnh zqsPr4#~fJjU!@PsulAklgpP%u!J!84XMxq~R(==5&#nGz|4sa3P<8KKiB$`+Z?gDw z?P6?V$1~dhdQlb)!;O4*C%+X{gLAEcFIxC>T8luMWSM+tIZ zd(1AZ7<&X%-`=jBE=d>70OIfdQAHecjoT5u95UDN`h0`7sBK_wzEKI5q_H@GT?70 zmzlb~3rQ58;W@9CsQ>Exz~w$XK$Cx!Wv-MLfh7W31Q|VGRiC$W&4TP$*93C_EmD!| zZ6(wCLJiQ`w!C(qBmg_Mnv7?@9qaB;ejWG=pLmq6KJ9G84WIeyn%o!pdCuKjx}){^ z+$Ia)KPYx@u+xQn8Um%!U~yC|KFkjFs1M?VWej(Hem?1K8Ey$CBL58OQ5n+6ScWCH z|099*Ngm-a)~$7+ogk*NZ;h!ZV_KPmGxkX4$x|$pI*yZwRQWfs3!0&{^5XQLIPoTF zGu^>ee6;m&o+Mh}EM!s4*a3xQOn0loaf-v?{1E=22g03Da^11-@bk6FHSuA1F4bP8 zd?`GqKrY_nNUWE{N||WYHp1YgoBik&Yf=p8zaN<~?h~^d%zs^bx>1FN#m_ ze%cHZE7bou@gI=}L}->bp}>SB)T;>NWWvFS{Y&4M($M`U8Li7&r=w`qQ4d8w0ee?M zkOKn7b39Nvz|QY=U=4yFkPc%T62t9B6uq(^4H5lH^u zpc8gP&w(&I{B-MVbN7+a;_)th=vF5=S}$j?esaci>%9&fO40C&Ue7*CHGUQnZ0iDU zS-+}Jqg&3GyNW@$67`**1*C6X;TQWVja1cGodY5m*&UUk<6-^2)YGtkGBBi5M-H1N zf%7V+Ex-^Ky}qJf9Wj@1WO!65hL=qL{FV7hdKho}{zJR^;v(HTuxAwSQ7+Ti4g1)GY7hfpxeHv44x*8mR5-rX0f1>W$VeQEPrp%Q}j6D;`{$02a zd&dSCy91`PG$465x}_566=l$2C??kd4^5*ppgn`&u!`YVUaHKHi&&e?DsXV(9bL;j z9W~smZlUxVq0Jh2ow!B*>m=BQk6&!}+VK3@d?ecSbKZINh6l@j#_52~8vmX)zS|Nj zI%)4ar*@bUi1vg}+D5O?iCx*|@~pF`8%+duT7>Oo9!(Lo06gF17ab_Q^5hk|Ww-JKEl7IJLoHcPf{uQvYVoU1BAhU#uECY-^)w&QUTqr>NGOTYaD5%{(O0vLKo$$lClU~ zBE81Ab<55*C1}wqH_> zwfs&Ti6v{icoO0=V@Y~LrA+bOs{J%f!rFdf{eg^b$tWZdTMsPsv+DwpM^q{#huygW z$X(|2uBF;NXH{(yX80vINnF#KFS&UH7NmT7fc1JF@s`t;?9^#K5=T(oLh{z9uoKV>1(oCX6`y@*kipsmC2iiGUJ# zKVessERD@OPHxD1Ty_{=>ndKaM)y$M?1&s;dpWXenJ3?}m%;vcZ-{VFWpfu|V|Bc6 z$qTfP7y`(5-J$Pqn{bXMC9F}O-|i0Uon7qBRW4N8)BW@rJT$S}fN1dI6v#~S!Rq^ySN{nYS6y#I@0^k%M z(v#Gg>wZqdkqM=*o6EUiLUCz*B7gcKM-LrSynGj~=`^eA9+gyUy3$=(_3J z29%+Rf7$Doh6EgDix)iV|lFPZ=udKr=4(8FMmrM_;KCk;y!N8NBc4!sIOlM+I zN8=LRkMg|#KsTwC8$#HoBi5lxu#MDPjIi?PkRnq<`|Y`LNTsVx`EQzYXARZW;UlF2 zyYSN0nc!hNU9hE|B)|8P(CLDYYsQ@lMd{~}v-9_NNVf2mEH>9sq{xDzkvZj1Pgf6c7ACRq&OLF0A9VXvrx0=V?$P8W_WilwBh1|$kGltANH#>n$KrvrO z)qSu!8iM%s78@U$8trc~x7db>Dq5zit-fj?cHqtopoN>ZP5i|qL2F1pySTRK=Y7_o zmni$U+x6LtX3k3o`~(kq;`q_pR*W@U%9oKbK`FMye@iX5XvzKv+9aJ;uB#(Bz4a-) zv%#W|8I!r7%L6yHZ1{J9PeIXb|3Xc8>?{SftnQJ;r`qOOs{T-R@yK=$`J zQf{?a5%zv9w+!vj@5vRleS}q;r-Q=cH$T3^vBc28sGAd(Wn*6d0*M-(Z()!LjQI zm|qagFp`k>0V|6qo(zt~ zb+_!$E@|(|Zn68cD?5VmsNSj}dNT+ax>PXSc%p9fWKT|@DK=xN`KQ&nA|PzOf2h66a9UiL7#@7_H}V&XeC+I7Jt&j z*6|JI2?Or`LrlxO(CCR`axqhrr>60UH}F5eNzMwy$OrMZKQ9;^~%(a#9)C8G?Kh51)v zDg5MI?8&rCk-|BQ&*5_?0(1@%$@*8x<>D<@K_H|^*0mRQiEKZG&asEb-7$+ZYHc!9 z&<9R|eo6bCff>#SO30fG2p;)Z_K=$TX+E5_#Q2>6HjDy4I7x&H_UCg#`RosQG)5-J zy1q?k)ECp*Xml213<*jOyd=3ckBA@>d**wkbi_f6u5*Za@WHyn0-6I`JNBz>bg-W@ zMske(%N!Z*hWxK7F2M-K?%bit!ttooSK$Kn+w(SyMDtgu(r3uR$#628bnFPZ;RH-7 z(qvbkF6;HBC;J!|+7>r|V)wrUBJ$Qj=8$GD3t`sA!nJlkI<T*Jva_mJ zE&aAXbf#M?lcLsKjY=%X{v9S4E)IaNO{vHem`;N5Rec zbJgae3K-6R-~8)5Xf?_v5E2bHa^27U`e{DyrLdmG>1{a??o4Bm(w^-}-`cTZjy7Rs z9~^6l>Ou+i&dFHXU4nTR{`{Jl%Jd)+&e9KlDG%f-K5b+anKz=ZX*l$aRNqHFNU8oI z&i?%-pt2;J@V7pH1gk~O=Fljtym8%W&tYujez{kd|DI9gS|`odb1XgO+YQODAqPf{Vpd-X(!muEL_W|t(T-*>zi z6=Ozvv)y$8sN4f$mQCnS-Ry{G;%>*mFm`)?4_Cw9)Zt&DAn*DPY6o1JjY}y=;C@3m z{o!N%3SQMMs=1MVy}%JWDlyr-!48~PyZrJBNi3qX6-QzatM`;vc%Vh=4X1bHH-q&q zIw_a+m_moFuVuSVoe||5iC;YfRPy|6Iyi!ae`EVM5D&sB)eu`dAyc@*(RIVu9u=nO*9L*9t0T%c6 z+a!=DaIFtz6rM^s!nF@Xh6j7+eZCu09$>3}UE<36%C5ta&VvLl2r;=zlkB%rV9|tG z(yuali7_OplP!s53J`eBf0v1Wq@f*QggmNR75U=qkWO0nJ*{e%ejkD^!>F$ zL^X3su8wgZchh33lr=nshSK&SQ6}5$QC=F{wu^3mOPS?|$nV?iKXoHOsSxOAfzV z9b?z7jB`M#AZy3VbA}dvV?uv-;d>MwlG>q5@jY|#IE())Voa%~C`E^?r<%SD&d?@8 zKtGB5Bp%_dDvJI~A-Izj)f4EGO>bxGd9+r`SFa?ka5oR*G#emKyX3tcNL6E~As`(v zH%bYRDBcbzZlS?64Ys75Bo-DDk6EIvm3SW4o%^Eq6|zMCSD*PwaP z-;&E(C(*#U+*kHnO5KSbHy4n*0BBp z5?A4CdCe(9BmsO#87^Mw7$RMxUI3Ru#Vt!7j(8O9uDiuID3;jM$EsaL)PRS6!;;Bs zq`QrpErTE=CCe}^_8>IlHl}+zU%Of{I4~5rB)yX?G`#(WWM6mlL3K7S&f#WpX}s~O z7(yPb2_zPbOwm`ExW%|VvXUJqepf~$HJLmrPZTgu9n&X%-82Ndq?OD@aSNI3_YGVM zrln5SNcGij@P0EEndeI!7CXuFT~9UybN?NwLN1P@Q5h!PO#0H^tX5wZjL(rYHGrU;Yb9g{Hn6iSbN zXk|(b%%#p2vUo~yEcIi?e+;ecGwy{8-0j_Ez<=6U6mUb|Q>tRLE4ijS5)1+Nq`k!$ zL13rH?>+ZNEIT!c6j$qh>kNmlg=JJ-3L?R54l?MMns((^$Nlou#yNo8ALLltz)6dW ztUE0U7cbtSTwk{ZG3MVyRO&xX8pMh5VQ!WwMplM!mb@62gLHMz9}0Qc)xKFh0Fc`r zUUWzk4!S;lhL5Q``o7rDIqQ`Y*YPclCP~D}LBWC;*Iw`&m}(d`FUh&to~pJe!pyCK zyY8KxApw<{N}3p4+=z+i5z7GsOF4l9X`RDUeUB%a@#U%ZgKbOvAl!?_dDfB|5CkpEJ(@u{wdrJ7T4kiQ;|(j_kT(96g`}q zdr)^=hh(dK-lqy{%eHr1{rV>y1r~8TblXRewRHaC@#9a}GSju=0O+DnOFoN|qz(f> z0H{P|t*`&NUDV`p6I^$YyC`?-e;iYP>)`71neUf6RF$QeKa=$BOc!tCrxbDG@Uh7Y z8r#@4q;wd%g_|2Pa$>7&gKtbQGS8T3mw(Lc%cyZ_z7B@>n_-A?=t*#uhhE}%-}F^r zAep)1bU(S#G2-Jo)Y$P*p^-F@q%Ts@vplI`I@Ah0n0`s}d~9Q!+FC0~?Sn6gU3&5x z++J#mtO`S{5A<^CFgv!u!R`o-$a%|?yfsab_L2|%mz;7o8Q_b+YA>fUz(||LtYAgg zJJ;nI@wKpzTb(myaqR39DfSIv69!z`bMD(iS#@Tb-~3r_{9Ni+%UYdNQLpx9&fmRS z!RNc<)K1{U>g0i`@T!d3Vw1To^H=-v&kgA9ZL#(|(K{3^(WW+{VLn~_eobj#=@#PV z{+HHmA=4Bskt0LN%-Z)PzDXrI>Q0|-ka*E#0Ai=h&g{t5CBD7D^vf*~c+=vh)zBHo zn{Gy0jG}4ZUC(XIsyY+aE3dq=yy(L8D}#&cm~e6lgW;9BOCD(eP6yY;uZz6MIFBp5 zy%>QmSF{Rv>f{Onnx=KPl|IHxVRY}HDQCF!lRdLBcuFvYjNBlzX2X6)7Hnj30Ef)?s+ zP!&ED?MA!YKJvMoMWG$v7k1~hcV64QoaR^#@I`9%EcyzZdKg|{3rI_@cIkp6RF(YD zz>CL`i?_f%e`!lfL$op{EMBv z47FVVnOR7L3q>jtxYouD@y_shZ1j;k#pf}&Xd3GJtL8g66&d4u(2!AlFutc=XJiKK zc(UJ5C-DEI!#26oA@^0|uUx(nh;xnzFTkfuQT_S=Y>gcbQ~z^&HZgBmxdY7jg8ncD zZ*e)rEwPvvL{5NkTy7GB6HD{nuHpmNlwur}}G;*Z^<-hL ziznaqH#<%Bh^zU~KCT-${k+MWU?~}}Z9)B^k+=gd&k!{pgEv+=bP8MX?G^a%-hB5R z_1(2{yX_;|X%ucM=1|9M)W=BzVhL7er99Ao@kX4pOKq*Ic3Gw4gGcnO@)N;Y%SBAl z8gi^AT)EiEix5S7+0-k>gHwOc&08=$ZbIDf`KYEsj5VTe$$70RV0(LbuEs#A3(ZZ3 z@a}Vy$gtFPCbp;St8+CHuJ?9IPt=_)D*RA%c#4~nY3{~{ix|pJn#ixBVv~trVxsnw z*^ed<-{$fSl&$S#>z67n4gLFxQani4vLVv+xrV(7aKu3@)*z^OyTvHA_63fSvL=H9 z3jaaKJ@PP4`8@HS1D`BV{6&%EykBtty6+#Hg)yjxEOm7DD+3vs!EMW$uR|0LviWdaL)k7@-jO_mz#51DRuwbI zdx;llE~J+YeXq+|V6-yC2T`Hr&pR(HH83A8_{R3lYbnywCq-6d$UeaOE=MNhdmLDn z{DAV;ei!WJTcqDswqJaU_qSSl;`}8Z0r&N)Qn+-b=qMEiqhWbaGtC!W4oY$2Yv^es z$dv$yq)d@ys_y<$ZRBbZo8a%@^zV}7F)TsGHJ5JSdFhWOh z{(dgKuklyfGP!Aw?U+P{>?8-jEzaqRM||RK-b+^M#)E5$3XCD(=Jq8I=lPo538(dJ zlzjMNy|yW4SQM%6*wj8!WK;N`N0lp7wg7R)rJ=Y!*YXDP!SCQ^4~0CtSgfp#kPx7< zu$tk-!}C~4JLkhc)66TwjBwnJ;=c+vlvBKmVcokB`quC#`N6@1%40jx73ul3;qOZr zvRfFK&hX~2X0Y|ED;eq)|7^@V3?7qn16-&BhdT&raBc5XwU*I~Hq*`Es&R%r046%gj|Z!gc$ z2?S~SxjXq#S>GO`wE7cg`#Js&#if1QRNENgiF%5QFR3pkPboS?q?kF|FbM>Dsr(Arh@ zbH!^>v^}n6N5_wYSufwVSv)ep^fbt~IX{*ljBQ$Gk!}#N3=StD>hI}n1w}o9w~S%e z!V*+GcDX0_lN;yY`4G8~-5)fRnLSZwpSeT(GkIDn0{g^q5+v%g7C>-l!c@5e;zA;eL)t2D=RKFzP=A=VgUtS_su*x2|PG_c)W~Hh*#TTb5v7@F$Yr7C?#7ebXy;+ z4^azmQuqK4bO%;L3N|s(nl$!N5uJBZC>9N=F41S#skq z3O2Hs)#3rFZ+(3IfpP09(R3vO!=#K8EwH2I8;Bz{Ssm5KZzjuMeO5*qeF;qSIoWhx zDZ-akkZhhrUIsc6dmmS(=ckVaiKnC7d;Ht3MCvFYs8@Yqx;1Fbms^BHkm8hoSm;aX%Wh4Cqr-O?`Ay)#W;%rZ-E;^rKy?3P zhS%C{SR|#Z-tSGBcV_0;J28t|e4!8_(0wY^8}CpLuo+@M9_jO$q2{sz%AS|4hjw)V zk9N~km=CSqcWH&@=b+k)w?v|}I^5FzN%CyY$eX>*ku(8ve$A`z1qSB~je|7A`$Tc? z;>8Jo8qrEZG*2o~wK|D>6a=I3xxrp?J`-4)?hQL~{hahp#=+wyQ(f_ z|M~{tar7rA>}@Y!|(Sh@ktX|(|FCMSfuepo$o zG+OJamW#(euSTxNZuG(nH3^A}GLBb|76$6%caUUN{Vi#VWMTRnob|@3z+*XfnC>2n zOIMKFyjcn;G_5cAvDf%u|Lzzc?(@_NfkG}$dzCsc0t_TQSKmiG8BF4HWplC_(iueK zojSf!(+C4eI$oIkf}x3y-+k~-;H(?SZhujq`oi*A&>MvKwM zX~80vw4_?W{-dv{*tF{winrf@J&6?c5?2aFf17wEA&fY|Q3qozW*2rQ*C|kr7vwT97jwbbRt3*~h~DR#{7U^|T+T4?_X! zQmO`#oc#xX(*RTP1|QP;E|uwm6Mk;feEvUwd#t#Fq~BU~R4C^s(wmijYYB!idi5T=wI8^-zp zcS063NP!B+WLP!v)Q0x}1z`QRXfht9Uo6#?FtB1DiNql;)&-BY2$n)%?42 z{8HsL{_efJ(M7HO#c&EKhD%0>qK6z28TnrvKq_;^SEtxN!(>ryJltX*634(pPi+=H zfJ+8Q6uF;dtKW%Xf@Bru<8kDj+%7?v+E^7-JzYeJ%Lf;pbr?qwxE zzS-}=N@1b%1OVZ?;YDavCD?4UrQ%2O<;$aFTx+d946$*X+9;_dH;vO_S|Yu*(%zvi-x zXmsX#$Zs$uWj7Q#Q$CwT|1-FOv2sKn-0$4$rId7`)zpb|o#UA&lcnSXcg-PGIUrHq zeRYRV{9NQn1qkVMfG4ACY)-rbJp-|R6RA$;ce?uku#=Q2r8wO@pS5?55eyN#_?-29 z?17b3CsG!kYURW6h*sHg(CZg2bs@CsJ&Oxb2_nPD&QxBbBGdK<; z1`&gIzbIy}>S5t^cRHSPyKr1L69Fm`B}VHR>2_T+h*CzPy^dGkSVHY{Ne}E}_5DZ? ztDU=OlG1SaRRTy`6F;4VnP~%x>s`)yszgk96805kw37IRVeggC7s3jyC^ZjE?9*XJ zBxUVS=|Dhp`4r?N|44M9vq(vbmC~-0Yk4(!e^0lh;JEL)d1wn1M5+$dv1Pc2f@F;f z1%(Xt&mD+$G7{YKEyq$=*YcLCwkoi2J(m{vLAZOo*QOWrpGg7IcPv%)6jH*O&b zc>=X>>8>De{;u&tvn!{{56g(sJL9HlDm zO;_JRBNq1kqownSvF4wpU-X-LaH*3>6FC0o#0`-`Fy$G@_aEXi5HR1eN4zhI@9D8C z-@CItt(c21`_sy1f&9)x)Jc6EGOzR|>_ z!e59-Vbe(Ak8LC|Zidogp^;1}6> zbbJ>Yw_j3WZ{#Oy*1Om$qmYAfPZ1;Lh2L~ccAJ=MyPBNdtt6(JdoP8Vr~Za19jn{9 zk?wo|FF*Zcu0O>t1;&Tv*=wAhEf?qHd?Ag#%O}r>db7j}v0BY=AW7D#g0yBHD2O4H zE(g3t+duPTy4uvnX1`8P@z^DRADonok>tdk-2q7{`t)umpRb^axa;~nv4XvRGew`N zhN#5XAmA_ozP6UhtdwpFffg9Z5b$k{b3L>bhbkWBmUjW5xx8|guj=+Y5|GF*5BR4= zn9Q_nNmuRk7y;8P(U%}`RGS>XC3h9xs&lY;tvjFs|T06xc zTy;NM1rDt^dC4!X2+wp%Nw-$H3}eY1vht3)%$GW3B;*?hc3494tl$D>Vl3Jj_+s z=c$wBH#I)MJ~T)R0e-g`(6JYNx`OeK5Z|8*S7E`-{UsOm&BS&CAaAF=N=S=ghVkP*cRR$K>IwqUXi1(s!H`0b&pUf<0pFq zkLQ(59vU|g`~XXUW_Rw6yZKPvFv!lk!cPh-EWAK|H*^e1nru2M`kW+S{(VMG_XGK} zCkz6&$x*qFIje3{M}4{-ef{?3xICc_wK!k+KTC zoMb2JTZIPNRBp5wC|~;!G_<7=3$u-hd^p|I&enWxq+9s79_RLkp9VN(Q;F#^nLN+6 z3^I0PqV%u5VBzVh!lZq5L!M-?X}XUP0;V`C$DGac%22J*rN2;GUoZ~mAHHwjg`Eyl z!`af|0Kc^}_MhAmC!-Ssm&H~bJF307#Y`DtsUh1|{pD9gS@9(`Du1AI8 z0v7AqwD=zu^gLsdmr~R-fwwLS7Yv>3E1wYmGBRZmU%6)PFWVKn$cfvFiDgfd=~loy z6b%L#Y=RlK^kD`5vNbC@A4G*bSBkCo8JoCu?~~=`Pju8hB>C+(conjv>AQ1uc61V( z+%?_~f@}j~y@gNjW~M7~6@V`jXlQJMZg{sVU?{$bsYe$J^? z-WY@vRCC&g&wV;bb`z{Ea5L2m8%Tv!h6JlQ^>M%tDZLl+WQp`CrBr@E#3hbAxBDsO zzjkvtZW%BMqave=j+Yk}4sFHz3dP^f4Ui)=J64;l@ zPtt-yNiBX#hRYW_c-Sz?NMpT-?9bc}Tg_FoS*QiUg_|6(~Pl`1VV5Kep6|+9Z`h&Q~a`9CL8vjHT6@MEy$*RsAz zusDD1?o5RjHA!=u!K7UA5{lHFrpY3sF+P0%E=kUL)^}H)Jd%8$d&%y~!#OYz+=hI>)% zlR9S$OXq0k=Fg3Ic~@rtMr?z6)}lpFt*y;bakuy@gO!9PZ_ESARuZMtVH^F0vE*0# z_4jX1XPv-DtNi#rZF4}n@*a6jx%IR6H~gbXH_)MuP9^ayA1 z;39qH&g=M~LKPLW(;AA*M?_8n6{+D}X#E#~p5@4AF*0kcg*Cg}$*^bWt}H!ctFMB< z;HtIxZ<}v7-Yg%-S0sAba@eTk!=BDZ$?%n0Q*FueTHiDd=@%Ff_Mlo6H5>WVE#4zr z))bD4oF4F2eX!x+l>@|fx{GATe*upV|DtqI z&W~n%Z%D@Xh$tl!uv@4x;ZBoAONwonQ@xR!`1_B{uftwJ9tiu9>C>dg1#Gv= zis}>H$`dWnr%>&Up3=v4&|>hf$1mfRYu+XuY<{w+a`sEr35o3EuL_=u(W4emr>Cjp z_ndRfr$6)w7wR-*Sm@8aKkZ2du4@mg!s&W<=bwZ!nevw|Qg*A`vIP*!=A{mRchs8q zqU*mZ*HSFdvYsf7hk$73ZVm0c@|W}tJvVHA+wfH(+H@N%g@xUgi}*0BSmaEmgZxSx zoRX`x?G0;6#k=@YraRN%Z+|{f^0!8{%D(_bFWyl2sn7HTC*Mt-fSX7LEd3QoCiriSm9Zomkz# z#ns0j6Z!5NwZ!U!QqWFecE-j}V;RVYX~#h^ux|ts^~KHP#rTpYLL(%+K2@hBi#Tcn zy?;m;pIN7J!6$r==8C#QpcnqeyFI0B>=pdYRExp#>DWQ&FA*)wn;Y|o)M!oX=R?&rz#eM1Ng5Fl=Si=DrZELx23WG3t;02F9R(z?BY!(nwKKu5sC%@bn z+^_09SNw$+yss^H!ZwpCU-A2yqy`~O>`iC+LHnrPmDi&GiCh3$c~qAji=~;#DOI^h zi74fGRFPjM82}cy?ID=^5t4MmJmCDE?()g0r`=zu6UA~xPw~uHWHD2&qsKme;ku7i z!2B?%5wyPnx?pTJI;yTothBsGX74rITSOxFB5x7uizeJfUwyCnBkIi0LEN`8Bpy;o z$2WYr{GjWmOKBUSf;+EVo=fDpZSA)0z@E^{ss{ueQ;7y&ZFy6u?0^GjI=Cz6k#fv4 z+c8S9Z)!~*P-lsjA{Yh{6de4G<33)>M**favCQYZ&aP=AQlAy_BRnM$)Y0v7lzNC?yFU&xlxkW@553%wx2lLo9FTQy%1HF zSAl$Kml^KeG+Ehn87Sk~L#tUs4z?$&5yZijME|p8W`PSS&-=7Z_1Kp(5@X7bzvmdU zd$xQgh2Tj#;62?(cOCca_bNO$lFie}`0L8#k@#G=Ys@Lu;pQkG;4gtbupn*YDYFva zvwF7Y1*>3z*hC&?7voY=`DLAty5V#Je1XucqGgz{f*^zmeF|WlG1FAu)#T>o&NmP}M3e+7AUn?onuytYMPK`3*hv~c1O65+1pGYp5<|z;|fD#H_ z)@QuE^;T`fFcGz$OEYe;N)#Td)8k!E#*uXmW%8fUCI zVjcN`KO|4{l%W$zCVOR5%^3R1sbc^Km%it-wFqj6&E&Hl%WKY7cOCV)qVx&&Is2Wl z#Y)mVYfw~jq=DZ_+QW&1^mc?AqPYor%g#v(Nn)?va!WG&J-!M9a>AmRw~fr>E%LclvC-+Rp0AgP?)?Yb_F@T>(({ zZL`(uq&`o>Osj`p(mpcFdnzyfD=>8-yw)0Mx)oIij;cll=5I ztk!*}b*HK+%gK1dBPbQ_vF)?7or9S3M`W6Kt|Uy64w4T*Ey)9nUzg~aw;v^g@jv=i z+UesK`WhA2;D9(R+!K13Dr2TjSQq7*O)E7quY+o7yh!(sI9{*NBm|O7rbyq*=Z4PMJY9SVS>#s#d5^JG9@6&^0-y=!TQ%~ zq|{6{c|`Y7-GB+Hf|P~7KH(`s*tg2K>0LMmdSnL;_MHYy&)2q{9PewYeVjWAkT`^W zX<(H}HqMOEE1Hj=Oeln(It6mBtRSmm_lA@E9O8usLWpckDj3 z)lig6;hHtJaxV=bfJ&jk`*oqmhjbl%DXLrXv-|g2T zhfth%;3CTsrk%|J3>-?&2)xyN=-Mt>q1xneZ!8CH=yVT@Bk@&kjl^`Bul@pRq8@?Q zBa((YK9j92fm0>OAhl+h)vd0BhmLyg-s7^V`JPPqKPH#;@9f)6;Q|+RTy4ttnv9q! z!C7zpt!8~UagRGUK*z1dHKcj?djl&bIbu3x&$^72)lNoWcAoTZf@nJN-W+O<45ybp-WV}rQ9ACa&sVmt5CEp_iwwp-HRwcoWWlpI1U`Nzmx zW67zAoHW!S(5;Y=K2GZ+G&ilSw>W${OnaP#s6VJP^xo|25c5h-P4)K|wK|+^so$gB+UBjsSTNb!S!rIZSv9JC+tc=wC|pH-lQ zN5eS5JLrWg%GIbJyKFCv^uuF(;6XP7UCURt0@saP_70~#Fz{i5hD8}SJf<96vxRJ8 z+m59XwuH&io<0WNb1^(34t3=vX6iWz~|dB<7q5O#bqUzM$3kv?!!Gt>3csW@UW zrsDJ7{^4-8BDZtrkTQO_N2LP!6SQLlquhbokw5@?uM!6Tl(bRo_$v@nZ;BXCA-1b& zMZdfXwFAB_2s)FFEJAoa(x|&GmmfvR40d`>0RHm5F3V@tR5H^8nET*$>az(nk1Csl zKbOYk{jYs?CpigAwecv~F*L?%0fT#HmNxO?fvnh5R{SJ69>AIbh0UWSu*?PDQ z3mo=R+@xX$2tVI|kCm-e?8v0wwGTN2%Ex~JoPRf_8{6I$Z5AE?COQ|5kV0crR6^0M z@?c}{CHmtV8NwPQ=gAm>rH@{x4 zOz6t@m0@=YGYDFuuQx2=*C5H=uR0!E@2fR#sfRO|%SBhL<~R*mPc~0E94?(T#IKbF zpnk__MxY~-n$oZs>U;f-N0f3)zcaqw+c;8f1l#}`$C2)=Ic3C+VSP>90hTl{kwnkV z1%>&g^|RzLXTB#wG;q?{;R^7L=|hfc$rd+Fd3eH?iLNI&+GRzi6k;W}{U-P&oF zP`)IPN|L8_amnrn!Ly-%ybjBqWRSYBO%Y%8H?GY=zEv)#jYos75&p$2U@7c$_Bt=e zmeyoUeIpGV;8IpsuTMb6fPx;i!UC7KYb%|7#0*1rkBnK7%+vPXnEaKY)^Z@cE~tdv z5O~(S95m)MG3DlVWgcF#eXiuJF6DAwMD|>k=q-|Qxg9E~lL#&C9qP%RTb}0~L-{(( zyXgIIIcjVpYQ|<>Nkrb14PSNU? zRhA#wI{$`*6U-~(gZQwhF7Dw{Osp>&Dq;QzsAdomZ0>^}S?^4H2*^ z4DviG%7L(bK5O$Tw}0#A$@OAjN3}Wh+nMVE0y7!Bl zA?Ki;8&Ph*Kjn`%oF4_X3w!MS=Uh;GjLIM9I# z4_B75i+{70^M9BA7Xtml90r1OX-4{J=X1y<{sHmixK~(D{#@kXt;tH-3%4&TKe;2Z z3HxSq!Fd!F1g4#({-fi1dW7VE${g+gi}%0k?*Eq$zkk*LFJH_5ddI&crT^hoS>>;H4GKqr?Hau", "PFC "] edition = "2018" license = "GPL-3.0-or-later" @@ -21,5 +21,6 @@ cw-storage-plus = "0.13" pfc-steak = { path = "../../packages/steak" } serde = { version = "1.0.103", default-features = false, features = ["derive"] } pfc-fee-split = "0.2.3" +pfc-dust-collector = { path = "../../packages/dust_collector" } #[dev-dependencies] #serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/contracts/hub/src/contract.rs b/contracts/hub/src/contract.rs index cf405de..03e8656 100644 --- a/contracts/hub/src/contract.rs +++ b/contracts/hub/src/contract.rs @@ -86,7 +86,9 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S execute::unpause_validator(deps, env, info.sender, validator) } ExecuteMsg::SetUnbondPeriod { unbond_period } => execute::set_unbond_period(deps, env, info.sender, unbond_period), - + ExecuteMsg::SetDustCollector { dust_collector } => {execute::set_dust_collector(deps,env,info.sender,dust_collector)} + ExecuteMsg::CollectDust { } => { execute::collect_dust(deps,env)} + ExecuteMsg::ReturnDenom { } => {execute::return_denom(deps,env,info.funds)} } } @@ -205,16 +207,19 @@ pub fn migrate(deps: DepsMut, env: Env, _msg: MigrateMsg) -> StdResult state.fee_rate.save(deps.storage, &Decimal::zero())?; state.fee_account_type.save(deps.storage,&FeeType::Wallet)?; ConfigV100::upgrade_stores(deps.storage,&deps.querier, env.contract.address)?; + state.dust_collector.save(deps.storage,&None)?; } "2.1.4" => { let state = State::default(); ConfigV100::upgrade_stores(deps.storage, &deps.querier,env.contract.address)?; state.fee_account_type.save(deps.storage,&FeeType::Wallet)?; + state.dust_collector.save(deps.storage,&None)?; } "2.1.5" => { ConfigV100::upgrade_stores(deps.storage, &deps.querier,env.contract.address)?; let state = State::default(); state.fee_account_type.save(deps.storage,&FeeType::Wallet)?; + state.dust_collector.save(deps.storage,&None)?; } "2.1.6" | "2.1.7" => { let state = State::default(); @@ -226,11 +231,13 @@ pub fn migrate(deps: DepsMut, env: Env, _msg: MigrateMsg) -> StdResult )?; state.fee_account_type.save(deps.storage,&FeeType::Wallet)?; + state.dust_collector.save(deps.storage,&None)?; }, "2.1.8" => { let state = State::default(); state.fee_account_type.save(deps.storage,&FeeType::Wallet)?; + state.dust_collector.save(deps.storage,&None)?; } _ => {} }, diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index c02d64f..df4f6be 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -992,3 +992,45 @@ pub fn update_fee(deps: DepsMut, sender: Addr, new_fee: Decimal) -> StdResult, +) -> StdResult { + let state = State::default(); + + state.assert_owner(deps.storage, &sender)?; + if let Some(ref dust_addr ) = dust_collector { + state.dust_collector.save(deps.storage, &Some(deps.api.addr_validate(dust_addr )? ))?; + } else { + state.dust_collector.save(deps.storage, &None)?; + }; + let event = Event::new("steak/set_dust_collector") + .add_attribute("dust_collector", dust_collector.unwrap_or("-cleared-".into())); + + Ok(Response::new() + .add_event(event) + .add_attribute("action", "steakhub/set_dust_collector")) +} + +pub fn collect_dust(deps: DepsMut, _env: Env) -> StdResult { + let state = State::default(); + + if let Some(_dust_addr) = state.dust_collector.load(deps.storage)? { + Ok(Response::new().add_attribute("dust", "tbd")) + } else { + Err(StdError::generic_err("No dust collector set")) + } +} + +pub fn return_denom(deps: DepsMut, _env: Env, _funds: Vec) -> StdResult { + let state = State::default(); + + if let Some(_dust_addr) = state.dust_collector.load(deps.storage)? { + Ok(Response::new().add_attribute("dust", "returned")) + } else { + Err(StdError::generic_err("No dust collector set")) + } +} \ No newline at end of file diff --git a/contracts/hub/src/queries.rs b/contracts/hub/src/queries.rs index bf265d6..cb5e031 100644 --- a/contracts/hub/src/queries.rs +++ b/contracts/hub/src/queries.rs @@ -42,6 +42,7 @@ pub fn config(deps: Deps) -> StdResult { max_fee_rate: state.max_fee_rate.load(deps.storage)?, validators: validator_active_vec, paused_validators, + dust_collector:None }) } diff --git a/contracts/hub/src/state.rs b/contracts/hub/src/state.rs index d53532b..2a0cadd 100644 --- a/contracts/hub/src/state.rs +++ b/contracts/hub/src/state.rs @@ -42,6 +42,8 @@ pub(crate) struct State<'a> { pub validators_active: Item<'a, Vec>, /// coins in 'denom' held before reinvest was called. pub prev_denom: Item<'a, Uint128>, + /// Dust Collector contract + pub dust_collector: Item<'a, Option>, } impl Default for State<'static> { @@ -77,7 +79,8 @@ impl Default for State<'static> { unbond_requests: IndexedMap::new("unbond_requests", ubr_indexes), validators_active: Item::new("validators_active"), prev_denom: Item::new("prev_denom"), - fee_account_type: Item::new("fee_account_type") + fee_account_type: Item::new("fee_account_type"), + dust_collector: Item::new("dust_collector") } } } diff --git a/contracts/hub/src/testing/tests.rs b/contracts/hub/src/testing/tests.rs index 94fc901..be11646 100644 --- a/contracts/hub/src/testing/tests.rs +++ b/contracts/hub/src/testing/tests.rs @@ -1,10 +1,10 @@ use std::str::FromStr; -use cosmwasm_std::testing::{mock_env, mock_info, MockApi, MockStorage, MOCK_CONTRACT_ADDR}; use cosmwasm_std::{ - to_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, DistributionMsg, Event, Order, OwnedDeps, - Reply, ReplyOn, StdError, SubMsg, SubMsgResponse, Uint128, WasmMsg, + Addr, BankMsg, Coin, CosmosMsg, Decimal, DistributionMsg, Event, Order, OwnedDeps, Reply, + ReplyOn, StdError, SubMsg, SubMsgResponse, to_binary, Uint128, WasmMsg, }; +use cosmwasm_std::testing::{MOCK_CONTRACT_ADDR, mock_env, mock_info, MockApi, MockStorage}; use cw20::{Cw20ExecuteMsg, MinterResponse}; use cw20_base::msg::InstantiateMsg as Cw20InstantiateMsg; @@ -55,11 +55,11 @@ fn setup_test() -> OwnedDeps { "charlie".to_string(), ], label: None, - marketing:None - + marketing: None, + dust_collector:None }, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 1); assert_eq!( @@ -75,15 +75,15 @@ fn setup_test() -> OwnedDeps { initial_balances: vec![], mint: Some(MinterResponse { minter: MOCK_CONTRACT_ADDR.to_string(), - cap: None + cap: None, }), marketing: None, }) - .unwrap(), + .unwrap(), funds: vec![], label: "steak_token".to_string(), }), - REPLY_INSTANTIATE_TOKEN + REPLY_INSTANTIATE_TOKEN, ) ); @@ -102,7 +102,7 @@ fn setup_test() -> OwnedDeps { }), }, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 0); @@ -136,11 +136,11 @@ fn setup_test_fee_split() -> OwnedDeps { "charlie".to_string(), ], label: None, - marketing:None - + marketing: None, + dust_collector:None }, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 1); assert_eq!( @@ -156,15 +156,15 @@ fn setup_test_fee_split() -> OwnedDeps { initial_balances: vec![], mint: Some(MinterResponse { minter: MOCK_CONTRACT_ADDR.to_string(), - cap: None + cap: None, }), marketing: None, }) - .unwrap(), + .unwrap(), funds: vec![], label: "steak_token".to_string(), }), - REPLY_INSTANTIATE_TOKEN + REPLY_INSTANTIATE_TOKEN, ) ); @@ -183,7 +183,7 @@ fn setup_test_fee_split() -> OwnedDeps { }), }, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 0); @@ -209,16 +209,17 @@ fn proper_instantiation() { epoch_period: 259200, unbond_period: 1814400, denom: "uxyz".to_string(), - fee_type:"Wallet".to_string(), + fee_type: "Wallet".to_string(), fee_account: "the_fee_man".to_string(), fee_rate: Decimal::from_ratio(10_u128, 100_u128), max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), validators: vec![ "alice".to_string(), "bob".to_string(), - "charlie".to_string() + "charlie".to_string(), ], - paused_validators: vec![] + paused_validators: vec![], + dust_collector: None, } ); @@ -254,16 +255,17 @@ fn proper_instantiation() { epoch_period: 259200, unbond_period: 1814400, denom: "uxyz".to_string(), - fee_type:"FeeSplit".to_string(), + fee_type: "FeeSplit".to_string(), fee_account: "fee_split_contract".to_string(), fee_rate: Decimal::from_ratio(10_u128, 100_u128), max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), validators: vec![ "alice".to_string(), "bob".to_string(), - "charlie".to_string() + "charlie".to_string(), ], - paused_validators: vec![] + paused_validators: vec![], + dust_collector: None, } ); } @@ -280,7 +282,7 @@ fn bonding() { mock_info("user_1", &[Coin::new(1_000_000, "uxyz")]), ExecuteMsg::Bond { receiver: None }, ) - .unwrap(); + .unwrap(); // 3 messages. (switched to 3 so we can 'send' instead of 'transfer' minted tokens, so contract will know about it // 1 - delegate @@ -299,10 +301,10 @@ fn bonding() { contract_addr: "steak_token".to_string(), msg: to_binary(&Cw20ExecuteMsg::Mint { recipient: "cosmos2contract".to_string(), - amount: Uint128::new(1_000_000) + amount: Uint128::new(1_000_000), }) - .unwrap(), - funds: vec![] + .unwrap(), + funds: vec![], }), gas_limit: None, reply_on: ReplyOn::Never, @@ -319,8 +321,8 @@ fn bonding() { recipient: "user_1".to_string(), amount: Uint128::new(1000000), }) - .unwrap(), - funds: vec![] + .unwrap(), + funds: vec![], }), gas_limit: None, reply_on: ReplyOn::Never, @@ -328,7 +330,6 @@ fn bonding() { ); - // Bond when there are existing delegations, and Luna:Steak exchange rate is >1 // Previously user 1 delegated 1,000,000 uluna. We assume we have accumulated 2.5% yield at 1025000 staked deps.querier.set_staking_delegations(&[ @@ -347,7 +348,7 @@ fn bonding() { receiver: Some("user_3".to_string()), }, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 3); assert_eq!( @@ -362,13 +363,13 @@ fn bonding() { contract_addr: "steak_token".to_string(), msg: to_binary(&Cw20ExecuteMsg::Mint { recipient: "cosmos2contract".to_string(), - amount: Uint128::new(12043) + amount: Uint128::new(12043), }) - .unwrap(), - funds: vec![] + .unwrap(), + funds: vec![], }), gas_limit: None, - reply_on: ReplyOn::Never + reply_on: ReplyOn::Never, } ); @@ -382,11 +383,11 @@ fn bonding() { recipient: "user_3".to_string(), amount: Uint128::new(12043), }) - .unwrap(), - funds: vec![] + .unwrap(), + funds: vec![], }), gas_limit: None, - reply_on: ReplyOn::Never + reply_on: ReplyOn::Never, } ); @@ -428,7 +429,7 @@ fn harvesting() { mock_info("worker", &[]), ExecuteMsg::Harvest {}, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 4); assert_eq!( @@ -465,10 +466,10 @@ fn harvesting() { msg: CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: MOCK_CONTRACT_ADDR.to_string(), msg: to_binary(&ExecuteMsg::Callback(CallbackMsg::Reinvest {})).unwrap(), - funds: vec![] + funds: vec![], }), gas_limit: None, - reply_on: ReplyOn::Never + reply_on: ReplyOn::Never, } ); } @@ -494,7 +495,7 @@ fn registering_unlocked_coins() { }), }, ) - .unwrap(); + .unwrap(); // Unlocked coins in contract state should have been updated let unlocked_coins = state.unlocked_coins.load(deps.as_ref().storage).unwrap(); @@ -506,7 +507,7 @@ fn registering_unlocked_coins() { Coin::new(345, "uusd"), Coin::new( 69420, - "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B" + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", ), ] ); @@ -522,8 +523,8 @@ fn reinvesting() { Delegation::new("bob", 333333, "uxyz"), Delegation::new("charlie", 333333, "uxyz"), ]); - state.prev_denom.save(deps.as_mut().storage,&Uint128::from(0 as u32)).unwrap(); - deps.querier.set_bank_balances(&[Coin::new(234u128,"uxyz")]); + state.prev_denom.save(deps.as_mut().storage, &Uint128::from(0 as u32)).unwrap(); + deps.querier.set_bank_balances(&[Coin::new(234u128, "uxyz")]); // After the swaps, `unlocked_coins` should contain only uxyz and unknown denoms state @@ -547,7 +548,7 @@ fn reinvesting() { mock_info(MOCK_CONTRACT_ADDR, &[]), ExecuteMsg::Callback(CallbackMsg::Reinvest {}), ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 2); assert_eq!( @@ -556,7 +557,7 @@ fn reinvesting() { id: 0, msg: Delegation::new("bob", 234 - 23, "uxyz").to_cosmos_msg(), gas_limit: None, - reply_on: ReplyOn::Never + reply_on: ReplyOn::Never, } ); let send_msg = BankMsg::Send { @@ -569,7 +570,7 @@ fn reinvesting() { id: 0, msg: CosmosMsg::Bank(send_msg), gas_limit: None, - reply_on: ReplyOn::Never + reply_on: ReplyOn::Never, } ); @@ -579,10 +580,9 @@ fn reinvesting() { unlocked_coins, vec![Coin::new( 69420, - "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B" + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", )], ); - } #[test] @@ -595,8 +595,8 @@ fn reinvesting_fee_split() { Delegation::new("bob", 333333, "uxyz"), Delegation::new("charlie", 333333, "uxyz"), ]); - state.prev_denom.save(deps.as_mut().storage,&Uint128::from(0 as u32)).unwrap(); - deps.querier.set_bank_balances(&[Coin::new(234u128,"uxyz")]); + state.prev_denom.save(deps.as_mut().storage, &Uint128::from(0 as u32)).unwrap(); + deps.querier.set_bank_balances(&[Coin::new(234u128, "uxyz")]); // After the swaps, `unlocked_coins` should contain only uxyz and unknown denoms state @@ -620,7 +620,7 @@ fn reinvesting_fee_split() { mock_info(MOCK_CONTRACT_ADDR, &[]), ExecuteMsg::Callback(CallbackMsg::Reinvest {}), ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 2); assert_eq!( @@ -629,18 +629,18 @@ fn reinvesting_fee_split() { id: 0, msg: Delegation::new("bob", 234 - 23, "uxyz").to_cosmos_msg(), gas_limit: None, - reply_on: ReplyOn::Never + reply_on: ReplyOn::Never, } ); - let send_msg = pfc_fee_split::fee_split_msg::ExecuteMsg::Deposit{ flush:false}; + let send_msg = pfc_fee_split::fee_split_msg::ExecuteMsg::Deposit { flush: false }; assert_eq!( res.messages[1], SubMsg { id: 0, - msg: send_msg.into_cosmos_msg("fee_split_contract", vec![Coin::new(23u128,"uxyz")]).unwrap(), + msg: send_msg.into_cosmos_msg("fee_split_contract", vec![Coin::new(23u128, "uxyz")]).unwrap(), gas_limit: None, - reply_on: ReplyOn::Never + reply_on: ReplyOn::Never, } ); @@ -650,10 +650,9 @@ fn reinvesting_fee_split() { unlocked_coins, vec![Coin::new( 69420, - "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B" + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", )], ); - } #[test] @@ -672,7 +671,7 @@ fn queuing_unbond() { msg: to_binary(&ReceiveMsg::QueueUnbond { receiver: None }).unwrap(), }), ) - .unwrap_err(); + .unwrap_err(); assert_eq!( err, @@ -691,7 +690,7 @@ fn queuing_unbond() { msg: to_binary(&ReceiveMsg::QueueUnbond { receiver: None }).unwrap(), }), ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 0); @@ -707,10 +706,10 @@ fn queuing_unbond() { msg: to_binary(&ReceiveMsg::QueueUnbond { receiver: Some("user_3".to_string()), }) - .unwrap(), + .unwrap(), }), ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 1); assert_eq!( @@ -720,10 +719,10 @@ fn queuing_unbond() { msg: CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: MOCK_CONTRACT_ADDR.to_string(), msg: to_binary(&ExecuteMsg::SubmitBatch {}).unwrap(), - funds: vec![] + funds: vec![], }), gas_limit: None, - reply_on: ReplyOn::Never + reply_on: ReplyOn::Never, } ); @@ -748,7 +747,7 @@ fn queuing_unbond() { UnbondRequest { id: 1, user: Addr::unchecked("user_1"), - shares: Uint128::new(23456) + shares: Uint128::new(23456), } ); assert_eq!( @@ -756,7 +755,7 @@ fn queuing_unbond() { UnbondRequest { id: 1, user: Addr::unchecked("user_3"), - shares: Uint128::new(69420) + shares: Uint128::new(69420), } ); @@ -767,7 +766,7 @@ fn queuing_unbond() { PendingBatch { id: 1, usteak_to_burn: Uint128::new(92876), // 23,456 + 69,420 - est_unbond_start_time: 269200 + est_unbond_start_time: 269200, } ); } @@ -844,7 +843,7 @@ fn submitting_batch() { mock_info(MOCK_CONTRACT_ADDR, &[]), ExecuteMsg::SubmitBatch {}, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 4); assert_eq!( @@ -859,7 +858,7 @@ fn submitting_batch() { res.messages[2], SubMsg::reply_on_success( Undelegation::new("charlie", 31732, "uxyz").to_cosmos_msg(), - REPLY_REGISTER_RECEIVED_COINS + REPLY_REGISTER_RECEIVED_COINS, ) ); assert_eq!( @@ -871,11 +870,11 @@ fn submitting_batch() { msg: to_binary(&Cw20ExecuteMsg::Burn { amount: Uint128::new(92876) }) - .unwrap(), - funds: vec![] + .unwrap(), + funds: vec![], }), gas_limit: None, - reply_on: ReplyOn::Never + reply_on: ReplyOn::Never, } ); @@ -886,7 +885,7 @@ fn submitting_batch() { PendingBatch { id: 2, usteak_to_burn: Uint128::zero(), - est_unbond_start_time: 528401 // 269,201 + 259,200 + est_unbond_start_time: 528401, // 269,201 + 259,200 } ); @@ -902,7 +901,7 @@ fn submitting_batch() { reconciled: false, total_shares: Uint128::new(92876), amount_unclaimed: Uint128::new(95197), - est_unbond_end_time: 2083601 // 269,201 + 1,814,400 + est_unbond_end_time: 2083601, // 269,201 + 1,814,400 } ); } @@ -986,7 +985,7 @@ fn reconciling() { mock_info("worker", &[]), ExecuteMsg::Reconcile {}, ) - .unwrap(); + .unwrap(); // Expected received: batch 2 + batch 3 = 1385 + 1506 = 2891 // Expected unlocked: 10000 @@ -1154,7 +1153,7 @@ fn withdrawing_unbonded() { mock_info("user_1", &[]), ExecuteMsg::WithdrawUnbonded { receiver: None }, ) - .unwrap_err(); + .unwrap_err(); assert_eq!(err, StdError::generic_err("withdrawable amount is zero")); @@ -1175,7 +1174,7 @@ fn withdrawing_unbonded() { mock_info("user_1", &[]), ExecuteMsg::WithdrawUnbonded { receiver: None }, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 1); assert_eq!( @@ -1184,10 +1183,10 @@ fn withdrawing_unbonded() { id: 0, msg: CosmosMsg::Bank(BankMsg::Send { to_address: "user_1".to_string(), - amount: vec![Coin::new(59646, "uxyz")] + amount: vec![Coin::new(59646, "uxyz")], }), gas_limit: None, - reply_on: ReplyOn::Never + reply_on: ReplyOn::Never, } ); @@ -1256,7 +1255,7 @@ fn withdrawing_unbonded() { receiver: Some("user_2".to_string()), }, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 1); assert_eq!( @@ -1265,10 +1264,10 @@ fn withdrawing_unbonded() { id: 0, msg: CosmosMsg::Bank(BankMsg::Send { to_address: "user_2".to_string(), - amount: vec![Coin::new(71155, "uxyz")] + amount: vec![Coin::new(71155, "uxyz")], }), gas_limit: None, - reply_on: ReplyOn::Never + reply_on: ReplyOn::Never, } ); @@ -1313,7 +1312,7 @@ fn adding_validator() { validator: "dave".to_string(), }, ) - .unwrap_err(); + .unwrap_err(); assert_eq!( err, @@ -1328,7 +1327,7 @@ fn adding_validator() { validator: "alice".to_string(), }, ) - .unwrap_err(); + .unwrap_err(); assert_eq!( err, @@ -1343,7 +1342,7 @@ fn adding_validator() { validator: "dave".to_string(), }, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 0); @@ -1354,7 +1353,7 @@ fn adding_validator() { String::from("alice"), String::from("bob"), String::from("charlie"), - String::from("dave") + String::from("dave"), ], ); } @@ -1378,7 +1377,7 @@ fn removing_validator() { validator: "charlie".to_string(), }, ) - .unwrap_err(); + .unwrap_err(); assert_eq!( err, @@ -1393,7 +1392,7 @@ fn removing_validator() { validator: "dave".to_string(), }, ) - .unwrap_err(); + .unwrap_err(); assert_eq!( err, @@ -1412,26 +1411,26 @@ fn removing_validator() { validator: "charlie".to_string(), }, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 2); assert_eq!( res.messages[0], SubMsg::reply_on_success( Redelegation::new("charlie", "alice", 170833, "uxyz").to_cosmos_msg(), - REPLY_REGISTER_RECEIVED_COINS + REPLY_REGISTER_RECEIVED_COINS, ), ); assert_eq!( res.messages[1], SubMsg::reply_on_success( Redelegation::new("charlie", "bob", 170833, "uxyz").to_cosmos_msg(), - REPLY_REGISTER_RECEIVED_COINS + REPLY_REGISTER_RECEIVED_COINS, ), ); let validators = state.validators.load(deps.as_ref().storage).unwrap(); - assert_eq!(validators, vec![String::from("alice"), String::from("bob")],); + assert_eq!(validators, vec![String::from("alice"), String::from("bob")], ); } #[test] @@ -1447,7 +1446,7 @@ fn transferring_ownership() { new_owner: "jake".to_string(), }, ) - .unwrap_err(); + .unwrap_err(); assert_eq!( err, @@ -1462,7 +1461,7 @@ fn transferring_ownership() { new_owner: "jake".to_string(), }, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 0); @@ -1475,7 +1474,7 @@ fn transferring_ownership() { mock_info("pumpkin", &[]), ExecuteMsg::AcceptOwnership {}, ) - .unwrap_err(); + .unwrap_err(); assert_eq!( err, @@ -1488,13 +1487,14 @@ fn transferring_ownership() { mock_info("jake", &[]), ExecuteMsg::AcceptOwnership {}, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 0); let owner = state.owner.load(deps.as_ref().storage).unwrap(); assert_eq!(owner, Addr::unchecked("jake")); } + #[test] fn splitting_fees() { let mut deps = setup_test(); @@ -1506,7 +1506,7 @@ fn splitting_fees() { mock_info("jake", &[]), ExecuteMsg::TransferFeeAccount { fee_account_type: "Wallet".to_string(), - new_fee_account: "charlie".to_string() + new_fee_account: "charlie".to_string(), }, ) .unwrap_err(); @@ -1522,7 +1522,7 @@ fn splitting_fees() { mock_info("larry", &[]), ExecuteMsg::TransferFeeAccount { fee_account_type: "xxxx".to_string(), - new_fee_account: "charlie".to_string() + new_fee_account: "charlie".to_string(), }, ) .unwrap_err(); @@ -1532,13 +1532,13 @@ fn splitting_fees() { StdError::generic_err("Invalid Fee type: Wallet or FeeSplit only") ); - execute( + execute( deps.as_mut(), mock_env(), mock_info("larry", &[]), ExecuteMsg::TransferFeeAccount { fee_account_type: "Wallet".to_string(), - new_fee_account: "charlie".to_string() + new_fee_account: "charlie".to_string(), }, ) .unwrap(); @@ -1552,16 +1552,17 @@ fn splitting_fees() { epoch_period: 259200, unbond_period: 1814400, denom: "uxyz".to_string(), - fee_type:"Wallet".to_string(), + fee_type: "Wallet".to_string(), fee_account: "charlie".to_string(), fee_rate: Decimal::from_ratio(10_u128, 100_u128), max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), validators: vec![ "alice".to_string(), "bob".to_string(), - "charlie".to_string() + "charlie".to_string(), ], - paused_validators: vec![] + paused_validators: vec![], + dust_collector: None, } ); @@ -1572,7 +1573,7 @@ fn splitting_fees() { mock_info("larry", &[]), ExecuteMsg::TransferFeeAccount { fee_account_type: "FeeSplit".to_string(), - new_fee_account: "contract".to_string() + new_fee_account: "contract".to_string(), }, ) .unwrap(); @@ -1586,16 +1587,17 @@ fn splitting_fees() { epoch_period: 259200, unbond_period: 1814400, denom: "uxyz".to_string(), - fee_type:"FeeSplit".to_string(), + fee_type: "FeeSplit".to_string(), fee_account: "contract".to_string(), fee_rate: Decimal::from_ratio(10_u128, 100_u128), max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), validators: vec![ "alice".to_string(), "bob".to_string(), - "charlie".to_string() + "charlie".to_string(), ], - paused_validators: vec![] + paused_validators: vec![], + dust_collector:None } ); } @@ -1795,7 +1797,7 @@ fn querying_unbond_requests() { res, vec![ unbond_requests[0].clone().into(), - unbond_requests[3].clone().into() + unbond_requests[3].clone().into(), ] ); @@ -1861,7 +1863,7 @@ fn computing_redelegations_for_removal() { compute_redelegations_for_removal( ¤t_delegations[3], ¤t_delegations[..3], - "uxyz" + "uxyz", ), expected, ); @@ -1876,9 +1878,9 @@ fn computing_redelegations_for_rebalancing() { Delegation::new("dave", 40471, "uxyz"), Delegation::new("evan", 2345, "uxyz"), ]; - let active_validators:Vec = vec!["alice".to_string(),"bob".to_string(), - "charlie".to_string(), "dave".to_string(), - "evan".to_string()]; + let active_validators: Vec = vec!["alice".to_string(), "bob".to_string(), + "charlie".to_string(), "dave".to_string(), + "evan".to_string()]; // uluna_per_validator = (69420 + 88888 + 1234 + 40471 + 2345) / 4 = 40471 // remainer = 3 // src_delegations: @@ -1910,15 +1912,15 @@ fn computing_redelegations_for_rebalancing() { ]; assert_eq!( - compute_redelegations_for_rebalancing(active_validators,¤t_delegations,Uint128::from(10 as u64)), + compute_redelegations_for_rebalancing(active_validators, ¤t_delegations, Uint128::from(10 as u64)), expected, ); - let partially_active = vec![ "alice".to_string(), - "charlie".to_string(), - "dave".to_string(), - "evan".to_string()]; + let partially_active = vec!["alice".to_string(), + "charlie".to_string(), + "dave".to_string(), + "evan".to_string()]; let partially_expected = vec![ Redelegation::new("alice", "dave", 10118, "uxyz"), @@ -1926,7 +1928,7 @@ fn computing_redelegations_for_rebalancing() { Redelegation::new("charlie", "evan", 38299, "uxyz"), ]; assert_eq!( - compute_redelegations_for_rebalancing(partially_active.clone(),¤t_delegations,Uint128::from(10 as u64)), + compute_redelegations_for_rebalancing(partially_active.clone(), ¤t_delegations, Uint128::from(10 as u64)), partially_expected, ); @@ -1935,7 +1937,7 @@ fn computing_redelegations_for_rebalancing() { Redelegation::new("charlie", "evan", 29414, "uxyz"), ]; assert_eq!( - compute_redelegations_for_rebalancing(partially_active,¤t_delegations,Uint128::from(15_000 as u64)), + compute_redelegations_for_rebalancing(partially_active, ¤t_delegations, Uint128::from(15_000 as u64)), partially_expected_minimums, ); } @@ -1956,7 +1958,7 @@ fn parsing_coin() { coin, Coin::new( 23456, - "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B" + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", ) ); @@ -2006,7 +2008,7 @@ fn adding_coins() { vec![ Coin::new(88888, "uatom"), Coin::new(23456, "uxyz"), - Coin::new(69420, "uusd") + Coin::new(69420, "uusd"), ] ); } @@ -2023,7 +2025,7 @@ fn receiving_funds() { &[Coin::new(12345, "uatom"), Coin::new(23456, "uxyz")], "uxyz", ) - .unwrap_err(); + .unwrap_err(); assert_eq!( err, StdError::generic_err("must deposit exactly one coin; received 2") diff --git a/init/migaloo_hub_init.json b/init/migaloo_hub_init.json new file mode 100644 index 0000000..7ba886d --- /dev/null +++ b/init/migaloo_hub_init.json @@ -0,0 +1,25 @@ +{ + "cw20_code_id": 3, + "owner": "migaloo1lu92zj8q6cmrptu09rp3343x9969r9qrsplfat", + "name": "bWHALE Token", + "symbol": "bWHALE", + "decimals": 6, + "epoch_period": 259200, + "unbond_period": 1814400, + "validators": [ + "migaloovaloper1670dvuv348eynr9lsmdrhqu3g7vpmzx94s460k", + "migaloovaloper1m9s6jkkt3fnt3qzx5htrsaxd6xhufyvtq4l7ps", + "migaloovaloper14ts0j42qkpr43a3tgxr7zz6l6zdf7hdeaayydd" + ], + "denom": "uwhale", + "fee_account": "migaloo1wug8sewp6cedgkmrmvhl3lf3tulagm9hnvy8p0rppz9yjw0g4wtqvk723g", + "fee_amount": "0.10", + "max_fee_amount": "0.10", + "fee_account_type": "FeeSplit", + "label": "boneWHALE", + "marketing": { + "project": "https://backbonelabs.io", + "description": "The GraveDigger is the Liquid Staking Product of BackBone Labs. It's liquid staking derivative (LSD) is boneWHALE.", + "marketing": "migaloo1lu92zj8q6cmrptu09rp3343x9969r9qrsplfat" + } +} \ No newline at end of file diff --git a/init/narwhal_hub_init.json b/init/narwhal_hub_init.json new file mode 100644 index 0000000..ac66def --- /dev/null +++ b/init/narwhal_hub_init.json @@ -0,0 +1,25 @@ +{ + "cw20_code_id": 24, + "owner": "migaloo1kdtdg0lvy8asxn8clnjfpusuvf93zuknltr2eh", + "name": "bWHALE Token", + "symbol": "bWHALE", + "decimals": 6, + "epoch_period": 259200, + "unbond_period": 1814400, + "validators": [ + "migaloovaloper16q6p6nkzc0525qnnrdd93urxv6pr8zhc4vhjy8", + "migaloovaloper1rqvctgdpafvc0k9fx4ng8ckt94x723zmp3g0jv", + "migaloovaloper18ulrp6juyj0tt0zmkrxn3ex4mkd3kkg6uk7nfx" + ], + "denom": "uwhale", + "fee_account": "migaloo1d6k8pahenpmx4spwfpf80zealtecfv7v0n7v49rxeu4lpzgzhrwq4wmhkr", + "fee_amount": "0.10", + "max_fee_amount": "0.10", + "fee_account_type": "FeeSplit", + "label": "boneWHALE", + "marketing": { + "project": "https://backbonelabs.io", + "description": "The GraveDigger is the Liquid Staking Product of BackBone Labs. It's liquid staking derivative (LSD) is boneWHALE.", + "marketing": "migaloo1kdtdg0lvy8asxn8clnjfpusuvf93zuknltr2eh" + } +} \ No newline at end of file diff --git a/packages/dust_collector/Cargo.toml b/packages/dust_collector/Cargo.toml new file mode 100644 index 0000000..20b3bf2 --- /dev/null +++ b/packages/dust_collector/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "pfc-dust-collector" +version = "3.0.0" +authors = ["PFC "] +edition = "2018" +description = "Liquid steaking protocol for the cosmos" +license = "GPL-3.0-or-later" +homepage = "https://liquidsteaking.app" +repository = "https://github.com/PFC-developer/steak-contracts" + +[dependencies] +cosmwasm-std = {version="1.2.1", features=["ibc3"]} +cosmwasm-schema = "1.2.1" + +schemars = "0.8.1" +serde = { version = "1.0.103", default-features = false, features = ["derive"] } + diff --git a/packages/steak/Cargo.toml b/packages/steak/Cargo.toml index 47a40d2..2cf2afc 100644 --- a/packages/steak/Cargo.toml +++ b/packages/steak/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak" -version = "2.1.15" +version = "2.2.2" authors = ["larry ", "PFC "] edition = "2018" description = "Liquid steaking protocol for the cosmos" @@ -9,7 +9,8 @@ homepage = "https://liquidsteaking.app" repository = "https://github.com/PFC-developer/steak-contracts" [dependencies] -cosmwasm-std = "1.0" +cosmwasm-std = {version="1.2.1", features=["ibc3"]} +cosmwasm-schema = "1.2.1" cw20 = "1.0.0" cw20-base = { version = "1.0.0", features = ["library"] } schemars = "0.8.1" diff --git a/packages/steak/src/hub.rs b/packages/steak/src/hub.rs index 9b4ff9a..2a034f5 100644 --- a/packages/steak/src/hub.rs +++ b/packages/steak/src/hub.rs @@ -1,9 +1,10 @@ -use cosmwasm_std::{to_binary, Addr, Coin, CosmosMsg, Decimal, Empty, StdResult, Uint128, WasmMsg}; +use std::str::FromStr; + +use cosmwasm_std::{Addr, Coin, CosmosMsg, Decimal, Empty, StdResult, to_binary, Uint128, WasmMsg}; use cw20::Cw20ReceiveMsg; use cw20_base::msg::InstantiateMarketingInfo as Cw20InstantiateMarketingInfo; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use std::str::FromStr; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct InstantiateMsg { @@ -36,7 +37,8 @@ pub struct InstantiateMsg { /// label for the CW20 token we create pub label: Option, /// Marketing info for the CW20 we create - pub marketing: Option + pub marketing: Option, + pub dust_collector: Option, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] @@ -86,9 +88,15 @@ pub enum ExecuteMsg { UpdateFee { new_fee: Decimal }, /// Callbacks; can only be invoked by the contract itself Callback(CallbackMsg), + // Set The Duster. + SetDustCollector { dust_collector: Option }, + /// Collect the Dust + CollectDust {}, + /// Return the Dust in shiny 'base denom' + ReturnDenom {}, } -#[derive(Serialize, Deserialize, Clone, Debug, Eq,PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum ReceiveMsg { /// Submit an unbonding request to the current unbonding queue; automatically invokes `unbond` @@ -96,7 +104,7 @@ pub enum ReceiveMsg { QueueUnbond { receiver: Option }, } -#[derive(Serialize, Deserialize, Clone, Debug,Eq, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum CallbackMsg { /// Following the swaps, stake the Luna acquired to the whitelisted validators @@ -113,7 +121,7 @@ impl CallbackMsg { } } -#[derive(Serialize, Deserialize, Clone, Debug, Eq,PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum QueryMsg { /// The contract's configurations. Response: `ConfigResponse` @@ -137,7 +145,7 @@ pub enum QueryMsg { start_after: Option, limit: Option, }, - /// Enumreate all outstanding unbonding requests from given a user. Response: `Vec` + /// Enumerate all outstanding unbonding requests from given a user. Response: `Vec` UnbondRequestsByUser { user: String, start_after: Option, @@ -145,7 +153,7 @@ pub enum QueryMsg { }, } -#[derive(Serialize, Deserialize, Clone, Debug, Eq,PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, JsonSchema)] pub struct ConfigResponse { /// Account who can call certain privileged functions pub owner: String, @@ -160,7 +168,7 @@ pub struct ConfigResponse { /// denomination of coins to steak (uXXXX) pub denom: String, /// type of account to send the fees too - pub fee_type:String, + pub fee_type: String, /// Fee Account to send fees too pub fee_account: String, /// Fee "1.00 = 100%" @@ -170,9 +178,10 @@ pub struct ConfigResponse { /// Set of validators who will receive the delegations pub validators: Vec, pub paused_validators: Vec, + pub dust_collector: Option, } -#[derive(Serialize, Deserialize, Clone, Debug,Eq, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, JsonSchema)] pub struct StateResponse { /// Total supply to the Steak token pub total_usteak: Uint128, @@ -184,7 +193,7 @@ pub struct StateResponse { pub unlocked_coins: Vec, } -#[derive(Serialize, Deserialize, Clone, Debug, Eq,PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, JsonSchema)] pub struct PendingBatch { /// ID of this batch pub id: u64, @@ -194,7 +203,7 @@ pub struct PendingBatch { pub est_unbond_start_time: u64, } -#[derive(Serialize, Deserialize, Clone, Debug, Eq,PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, JsonSchema)] pub struct Batch { /// ID of this batch pub id: u64, @@ -208,7 +217,7 @@ pub struct Batch { pub est_unbond_end_time: u64, } -#[derive(Serialize, Deserialize, Clone, Debug, Eq,PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, JsonSchema)] pub struct UnbondRequest { /// ID of the batch pub id: u64, @@ -218,7 +227,7 @@ pub struct UnbondRequest { pub shares: Uint128, } -#[derive(Serialize, Deserialize, Clone, Debug, Eq,PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, JsonSchema)] pub struct UnbondRequestsByBatchResponseItem { /// The user's address pub user: String, @@ -259,6 +268,7 @@ pub enum FeeType { Wallet, FeeSplit, } + impl FromStr for FeeType { type Err = (); fn from_str(s: &str) -> Result { @@ -269,6 +279,7 @@ impl FromStr for FeeType { } } } + impl ToString for FeeType { fn to_string(&self) -> String { match &self { diff --git a/packages/steak/src/hub_tf.rs b/packages/steak/src/hub_tf.rs new file mode 100644 index 0000000..daa35bc --- /dev/null +++ b/packages/steak/src/hub_tf.rs @@ -0,0 +1,89 @@ +use cosmwasm_schema::cw_serde; +// +use cosmwasm_std::{Decimal, Uint128}; + +use crate::hub::CallbackMsg; + +#[cw_serde] +pub struct InstantiateMsg { + /// Account who can call certain privileged functions + pub owner: String, + /// Name of the liquid staking token + pub epoch_period: u64, + /// The staking module's unbonding time, in seconds + pub unbond_period: u64, + /// Initial set of validators who will receive the delegations + pub validators: Vec, + /// denomination of coins to steak (uXXXX) + pub denom: String, + /// denomination of the steak token (eg steakLuna) + pub steak_denom: String, + /// type of fee account + pub fee_account_type: String, + /// Fee Account to send fees too + pub fee_account: String, + /// Fee "1.00 = 100%" + pub fee_amount: Decimal, + /// Max Fee "1.00 = 100%" + pub max_fee_amount: Decimal, + /// kuji_token_factory - uses token factory, but it's at a different module + pub kuji_token_factory: bool, + /// The Dust collector contract + pub dust_collector: Option, +} + +#[cw_serde] +pub enum ExecuteMsg { + /// Bond specified amount of Luna + Bond { receiver: Option, exec_msg: Option }, + /// Bond specified amount of Luna + Unbond { receiver: Option }, + + /// Withdraw Luna that have finished un-bonding in previous batches + WithdrawUnbonded { receiver: Option }, + /// Withdraw Luna that has finished unbonding in previous batches, for given address + WithdrawUnbondedAdmin { address: String }, + /// Add a validator to the whitelist; callable by the owner + AddValidator { validator: String }, + /// Remove a validator from the whitelist; callable by the owner + RemoveValidator { validator: String }, + /// Remove a validator from the whitelist; callable by the owner. Does not undelegate. use for typos + RemoveValidatorEx { validator: String }, + + /// Pause a validator from accepting new delegations + PauseValidator { validator: String }, + /// Unpause a validator from accepting new delegations + UnPauseValidator { validator: String }, + + /// Transfer ownership to another account; will not take effect unless the new owner accepts + TransferOwnership { new_owner: String }, + /// Accept an ownership transfer + AcceptOwnership {}, + /// Claim staking rewards, swap all for Luna, and restake + Harvest {}, + /// Use redelegations to balance the amounts of Luna delegated to validators + Rebalance { minimum: Uint128 }, + /// Update Luna amounts in unbonding batches to reflect any slashing or rounding errors + Reconcile {}, + /// Submit the current pending batch of unbonding requests to be unbonded + SubmitBatch {}, + /// Set unbond period + SetUnbondPeriod { unbond_period: u64 }, + + /// Transfer Fee collection account to another account + TransferFeeAccount { + fee_account_type: String, + new_fee_account: String, + }, + /// Update fee collection amount + UpdateFee { new_fee: Decimal }, + /// Callbacks; can only be invoked by the contract itself + Callback(CallbackMsg), + /// Set Dust Collector Contract + SetDustCollector { dust_collector: Option }, + /// Collect the Dust + CollectDust {}, + /// Return the Dust in shiny 'base denom' + ReturnDenom {}, +} + diff --git a/packages/steak/src/lib.rs b/packages/steak/src/lib.rs index b1f29ab..1690352 100644 --- a/packages/steak/src/lib.rs +++ b/packages/steak/src/lib.rs @@ -1,4 +1,5 @@ pub mod hub; +pub mod hub_tf; // this was copied from eris-staking's branch of STEAK. // From 6475c202db4cfe554fe4c3af2481785dd4147d5b Mon Sep 17 00:00:00 2001 From: The PFC <81114960+PFC-developer@users.noreply.github.com> Date: Fri, 24 Feb 2023 12:46:09 -0600 Subject: [PATCH 32/77] feat: tf --- packages/dust_collector/README.md | 6 ++++++ packages/dust_collector/src/dust_collector.rs | 9 +++++++++ packages/dust_collector/src/lib.rs | 2 ++ 3 files changed, 17 insertions(+) create mode 100644 packages/dust_collector/README.md create mode 100644 packages/dust_collector/src/dust_collector.rs create mode 100644 packages/dust_collector/src/lib.rs diff --git a/packages/dust_collector/README.md b/packages/dust_collector/README.md new file mode 100644 index 0000000..6b7571e --- /dev/null +++ b/packages/dust_collector/README.md @@ -0,0 +1,6 @@ +# Dust Collector Shared Messages +Messages used to call the Dust collector + +## License + +Contents of this repository are open source under [GNU General Public License v3](https://www.gnu.org/licenses/gpl-3.0.en.html) or later. diff --git a/packages/dust_collector/src/dust_collector.rs b/packages/dust_collector/src/dust_collector.rs new file mode 100644 index 0000000..9f006bc --- /dev/null +++ b/packages/dust_collector/src/dust_collector.rs @@ -0,0 +1,9 @@ + +use cosmwasm_schema::cw_serde; + + +#[cw_serde] +pub enum ExecuteMsg { + /// get some dust + DustReceived(), +} diff --git a/packages/dust_collector/src/lib.rs b/packages/dust_collector/src/lib.rs new file mode 100644 index 0000000..f2f0a6c --- /dev/null +++ b/packages/dust_collector/src/lib.rs @@ -0,0 +1,2 @@ + +pub mod dust_collector; From 74ebc6f9c1682503f1aaceb5388c769a96b26252 Mon Sep 17 00:00:00 2001 From: The PFC <81114960+PFC-developer@users.noreply.github.com> Date: Fri, 24 Feb 2023 14:04:27 -0600 Subject: [PATCH 33/77] feat: re-enable reply function for dust-collector --- build_arm.sh | 2 +- contracts/hub-tf/src/contract.rs | 62 ++++++++++++++++---------------- contracts/hub-tf/src/execute.rs | 2 +- init/migaloo_hub-tf_init.json | 17 +++++++++ 4 files changed, 49 insertions(+), 34 deletions(-) create mode 100644 init/migaloo_hub-tf_init.json diff --git a/build_arm.sh b/build_arm.sh index d9eafb4..af37350 100755 --- a/build_arm.sh +++ b/build_arm.sh @@ -2,4 +2,4 @@ docker run --rm -v "$(pwd)":/code \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ - cosmwasm/workspace-optimizer-arm64:0.12.10 + cosmwasm/workspace-optimizer-arm64:0.12.11 diff --git a/contracts/hub-tf/src/contract.rs b/contracts/hub-tf/src/contract.rs index 5af1d78..582c462 100644 --- a/contracts/hub-tf/src/contract.rs +++ b/contracts/hub-tf/src/contract.rs @@ -1,15 +1,12 @@ -use cosmwasm_std::{ - entry_point, to_binary, Binary, Deps, DepsMut, Env, MessageInfo, - Response, StdError, StdResult, -}; +use cosmwasm_std::{Binary, Deps, DepsMut, entry_point, Env, MessageInfo, Reply, Response, StdError, StdResult, to_binary}; +use cw2::{ContractVersion, get_contract_version, set_contract_version}; -use pfc_steak::hub::{CallbackMsg, MigrateMsg, QueryMsg}; - -//use crate::helpers::{ unwrap_reply}; +use pfc_steak::hub::{CallbackMsg, MigrateMsg, QueryMsg}; +use pfc_steak::hub_tf::{ExecuteMsg, InstantiateMsg}; use crate::{execute, queries}; -use cw2::{get_contract_version, set_contract_version, ContractVersion}; -use pfc_steak::hub_tf::{ExecuteMsg, InstantiateMsg}; + +//use crate::helpers::{ unwrap_reply}; /// Contract name that is used for migration. pub const CONTRACT_NAME: &str = "steak-hub-tf"; @@ -33,8 +30,7 @@ pub fn instantiate( pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> StdResult { let api = deps.api; match msg { - - ExecuteMsg::Bond { receiver,exec_msg } => execute::bond( + ExecuteMsg::Bond { receiver, exec_msg } => execute::bond( deps, env, receiver @@ -42,15 +38,15 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S .transpose()? .unwrap_or(info.sender), info.funds, - exec_msg - ), ExecuteMsg::Unbond { receiver} => execute::queue_unbond( + exec_msg, + ), + ExecuteMsg::Unbond { receiver } => execute::queue_unbond( deps, env, receiver .map(|s| api.addr_validate(&s)) .transpose()? - .unwrap_or(info.sender),info.funds - + .unwrap_or(info.sender), info.funds, ), ExecuteMsg::WithdrawUnbonded { receiver } => execute::withdraw_unbonded( deps, @@ -82,7 +78,7 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S ExecuteMsg::Rebalance { minimum } => execute::rebalance(deps, env, minimum), ExecuteMsg::Reconcile {} => execute::reconcile(deps, env), ExecuteMsg::SubmitBatch {} => execute::submit_batch(deps, env), - ExecuteMsg::TransferFeeAccount { fee_account_type,new_fee_account } => { + ExecuteMsg::TransferFeeAccount { fee_account_type, new_fee_account } => { execute::transfer_fee_account(deps, info.sender, fee_account_type, new_fee_account) } ExecuteMsg::UpdateFee { new_fee } => execute::update_fee(deps, info.sender, new_fee), @@ -93,11 +89,11 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S ExecuteMsg::UnPauseValidator { validator } => { execute::unpause_validator(deps, env, info.sender, validator) } - ExecuteMsg::SetUnbondPeriod { unbond_period } => execute::set_unbond_period(deps, env, info.sender, unbond_period), + ExecuteMsg::SetUnbondPeriod { unbond_period } => execute::set_unbond_period(deps, env, info.sender, unbond_period), - ExecuteMsg::SetDustCollector { dust_collector } => {execute::set_dust_collector(deps,env,info.sender,dust_collector)} - ExecuteMsg::CollectDust { } => { execute::collect_dust(deps,env)} - ExecuteMsg::ReturnDenom { } => {execute::return_denom(deps,env,info.funds)} + ExecuteMsg::SetDustCollector { dust_collector } => { execute::set_dust_collector(deps, env, info.sender, dust_collector) } + ExecuteMsg::CollectDust {} => { execute::collect_dust(deps, env) } + ExecuteMsg::ReturnDenom {} => { execute::return_denom(deps, env, info.funds) } } } @@ -118,21 +114,25 @@ fn callback( CallbackMsg::Reinvest {} => execute::reinvest(deps, env), } } -/* + #[entry_point] pub fn reply(deps: DepsMut, env: Env, reply: Reply) -> StdResult { match reply.id { - 1 => execute::register_steak_token(deps, unwrap_reply(reply)?), REPLY_REGISTER_RECEIVED_COINS => { - execute::register_received_coins(deps, env, unwrap_reply(reply)?.events) + execute::collect_dust(deps, env) + } + _ => { + Err(StdError::generic_err(format!( + "invalid reply id: {} {:?}", + reply.id, + reply.result + )) + ) } - id => Err(StdError::generic_err(format!( - "invalid reply id: {}; must be 1-2", - id - ))), } + } -*/ + #[entry_point] pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { @@ -179,16 +179,14 @@ pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult match contract_version.version.as_ref() { #[allow(clippy::single_match)] - "0" => { - - } + "0" => {} _ => {} }, _ => { return Err(StdError::generic_err( "contract name is not the same. aborting {}", - )) + )); } } /* diff --git a/contracts/hub-tf/src/execute.rs b/contracts/hub-tf/src/execute.rs index 2744e38..49f7337 100644 --- a/contracts/hub-tf/src/execute.rs +++ b/contracts/hub-tf/src/execute.rs @@ -1040,7 +1040,7 @@ pub fn collect_dust(deps: DepsMut, _env: Env) -> StdResult { if let Some(_dust_addr) = state.dust_collector.load(deps.storage)? { Ok(Response::new().add_attribute("dust", "tbd")) } else { - Err(StdError::generic_err("No Dust collector set")) + Ok(Response::new().add_attribute("dust", "dust-collector-called")) } } diff --git a/init/migaloo_hub-tf_init.json b/init/migaloo_hub-tf_init.json new file mode 100644 index 0000000..003c51b --- /dev/null +++ b/init/migaloo_hub-tf_init.json @@ -0,0 +1,17 @@ +{ + "owner": "migaloo1lu92zj8q6cmrptu09rp3343x9969r9qrsplfat", + "epoch_period": 259200, + "unbond_period": 1814400, + "validators": [ + "migaloovaloper1670dvuv348eynr9lsmdrhqu3g7vpmzx94s460k", + "migaloovaloper1m9s6jkkt3fnt3qzx5htrsaxd6xhufyvtq4l7ps", + "migaloovaloper14ts0j42qkpr43a3tgxr7zz6l6zdf7hdeaayydd" + ], + "denom": "uwhale", + "steak_denom": "boneWhale", + "fee_account": "migaloo1wug8sewp6cedgkmrmvhl3lf3tulagm9hnvy8p0rppz9yjw0g4wtqvk723g", + "fee_amount": "0.10", + "max_fee_amount": "0.10", + "fee_account_type": "FeeSplit", + "kuji_token_factory": false +} \ No newline at end of file From 37494227dda26cec9c2e2b8bc439c33f9668298d Mon Sep 17 00:00:00 2001 From: The PFC <81114960+PFC-developer@users.noreply.github.com> Date: Sun, 26 Feb 2023 12:28:17 -0600 Subject: [PATCH 34/77] feat: kuji version works fix: don't need cosmwasm_1_1 (doesn't work for kuji) --- Cargo.lock | 19 +--- contracts/hub-tf/Cargo.toml | 7 +- contracts/hub-tf/src/execute.rs | 60 +++++------ contracts/hub-tf/src/kujira/denom.rs | 144 +++++++++++++++++++++++++++ contracts/hub-tf/src/kujira/mod.rs | 2 + contracts/hub-tf/src/lib.rs | 1 + init/harpoon_hub-tf_init.json | 17 ++++ 7 files changed, 199 insertions(+), 51 deletions(-) create mode 100644 contracts/hub-tf/src/kujira/denom.rs create mode 100644 contracts/hub-tf/src/kujira/mod.rs create mode 100644 init/harpoon_hub-tf_init.json diff --git a/Cargo.lock b/Cargo.lock index f9708a2..6889e87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -654,22 +654,6 @@ dependencies = [ "sha2 0.10.6", ] -[[package]] -name = "kujira" -version = "0.7.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b69b49819362f0ab701d4c322e86a0b2735b6129ca857b3b982b2a6ec5431b22" -dependencies = [ - "anyhow", - "cosmwasm-std", - "cw20 1.0.0", - "hex", - "schemars", - "serde", - "sha2 0.10.6", - "thiserror", -] - [[package]] name = "libc" version = "0.2.124" @@ -751,14 +735,13 @@ dependencies = [ [[package]] name = "pfc-steak-hub-tf" -version = "3.0.1" +version = "3.0.2" dependencies = [ "cosmwasm-std", "cw-item-set", "cw-ownable", "cw-storage-plus 1.0.1", "cw2 1.0.1", - "kujira", "osmosis-std-derive", "pfc-dust-collector", "pfc-fee-split", diff --git a/contracts/hub-tf/Cargo.toml b/contracts/hub-tf/Cargo.toml index a9a140c..cbe4d96 100644 --- a/contracts/hub-tf/Cargo.toml +++ b/contracts/hub-tf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub-tf" -version = "3.0.1" +version = "3.0.2" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" @@ -13,7 +13,8 @@ crate-type = ["cdylib", "rlib"] backtraces = ["cosmwasm-std/backtraces"] [dependencies] -cosmwasm-std = { version = "1.2.1", features=["iterator","stargate","cosmwasm_1_1","staking"]} +#cosmwasm-std = { version = "1.2.1", features=["iterator","stargate","cosmwasm_1_1","staking"]} +cosmwasm-std = { version = "1.2.1", features=["iterator","stargate","staking"]} cw2= "1.0.1" #cw20 = "1.0.0" @@ -28,6 +29,6 @@ pfc-steak = { path = "../../packages/steak" } pfc-dust-collector = { path = "../../packages/dust_collector" } serde = { version = "1.0.103", default-features = false, features = ["derive"] } pfc-fee-split = "0.2.3" -kujira = "0.7.25" +#kujira = "0.7.25" osmosis-std-derive="0.13.2" protobuf = { version = "3.1.0", features = ["with-bytes"] } \ No newline at end of file diff --git a/contracts/hub-tf/src/execute.rs b/contracts/hub-tf/src/execute.rs index 49f7337..1156cf8 100644 --- a/contracts/hub-tf/src/execute.rs +++ b/contracts/hub-tf/src/execute.rs @@ -3,8 +3,6 @@ use std::iter::FromIterator; use std::str::FromStr; use cosmwasm_std::{Addr, BankMsg, Coin, CosmosMsg, Decimal, DepsMut, DistributionMsg, Env, Event, Order, Response, StdError, StdResult, SubMsg, to_binary, Uint128, WasmMsg}; -use kujira::denom::Denom; -use kujira::msg::DenomMsg; use pfc_steak::DecimalCheckedOps; use pfc_steak::hub::{ @@ -20,7 +18,8 @@ use crate::math::{ }; use crate::state::{previous_batches, State, unbond_requests, VALIDATORS, VALIDATORS_ACTIVE}; use crate::token_factory; -use crate::token_factory::denom::{MsgBurn, MsgCreateDenom, MsgMint}; +use crate::kujira; +//use crate::token_factory::denom::{MsgBurn, MsgCreateDenom, MsgMint}; use crate::types::{Coins, Delegation}; //-------------------------------------------------------------------------------------------------- @@ -81,20 +80,18 @@ pub fn instantiate(deps: DepsMut, env: Env, msg: InstantiateMsg) -> StdResult>::into( + kujira::denom::MsgCreateDenom { sender: env.contract.address.to_string(), subdenom: steak_denom_msg }) + } else { - let c = >::into( - MsgCreateDenom { sender: env.contract.address.to_string(), subdenom: steak_denom_msg }); + >::into( + token_factory::denom::MsgCreateDenom { sender: env.contract.address.to_string(), subdenom: steak_denom_msg }) - Ok(Response::new().add_message(c)) - } + + }; + Ok(Response::new().add_message(c)) } @@ -167,15 +164,16 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec, bond_msg: ); let mint_msg = if kuji_version { - let _k = DenomMsg::Mint { - denom: Denom::from(steak_denom), - amount: usteak_to_mint, - recipient: env.contract.address, - }; - // CosmosMsg::from(k); - todo!() - } else { - >::into(MsgMint { + >::into(kujira::denom::MsgMint { + sender: env.contract.address.to_string(), + recipient: env.contract.address.to_string(), + amount: Some(kujira::denom::Coin { + denom: steak_denom.clone(), + amount: usteak_to_mint.to_string(), + }), + }) + } else { + >::into(token_factory::denom::MsgMint { sender: env.contract.address.to_string(), amount: Some(token_factory::denom::Coin { denom: steak_denom.clone(), @@ -556,13 +554,15 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { let burn_msg = if kuji_version { - let _foo = DenomMsg::Burn { - denom: Denom::from(steak_denom), - amount: pending_batch.usteak_to_burn, - }; - todo!() + >::into(kujira::denom::MsgBurn { + sender: env.contract.address.to_string(), + amount: Some(kujira::denom::Coin { + denom: steak_denom, + amount: pending_batch.usteak_to_burn.to_string(), + }), + }) } else { - >::into(MsgBurn { + >::into(token_factory::denom::MsgBurn { sender: env.contract.address.to_string(), amount: Some(token_factory::denom::Coin { denom: steak_denom, diff --git a/contracts/hub-tf/src/kujira/denom.rs b/contracts/hub-tf/src/kujira/denom.rs new file mode 100644 index 0000000..231565c --- /dev/null +++ b/contracts/hub-tf/src/kujira/denom.rs @@ -0,0 +1,144 @@ +// source: https://github.com/White-Whale-Defi-Platform/white-whale-core/blob/feat/tokenfactory-lp/packages/pool-network/src/denom.rs + +use std::convert::TryFrom; +use std::convert::TryInto; + +use osmosis_std_derive::CosmwasmExt; + +// see https://github.com/notional-labs/wasmd/blob/v0.30.0-sdk469.4/proto/cosmwasm/tokenfactory/v1beta1/tx.proto + +/// Coin defines a token with a denomination and an amount. +/// +/// NOTE: The amount field is an Int which implements the custom method +/// signatures required by gogoproto. +#[derive( +Clone, +PartialEq, +Eq, +::prost::Message, +serde::Serialize, +serde::Deserialize, +schemars::JsonSchema, +CosmwasmExt, +)] +#[proto_message(type_url = "/cosmos.base.v1beta1.Coin")] +pub struct Coin { + #[prost(string, tag = "1")] + pub denom: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub amount: ::prost::alloc::string::String, +} + +/// MsgCreateDenom defines the message structure for the CreateDenom gRPC service +/// method. It allows an account to create a new denom. It requires a sender +/// address and a sub denomination. The (sender_address, sub_denomination) tuple +/// must be unique and cannot be re-used. +/// +/// The resulting denom created is defined as +/// . The resulting denom's admin is +/// originally set to be the creator, but this can be changed later. The token +/// denom does not indicate the current admin. +#[derive( +Clone, +PartialEq, +Eq, +::prost::Message, +serde::Serialize, +serde::Deserialize, +schemars::JsonSchema, +CosmwasmExt, +)] +#[proto_message(type_url = "/kujira.denom.MsgCreateDenom")] +pub struct MsgCreateDenom { + #[prost(string, tag = "1")] + pub sender: ::prost::alloc::string::String, + /// subdenom can be up to 44 "alphanumeric" characters long. + #[prost(string, tag = "2")] + pub subdenom: ::prost::alloc::string::String, +} + +/// MsgCreateDenomResponse is the return value of MsgCreateDenom +/// It returns the full string of the newly created denom +#[derive( +Clone, +PartialEq, +Eq, +::prost::Message, +serde::Serialize, +serde::Deserialize, +schemars::JsonSchema, +CosmwasmExt, +)] +#[proto_message(type_url = "/kujira.denom.MsgCreateDenomResponse")] +pub struct MsgCreateDenomResponse { + #[prost(string, tag = "1")] + pub new_token_denom: ::prost::alloc::string::String, +} + +/// MsgMint is the sdk.Msg type for allowing an admin account to mint +/// more of a token. For now, we only support minting to the sender account +#[derive( +Clone, +PartialEq, +Eq, +::prost::Message, +serde::Serialize, +serde::Deserialize, +schemars::JsonSchema, +CosmwasmExt, +)] +#[proto_message(type_url = "/kujira.denom.MsgMint")] +pub struct MsgMint { + #[prost(string, tag = "1")] + pub sender: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub amount: ::core::option::Option, + #[prost(string, tag = "3")] + pub recipient: ::prost::alloc::string::String, +} + +#[derive( +Clone, +PartialEq, +Eq, +::prost::Message, +serde::Serialize, +serde::Deserialize, +schemars::JsonSchema, +CosmwasmExt, +)] +#[proto_message(type_url = "/kujira.denom.MsgMintResponse")] +pub struct MsgMintResponse {} + +/// MsgBurn is the sdk.Msg type for allowing an admin account to burn +/// a token. For now, we only support burning from the sender account. +#[derive( +Clone, +PartialEq, +Eq, +::prost::Message, +serde::Serialize, +serde::Deserialize, +schemars::JsonSchema, +CosmwasmExt, +)] +#[proto_message(type_url = "/kujira.denom.MsgBurn")] +pub struct MsgBurn { + #[prost(string, tag = "1")] + pub sender: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub amount: ::core::option::Option, +} + +#[derive( +Clone, +PartialEq, +Eq, +::prost::Message, +serde::Serialize, +serde::Deserialize, +schemars::JsonSchema, +CosmwasmExt, +)] +#[proto_message(type_url = "/kujira.denom.MsgBurnResponse")] +pub struct MsgBurnResponse {} \ No newline at end of file diff --git a/contracts/hub-tf/src/kujira/mod.rs b/contracts/hub-tf/src/kujira/mod.rs new file mode 100644 index 0000000..396cc23 --- /dev/null +++ b/contracts/hub-tf/src/kujira/mod.rs @@ -0,0 +1,2 @@ +// +pub mod denom; \ No newline at end of file diff --git a/contracts/hub-tf/src/lib.rs b/contracts/hub-tf/src/lib.rs index 72df73e..4e9b4b2 100644 --- a/contracts/hub-tf/src/lib.rs +++ b/contracts/hub-tf/src/lib.rs @@ -8,6 +8,7 @@ pub mod queries; pub mod state; pub mod types; pub mod token_factory; +pub mod kujira; #[cfg(test)] mod testing; mod migrations; diff --git a/init/harpoon_hub-tf_init.json b/init/harpoon_hub-tf_init.json new file mode 100644 index 0000000..1c416e9 --- /dev/null +++ b/init/harpoon_hub-tf_init.json @@ -0,0 +1,17 @@ +{ + "owner": "kujira16q6p6nkzc0525qnnrdd93urxv6pr8zhcmpcu7g", + "epoch_period": 259200, + "unbond_period": 1209600, + "validators": [ + "kujiravaloper1pshqems6hdka48gc56r2ykshyaarkt40hl0rlh", + "kujiravaloper16q6p6nkzc0525qnnrdd93urxv6pr8zhcu5t0z8", + "kujiravaloper1e9rm4nszmfg3fdhrw6s9j69stqddk7ga2x84yf" + ], + "denom": "ukuji", + "steak_denom": "boneKuji", + "fee_account": "kujira16q6p6nkzc0525qnnrdd93urxv6pr8zhcmpcu7g", + "fee_amount": "0.10", + "max_fee_amount": "0.10", + "fee_account_type": "Wallet", + "kuji_token_factory": true +} \ No newline at end of file From 857e98b47859a75bf4ede0838151c8e77031ba6f Mon Sep 17 00:00:00 2001 From: The PFC <81114960+PFC-developer@users.noreply.github.com> Date: Fri, 3 Mar 2023 12:04:23 -0600 Subject: [PATCH 35/77] fix: add exec_msg to main hub --- Cargo.lock | 4 +-- contracts/hub/Cargo.toml | 2 +- contracts/hub/src/contract.rs | 58 ++++++++++++++++-------------- contracts/hub/src/execute.rs | 32 +++++++++++------ contracts/hub/src/testing/tests.rs | 3 +- packages/steak/Cargo.toml | 2 +- packages/steak/src/hub.rs | 2 +- 7 files changed, 60 insertions(+), 43 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6889e87..279429a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -708,7 +708,7 @@ dependencies = [ [[package]] name = "pfc-steak" -version = "2.2.2" +version = "2.2.3" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -720,7 +720,7 @@ dependencies = [ [[package]] name = "pfc-steak-hub" -version = "3.0.1" +version = "3.0.2" dependencies = [ "cosmwasm-std", "cw-storage-plus 0.13.4", diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index 52c8b42..a81ef99 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub" -version = "3.0.1" +version = "3.0.2" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub/src/contract.rs b/contracts/hub/src/contract.rs index 03e8656..5f31196 100644 --- a/contracts/hub/src/contract.rs +++ b/contracts/hub/src/contract.rs @@ -1,16 +1,16 @@ use cosmwasm_std::{ - entry_point, from_binary, to_binary, Binary, Decimal, Deps, DepsMut, Env, MessageInfo, Reply, - Response, StdError, StdResult, + Binary, Decimal, Deps, DepsMut, entry_point, Env, from_binary, MessageInfo, Reply, Response, + StdError, StdResult, to_binary, }; +use cw2::{ContractVersion, get_contract_version, set_contract_version}; use cw20::Cw20ReceiveMsg; use pfc_steak::hub::{CallbackMsg, ExecuteMsg, FeeType, InstantiateMsg, MigrateMsg, QueryMsg, ReceiveMsg}; +use crate::{execute, queries}; use crate::helpers::{get_denom_balance, unwrap_reply}; use crate::migrations::ConfigV100; use crate::state::State; -use crate::{execute, queries}; -use cw2::{get_contract_version, set_contract_version, ContractVersion}; /// Contract name that is used for migration. pub const CONTRACT_NAME: &str = "steak-hub"; @@ -35,7 +35,7 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S let api = deps.api; match msg { ExecuteMsg::Receive(cw20_msg) => receive(deps, env, info, cw20_msg), - ExecuteMsg::Bond { receiver } => execute::bond( + ExecuteMsg::Bond { receiver, exec_msg } => execute::bond( deps, env, receiver @@ -43,6 +43,7 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S .transpose()? .unwrap_or(info.sender), info.funds, + exec_msg, ), ExecuteMsg::WithdrawUnbonded { receiver } => execute::withdraw_unbonded( deps, @@ -74,7 +75,7 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S ExecuteMsg::Rebalance { minimum } => execute::rebalance(deps, env, minimum), ExecuteMsg::Reconcile {} => execute::reconcile(deps, env), ExecuteMsg::SubmitBatch {} => execute::submit_batch(deps, env), - ExecuteMsg::TransferFeeAccount { fee_account_type,new_fee_account } => { + ExecuteMsg::TransferFeeAccount { fee_account_type, new_fee_account } => { execute::transfer_fee_account(deps, info.sender, fee_account_type, new_fee_account) } ExecuteMsg::UpdateFee { new_fee } => execute::update_fee(deps, info.sender, new_fee), @@ -85,10 +86,10 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S ExecuteMsg::UnPauseValidator { validator } => { execute::unpause_validator(deps, env, info.sender, validator) } - ExecuteMsg::SetUnbondPeriod { unbond_period } => execute::set_unbond_period(deps, env, info.sender, unbond_period), - ExecuteMsg::SetDustCollector { dust_collector } => {execute::set_dust_collector(deps,env,info.sender,dust_collector)} - ExecuteMsg::CollectDust { } => { execute::collect_dust(deps,env)} - ExecuteMsg::ReturnDenom { } => {execute::return_denom(deps,env,info.funds)} + ExecuteMsg::SetUnbondPeriod { unbond_period } => execute::set_unbond_period(deps, env, info.sender, unbond_period), + ExecuteMsg::SetDustCollector { dust_collector } => { execute::set_dust_collector(deps, env, info.sender, dust_collector) } + ExecuteMsg::CollectDust {} => { execute::collect_dust(deps, env) } + ExecuteMsg::ReturnDenom {} => { execute::return_denom(deps, env, info.funds) } } } @@ -205,21 +206,21 @@ pub fn migrate(deps: DepsMut, env: Env, _msg: MigrateMsg) -> StdResult state.fee_account.save(deps.storage, &owner)?; state.max_fee_rate.save(deps.storage, &Decimal::zero())?; state.fee_rate.save(deps.storage, &Decimal::zero())?; - state.fee_account_type.save(deps.storage,&FeeType::Wallet)?; - ConfigV100::upgrade_stores(deps.storage,&deps.querier, env.contract.address)?; - state.dust_collector.save(deps.storage,&None)?; + state.fee_account_type.save(deps.storage, &FeeType::Wallet)?; + ConfigV100::upgrade_stores(deps.storage, &deps.querier, env.contract.address)?; + state.dust_collector.save(deps.storage, &None)?; } "2.1.4" => { let state = State::default(); - ConfigV100::upgrade_stores(deps.storage, &deps.querier,env.contract.address)?; - state.fee_account_type.save(deps.storage,&FeeType::Wallet)?; - state.dust_collector.save(deps.storage,&None)?; + ConfigV100::upgrade_stores(deps.storage, &deps.querier, env.contract.address)?; + state.fee_account_type.save(deps.storage, &FeeType::Wallet)?; + state.dust_collector.save(deps.storage, &None)?; } "2.1.5" => { - ConfigV100::upgrade_stores(deps.storage, &deps.querier,env.contract.address)?; + ConfigV100::upgrade_stores(deps.storage, &deps.querier, env.contract.address)?; let state = State::default(); - state.fee_account_type.save(deps.storage,&FeeType::Wallet)?; - state.dust_collector.save(deps.storage,&None)?; + state.fee_account_type.save(deps.storage, &FeeType::Wallet)?; + state.dust_collector.save(deps.storage, &None)?; } "2.1.6" | "2.1.7" => { let state = State::default(); @@ -230,21 +231,24 @@ pub fn migrate(deps: DepsMut, env: Env, _msg: MigrateMsg) -> StdResult &get_denom_balance(&deps.querier, env.contract.address, denom)?, )?; - state.fee_account_type.save(deps.storage,&FeeType::Wallet)?; - state.dust_collector.save(deps.storage,&None)?; - - }, - "2.1.8" => { + state.fee_account_type.save(deps.storage, &FeeType::Wallet)?; + state.dust_collector.save(deps.storage, &None)?; + } + "2.1.8" |"2.1.16"=> { + let state = State::default(); + state.fee_account_type.save(deps.storage, &FeeType::Wallet)?; + state.dust_collector.save(deps.storage, &None)?; + } + "3.0.1" => { let state = State::default(); - state.fee_account_type.save(deps.storage,&FeeType::Wallet)?; - state.dust_collector.save(deps.storage,&None)?; + state.dust_collector.save(deps.storage, &None)?; } _ => {} }, _ => { return Err(StdError::generic_err( "contract name is not the same. aborting {}", - )) + )); } } /* diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index df4f6be..7574807 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -128,7 +128,7 @@ pub fn register_steak_token(deps: DepsMut, response: SubMsgResponse) -> StdResul /// smallest amount of delegation. If delegations become severely unbalance as a result of this /// (e.g. when a single user makes a very big deposit), anyone can invoke `ExecuteMsg::Rebalance` /// to balance the delegations. -pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec) -> StdResult { +pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec,bond_msg: Option) -> StdResult { let state = State::default(); let denom = state.denom.load(deps.storage)?; let amount_to_bond = parse_received_fund(&funds, &denom)?; @@ -188,15 +188,27 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec) -> StdRes let send_transfer_msg: CosmosMsg = match contract_info { Ok(_) => { - CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: steak_token.to_string(), - msg: to_binary(&Cw20ExecuteMsg::Send { - contract: receiver.to_string(), - amount: usteak_to_mint, - msg: Default::default(), - })?, - funds: vec![], - }) + if let Some(exec_msg) = bond_msg { + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: steak_token.to_string(), + msg: to_binary(&Cw20ExecuteMsg::Send { + contract: receiver.to_string(), + amount: usteak_to_mint, + msg: to_binary(&exec_msg)?, + })?, + funds: vec![], + }) + } else { + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: steak_token.to_string(), + msg: to_binary(&Cw20ExecuteMsg::Send { + contract: receiver.to_string(), + amount: usteak_to_mint, + msg: Default::default(), + })?, + funds: vec![], + }) + } } Err(_) => { CosmosMsg::Wasm(WasmMsg::Execute { diff --git a/contracts/hub/src/testing/tests.rs b/contracts/hub/src/testing/tests.rs index be11646..2dd7b41 100644 --- a/contracts/hub/src/testing/tests.rs +++ b/contracts/hub/src/testing/tests.rs @@ -280,7 +280,7 @@ fn bonding() { deps.as_mut(), mock_env(), mock_info("user_1", &[Coin::new(1_000_000, "uxyz")]), - ExecuteMsg::Bond { receiver: None }, + ExecuteMsg::Bond { receiver: None,exec_msg:None }, ) .unwrap(); @@ -346,6 +346,7 @@ fn bonding() { mock_info("user_2", &[Coin::new(12345, "uxyz")]), ExecuteMsg::Bond { receiver: Some("user_3".to_string()), + exec_msg:None }, ) .unwrap(); diff --git a/packages/steak/Cargo.toml b/packages/steak/Cargo.toml index 2cf2afc..45387bf 100644 --- a/packages/steak/Cargo.toml +++ b/packages/steak/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak" -version = "2.2.2" +version = "2.2.3" authors = ["larry ", "PFC "] edition = "2018" description = "Liquid steaking protocol for the cosmos" diff --git a/packages/steak/src/hub.rs b/packages/steak/src/hub.rs index 2a034f5..ec70fc6 100644 --- a/packages/steak/src/hub.rs +++ b/packages/steak/src/hub.rs @@ -47,7 +47,7 @@ pub enum ExecuteMsg { /// Implements the Cw20 receiver interface Receive(Cw20ReceiveMsg), /// Bond specified amount of Luna - Bond { receiver: Option }, + Bond { receiver: Option, exec_msg: Option }, /// Withdraw Luna that have finished unbonding in previous batches WithdrawUnbonded { receiver: Option }, /// Withdraw Luna that has finished unbonding in previous batches, for given address From f791e1e3a3f32422c5c23d9737558f1214fe7306 Mon Sep 17 00:00:00 2001 From: The PFC <81114960+PFC-developer@users.noreply.github.com> Date: Thu, 9 Mar 2023 18:21:03 -0600 Subject: [PATCH 36/77] chore: more init messages --- init/chihuahua_hub_init.json | 25 +++++++++++++++++++++++++ init/juno_hub_init.json | 28 ++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 init/chihuahua_hub_init.json create mode 100644 init/juno_hub_init.json diff --git a/init/chihuahua_hub_init.json b/init/chihuahua_hub_init.json new file mode 100644 index 0000000..fa50dd5 --- /dev/null +++ b/init/chihuahua_hub_init.json @@ -0,0 +1,25 @@ +{ + "cw20_code_id": 275, + "owner": "chihuahua1lu92zj8q6cmrptu09rp3343x9969r9qr7qmaf8", + "name": "bHUAHUA Token", + "symbol": "bHUAHUA", + "decimals": 6, + "epoch_period": 259200, + "unbond_period": 1814400, + "validators": [ + "chihuahuavaloper15tnycxe9csn7mkul4vvlyxlkd9jyqlw4q80nmy", + "chihuahuavaloper1670dvuv348eynr9lsmdrhqu3g7vpmzx96h4l2d", + "chihuahuavaloper13x4kf2zcdtegl8tytg0j0tclgv9atvt443k8hg" + ], + "denom": "uhuahua", + "fee_account": "chihuahua1dhlrnqrpafcynx2w3mmveasv7rcmjhx0gsr7k740yx8x4q833vcq7tzlr4", + "fee_amount": "0.10", + "max_fee_amount": "0.10", + "fee_account_type": "FeeSplit", + "label": "boneHUAHUA", + "marketing": { + "project": "https://backbonelabs.io", + "description": "The GraveDigger is the Liquid Staking Product of BackBone Labs. It's liquid staking derivative (LSD) is bHUAHUA.", + "marketing": "chihuahua1lu92zj8q6cmrptu09rp3343x9969r9qr7qmaf8" + } +} \ No newline at end of file diff --git a/init/juno_hub_init.json b/init/juno_hub_init.json new file mode 100644 index 0000000..8a123ac --- /dev/null +++ b/init/juno_hub_init.json @@ -0,0 +1,28 @@ +{ + "cw20_code_id": 2241, + "owner": "juno1lu92zj8q6cmrptu09rp3343x9969r9qrt84g0e", + "name": "bJuno Token", + "symbol": "bJuno", + "decimals": 6, + "epoch_period": 345600, + "unbond_period": 2419200, + "validators": [ + "junovaloper1yy3tnegzmkdcm7czzcy3flw5z0zyr9vkkxrfse", + "junovaloper1083svrca4t350mphfv9x45wq9asrs60cpqzg0y", + "junovaloper1wd02ktcvpananlvd9u6jm3x3ap3vmw59jv9vez", + "junovaloper1dfeenydr9f2vggp8m7jpalvy8jv7fdndncedrm", + "junovaloper1wj4jhrll90knap3vt8z4qs5rrm9cla2kpc5944", + "junovaloper10wxn2lv29yqnw2uf4jf439kwy5ef00qdelfp7r" + ], + "denom": "ujuno", + "fee_account": "juno167yrnjppr034exsj940q39kfc3nmdg36jtfv63tymyzlyhxg4a3snesqhv", + "fee_amount": "0.10", + "max_fee_amount": "0.10", + "fee_account_type": "FeeSplit", + "label": "bJuno", + "marketing": { + "project": "https://backbonelabs.io", + "description": "The GraveDigger is the Liquid Staking Product of BackBone Labs. It's liquid staking derivative (LSD) is bJuno.", + "marketing": "juno1lu92zj8q6cmrptu09rp3343x9969r9qrt84g0e" + } +} \ No newline at end of file From 841d3f580f6228713726f27e9bfd3a853d9e1926 Mon Sep 17 00:00:00 2001 From: The PFC <81114960+PFC-developer@users.noreply.github.com> Date: Wed, 22 Mar 2023 11:37:47 -0500 Subject: [PATCH 37/77] feat: INJ/v3.0.3 --- Cargo.lock | 6 +- contracts/hub-tf/Cargo.toml | 2 +- contracts/hub-tf/src/contract.rs | 16 ++- contracts/hub-tf/src/execute.rs | 128 ++++++++++++--------- contracts/hub-tf/src/injective/denom.rs | 142 ++++++++++++++++++++++++ contracts/hub-tf/src/injective/mod.rs | 2 + contracts/hub-tf/src/lib.rs | 1 + contracts/hub-tf/src/queries.rs | 3 +- contracts/hub-tf/src/state.rs | 6 + contracts/hub-tf/src/testing/tests.rs | 16 ++- contracts/hub/Cargo.toml | 2 +- contracts/hub/src/queries.rs | 3 +- contracts/hub/src/testing/tests.rs | 6 +- init/injective_hub-tf_init.json | 17 +++ packages/steak/Cargo.toml | 2 +- packages/steak/src/hub.rs | 1 + packages/steak/src/hub_tf.rs | 36 +++++- 17 files changed, 317 insertions(+), 72 deletions(-) create mode 100644 contracts/hub-tf/src/injective/denom.rs create mode 100644 contracts/hub-tf/src/injective/mod.rs create mode 100644 init/injective_hub-tf_init.json diff --git a/Cargo.lock b/Cargo.lock index 279429a..6649969 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -708,7 +708,7 @@ dependencies = [ [[package]] name = "pfc-steak" -version = "2.2.3" +version = "2.2.4" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -720,7 +720,7 @@ dependencies = [ [[package]] name = "pfc-steak-hub" -version = "3.0.2" +version = "3.0.3" dependencies = [ "cosmwasm-std", "cw-storage-plus 0.13.4", @@ -735,7 +735,7 @@ dependencies = [ [[package]] name = "pfc-steak-hub-tf" -version = "3.0.2" +version = "3.0.3" dependencies = [ "cosmwasm-std", "cw-item-set", diff --git a/contracts/hub-tf/Cargo.toml b/contracts/hub-tf/Cargo.toml index cbe4d96..748ae68 100644 --- a/contracts/hub-tf/Cargo.toml +++ b/contracts/hub-tf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub-tf" -version = "3.0.2" +version = "3.0.3" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub-tf/src/contract.rs b/contracts/hub-tf/src/contract.rs index 582c462..64841eb 100644 --- a/contracts/hub-tf/src/contract.rs +++ b/contracts/hub-tf/src/contract.rs @@ -2,9 +2,10 @@ use cosmwasm_std::{Binary, Deps, DepsMut, entry_point, Env, MessageInfo, Reply, use cw2::{ContractVersion, get_contract_version, set_contract_version}; use pfc_steak::hub::{CallbackMsg, MigrateMsg, QueryMsg}; -use pfc_steak::hub_tf::{ExecuteMsg, InstantiateMsg}; +use pfc_steak::hub_tf::{ExecuteMsg, InstantiateMsg, TokenFactoryType}; use crate::{execute, queries}; +use crate::state::State; //use crate::helpers::{ unwrap_reply}; @@ -180,8 +181,17 @@ pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult match contract_version.version.as_ref() { #[allow(clippy::single_match)] "0" => {} - - _ => {} + "3.0.1" | "3.0.2" => { + let state = State::default(); + let kuji = state.kuji_token_factory.load(deps.storage)?; + if kuji { + state.token_factory_type.save(deps.storage,&TokenFactoryType::Kujira)? + } else { + state.token_factory_type.save(deps.storage,&TokenFactoryType::CosmWasm)? + } + + } + _ => {} }, _ => { return Err(StdError::generic_err( diff --git a/contracts/hub-tf/src/execute.rs b/contracts/hub-tf/src/execute.rs index 1156cf8..dbb1f56 100644 --- a/contracts/hub-tf/src/execute.rs +++ b/contracts/hub-tf/src/execute.rs @@ -8,17 +8,17 @@ use pfc_steak::DecimalCheckedOps; use pfc_steak::hub::{ Batch, CallbackMsg, FeeType, PendingBatch, UnbondRequest, }; -use pfc_steak::hub_tf::{ExecuteMsg, InstantiateMsg}; +use pfc_steak::hub_tf::{ExecuteMsg, InstantiateMsg, TokenFactoryType}; +use crate::{injective, token_factory}; use crate::contract::REPLY_REGISTER_RECEIVED_COINS; use crate::helpers::{get_denom_balance, parse_received_fund, query_all_delegations, query_delegation, query_delegations}; +use crate::kujira; use crate::math::{ compute_mint_amount, compute_redelegations_for_rebalancing, compute_redelegations_for_removal, compute_unbond_amount, compute_undelegations, reconcile_batches, }; use crate::state::{previous_batches, State, unbond_requests, VALIDATORS, VALIDATORS_ACTIVE}; -use crate::token_factory; -use crate::kujira; //use crate::token_factory::denom::{MsgBurn, MsgCreateDenom, MsgMint}; use crate::types::{Coins, Delegation}; @@ -69,7 +69,11 @@ pub fn instantiate(deps: DepsMut, env: Env, msg: InstantiateMsg) -> StdResult StdResult>::into( - kujira::denom::MsgCreateDenom { sender: env.contract.address.to_string(), subdenom: steak_denom_msg }) - - } else { - >::into( - token_factory::denom::MsgCreateDenom { sender: env.contract.address.to_string(), subdenom: steak_denom_msg }) - - + let c = match state.token_factory_type.load(deps.storage)? { + TokenFactoryType::CosmWasm => { + >::into( + token_factory::denom::MsgCreateDenom { sender: env.contract.address.to_string(), subdenom: steak_denom_msg }) + } + TokenFactoryType::Kujira => { + >::into( + kujira::denom::MsgCreateDenom { sender: env.contract.address.to_string(), subdenom: steak_denom_msg }) + } + TokenFactoryType::Injective => { + >::into( + injective::denom::MsgCreateDenom { sender: env.contract.address.to_string(), subdenom: steak_denom_msg }) + } }; + Ok(Response::new().add_message(c)) } @@ -113,7 +121,7 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec, bond_msg: let amount_to_bond = parse_received_fund(&funds, &denom)?; let steak_minted = state.steak_minted.load(deps.storage)?; let steak_denom = state.steak_denom.load(deps.storage)?; - let kuji_version = state.kuji_token_factory.load(deps.storage)?; + let token_factory_type = state.token_factory_type.load(deps.storage)?; let mut validators: Vec = Default::default(); for v in VALIDATORS_ACTIVE.items(deps.storage, None, None, Order::Ascending) { validators.push(v?); @@ -163,26 +171,37 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec, bond_msg: REPLY_REGISTER_RECEIVED_COINS, ); - let mint_msg = if kuji_version { - >::into(kujira::denom::MsgMint { - sender: env.contract.address.to_string(), - recipient: env.contract.address.to_string(), - amount: Some(kujira::denom::Coin { - denom: steak_denom.clone(), - amount: usteak_to_mint.to_string(), - }), - }) - } else { - >::into(token_factory::denom::MsgMint { - sender: env.contract.address.to_string(), - amount: Some(token_factory::denom::Coin { - denom: steak_denom.clone(), - amount: usteak_to_mint.to_string(), - }), - }) + let mint_msg = match token_factory_type { + TokenFactoryType::CosmWasm => { + >::into(token_factory::denom::MsgMint { + sender: env.contract.address.to_string(), + amount: Some(token_factory::denom::Coin { + denom: steak_denom.clone(), + amount: usteak_to_mint.to_string(), + }), + }) + } + TokenFactoryType::Kujira => { + >::into(kujira::denom::MsgMint { + sender: env.contract.address.to_string(), + recipient: env.contract.address.to_string(), + amount: Some(kujira::denom::Coin { + denom: steak_denom.clone(), + amount: usteak_to_mint.to_string(), + }), + }) + } + TokenFactoryType::Injective => { + >::into(injective::denom::MsgMint { + sender: env.contract.address.to_string(), + amount: Some(injective::denom::Coin { + denom: steak_denom.clone(), + amount: usteak_to_mint.to_string(), + }), + }) + } }; - let contract_info = deps.querier.query_wasm_contract_info(receiver.to_string()); // send the uSteak, optionally calling a smart contract @@ -475,7 +494,7 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { let state = State::default(); let denom = state.denom.load(deps.storage)?; let steak_denom = state.steak_denom.load(deps.storage)?; - let kuji_version = state.kuji_token_factory.load(deps.storage)?; + let token_factory_type = state.token_factory_type.load(deps.storage)?; let usteak_supply = state.steak_minted.load(deps.storage)?; let mut validators: Vec = Default::default(); for v in VALIDATORS.items(deps.storage, None, None, Order::Ascending) { @@ -553,22 +572,31 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { .collect::>(); - let burn_msg = if kuji_version { - >::into(kujira::denom::MsgBurn { - sender: env.contract.address.to_string(), - amount: Some(kujira::denom::Coin { - denom: steak_denom, - amount: pending_batch.usteak_to_burn.to_string(), + let burn_msg = match token_factory_type { + TokenFactoryType::Kujira => + >::into(kujira::denom::MsgBurn { + sender: env.contract.address.to_string(), + amount: Some(kujira::denom::Coin { + denom: steak_denom, + amount: pending_batch.usteak_to_burn.to_string(), + }), }), - }) - } else { - >::into(token_factory::denom::MsgBurn { - sender: env.contract.address.to_string(), - amount: Some(token_factory::denom::Coin { - denom: steak_denom, - amount: pending_batch.usteak_to_burn.to_string(), + TokenFactoryType::CosmWasm => + >::into(token_factory::denom::MsgBurn { + sender: env.contract.address.to_string(), + amount: Some(token_factory::denom::Coin { + denom: steak_denom, + amount: pending_batch.usteak_to_burn.to_string(), + }), }), - }) + TokenFactoryType::Injective => + >::into(injective::denom::MsgBurn { + sender: env.contract.address.to_string(), + amount: Some(injective::denom::Coin { + denom: steak_denom, + amount: pending_batch.usteak_to_burn.to_string(), + }), + }) }; // yes.. this will fail if supply is less than the amount to burn. this is intentional. state.steak_minted.save(deps.storage, &(usteak_supply - pending_batch.usteak_to_burn))?; @@ -1027,7 +1055,7 @@ pub fn set_dust_collector( }; let event = Event::new("steak/set_dust_collector") - .add_attribute("dust_collector", dust_collector.unwrap_or("-cleared-".into())); + .add_attribute("dust_collector", dust_collector.unwrap_or("-cleared-".into())); Ok(Response::new() .add_event(event) @@ -1050,6 +1078,6 @@ pub fn return_denom(deps: DepsMut, _env: Env, _funds: Vec) -> StdResult. The resulting denom's admin is +/// originally set to be the creator, but this can be changed later. The token +/// denom does not indicate the current admin. +#[derive( +Clone, +PartialEq, +Eq, +::prost::Message, +serde::Serialize, +serde::Deserialize, +schemars::JsonSchema, +CosmwasmExt, +)] +#[proto_message(type_url = "/injective.tokenfactory.v1beta1.MsgCreateDenom")] +pub struct MsgCreateDenom { + #[prost(string, tag = "1")] + pub sender: ::prost::alloc::string::String, + /// subdenom can be up to 44 "alphanumeric" characters long. + #[prost(string, tag = "2")] + pub subdenom: ::prost::alloc::string::String, +} + +/// MsgCreateDenomResponse is the return value of MsgCreateDenom +/// It returns the full string of the newly created denom +#[derive( +Clone, +PartialEq, +Eq, +::prost::Message, +serde::Serialize, +serde::Deserialize, +schemars::JsonSchema, +CosmwasmExt, +)] +#[proto_message(type_url = "/injective.tokenfactory.v1beta1.MsgCreateDenomResponse")] +pub struct MsgCreateDenomResponse { + #[prost(string, tag = "1")] + pub new_token_denom: ::prost::alloc::string::String, +} + +/// MsgMint is the sdk.Msg type for allowing an admin account to mint +/// more of a token. For now, we only support minting to the sender account +#[derive( +Clone, +PartialEq, +Eq, +::prost::Message, +serde::Serialize, +serde::Deserialize, +schemars::JsonSchema, +CosmwasmExt, +)] +#[proto_message(type_url = "/injective.tokenfactory.v1beta1.MsgMint")] +pub struct MsgMint { + #[prost(string, tag = "1")] + pub sender: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub amount: ::core::option::Option, +} + +#[derive( +Clone, +PartialEq, +Eq, +::prost::Message, +serde::Serialize, +serde::Deserialize, +schemars::JsonSchema, +CosmwasmExt, +)] +#[proto_message(type_url = "/injective.tokenfactory.v1beta1.MsgMintResponse")] +pub struct MsgMintResponse {} + +/// MsgBurn is the sdk.Msg type for allowing an admin account to burn +/// a token. For now, we only support burning from the sender account. +#[derive( +Clone, +PartialEq, +Eq, +::prost::Message, +serde::Serialize, +serde::Deserialize, +schemars::JsonSchema, +CosmwasmExt, +)] +#[proto_message(type_url = "/injective.tokenfactory.v1beta1.MsgBurn")] +pub struct MsgBurn { + #[prost(string, tag = "1")] + pub sender: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub amount: ::core::option::Option, +} + +#[derive( +Clone, +PartialEq, +Eq, +::prost::Message, +serde::Serialize, +serde::Deserialize, +schemars::JsonSchema, +CosmwasmExt, +)] +#[proto_message(type_url = "/injective.tokenfactory.v1beta1.MsgBurnResponse")] +pub struct MsgBurnResponse {} \ No newline at end of file diff --git a/contracts/hub-tf/src/injective/mod.rs b/contracts/hub-tf/src/injective/mod.rs new file mode 100644 index 0000000..396cc23 --- /dev/null +++ b/contracts/hub-tf/src/injective/mod.rs @@ -0,0 +1,2 @@ +// +pub mod denom; \ No newline at end of file diff --git a/contracts/hub-tf/src/lib.rs b/contracts/hub-tf/src/lib.rs index 4e9b4b2..2c1ea75 100644 --- a/contracts/hub-tf/src/lib.rs +++ b/contracts/hub-tf/src/lib.rs @@ -9,6 +9,7 @@ pub mod state; pub mod types; pub mod token_factory; pub mod kujira; +pub mod injective; #[cfg(test)] mod testing; mod migrations; diff --git a/contracts/hub-tf/src/queries.rs b/contracts/hub-tf/src/queries.rs index f544f57..0642e37 100644 --- a/contracts/hub-tf/src/queries.rs +++ b/contracts/hub-tf/src/queries.rs @@ -50,7 +50,8 @@ pub fn config(deps: Deps) -> StdResult { max_fee_rate: state.max_fee_rate.load(deps.storage)?, validators: validator_active_vec, paused_validators, - dust_collector: state.dust_collector.load(deps.storage)?.map( |a| a.to_string()) + dust_collector: state.dust_collector.load(deps.storage)?.map( |a| a.to_string()), + token_factory: Some(state.token_factory_type.load(deps.storage)?.to_string()) }) } diff --git a/contracts/hub-tf/src/state.rs b/contracts/hub-tf/src/state.rs index 44e451f..bfaa7ec 100644 --- a/contracts/hub-tf/src/state.rs +++ b/contracts/hub-tf/src/state.rs @@ -3,6 +3,7 @@ use cw_item_set::Set; use cw_storage_plus::{Index, IndexedMap, IndexList, Item, MultiIndex}; use pfc_steak::hub::{Batch, FeeType, PendingBatch, UnbondRequest}; +use pfc_steak::hub_tf::{ TokenFactoryType}; pub(crate) const BATCH_KEY_V101: &str = "previous_batches_101"; pub(crate) const BATCH_KEY_OWNER_V101: &str = "previous_batches_owner_101"; @@ -53,10 +54,14 @@ pub(crate) struct State<'a> { // pub validators_active: Item<'a, Vec>, /// coins in 'denom' held before reinvest was called. pub prev_denom: Item<'a, Uint128>, + /// Kuji version of Token-factory + /// @deprecated pub kuji_token_factory: Item<'a, bool>, /// Dust Collector contract pub dust_collector: Item<'a, Option>, + /// INJ version of Token-factory + pub token_factory_type: Item<'a, TokenFactoryType>, } @@ -84,6 +89,7 @@ impl Default for State<'static> { fee_account_type: Item::new("fee_account_type"), kuji_token_factory: Item::new("kuji_token_factory"), dust_collector: Item::new("dust_collector"), + token_factory_type: Item::new("token_factory_type") } } } diff --git a/contracts/hub-tf/src/testing/tests.rs b/contracts/hub-tf/src/testing/tests.rs index fb56dac..f838f88 100644 --- a/contracts/hub-tf/src/testing/tests.rs +++ b/contracts/hub-tf/src/testing/tests.rs @@ -50,7 +50,7 @@ fn setup_test() -> OwnedDeps { "bob".to_string(), "charlie".to_string(), ], - kuji_token_factory: false, + token_factory: "CosmWasm".to_string(), dust_collector: Some("dusty_1".to_string()) }, ) @@ -88,7 +88,7 @@ fn setup_test_fee_split() -> OwnedDeps { "charlie".to_string(), ], - kuji_token_factory: false, + token_factory: "CosmWasm".to_string(), dust_collector: Some("dusty_2".to_string()) }, ) @@ -130,7 +130,8 @@ fn proper_instantiation() { "charlie".to_string(), ], paused_validators: vec![], - dust_collector:Some("dusty_1".to_string()) + dust_collector:Some("dusty_1".to_string()), + token_factory: Some("CosmWasm".to_string()) } ); @@ -176,7 +177,8 @@ fn proper_instantiation() { "charlie".to_string(), ], paused_validators: vec![], - dust_collector: Some("dusty_2".to_string()) + dust_collector: Some("dusty_2".to_string()), + token_factory: Some("CosmWasm".to_string()) } ); } @@ -1392,7 +1394,8 @@ fn splitting_fees() { "charlie".to_string(), ], paused_validators: vec![], - dust_collector:Some("dusty_1".to_string()) + dust_collector:Some("dusty_1".to_string()), + token_factory: Some("CosmWasm".to_string()) } ); @@ -1427,7 +1430,8 @@ fn splitting_fees() { "charlie".to_string(), ], paused_validators: vec![], - dust_collector: Some("dusty_1".to_string()) + dust_collector: Some("dusty_1".to_string()), + token_factory: Some("CosmWasm".to_string()) } ); } diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index a81ef99..99fc6df 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub" -version = "3.0.2" +version = "3.0.3" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub/src/queries.rs b/contracts/hub/src/queries.rs index cb5e031..16d1e6b 100644 --- a/contracts/hub/src/queries.rs +++ b/contracts/hub/src/queries.rs @@ -42,7 +42,8 @@ pub fn config(deps: Deps) -> StdResult { max_fee_rate: state.max_fee_rate.load(deps.storage)?, validators: validator_active_vec, paused_validators, - dust_collector:None + dust_collector:None, + token_factory:None }) } diff --git a/contracts/hub/src/testing/tests.rs b/contracts/hub/src/testing/tests.rs index 2dd7b41..1c7181d 100644 --- a/contracts/hub/src/testing/tests.rs +++ b/contracts/hub/src/testing/tests.rs @@ -220,6 +220,7 @@ fn proper_instantiation() { ], paused_validators: vec![], dust_collector: None, + token_factory:None } ); @@ -266,6 +267,7 @@ fn proper_instantiation() { ], paused_validators: vec![], dust_collector: None, + token_factory:None } ); } @@ -1564,6 +1566,7 @@ fn splitting_fees() { ], paused_validators: vec![], dust_collector: None, + token_factory:None } ); @@ -1598,7 +1601,8 @@ fn splitting_fees() { "charlie".to_string(), ], paused_validators: vec![], - dust_collector:None + dust_collector:None, + token_factory:None } ); } diff --git a/init/injective_hub-tf_init.json b/init/injective_hub-tf_init.json new file mode 100644 index 0000000..2e80b32 --- /dev/null +++ b/init/injective_hub-tf_init.json @@ -0,0 +1,17 @@ +{ + "owner": "inj1d3yya2s4cejxfe5et9djq38qgtvlsmn9nqjeu9", + "epoch_period": 259200, + "unbond_period": 1814400, + "validators": [ + "injvaloper1t0q28eyvuvdp6aw8rdgxewgw29qlaskm5dhaf2", + "injvaloper156t3yxd4udv0h9gwagfcmwnmm3quy0nph7tyh5", + "injvaloper1kk523rsm9pey740cx4plalp40009ncs0wrchfe" + ], + "denom": "inj", + "steak_denom": "bINJ", + "fee_account": "inj1z7s6eqeql5r23gptjrfhvks3z79vwweuezk77g", + "fee_amount": "0.10", + "max_fee_amount": "0.10", + "fee_account_type": "FeeSplit", + "token_factory": "Injective" +} \ No newline at end of file diff --git a/packages/steak/Cargo.toml b/packages/steak/Cargo.toml index 45387bf..522e765 100644 --- a/packages/steak/Cargo.toml +++ b/packages/steak/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak" -version = "2.2.3" +version = "2.2.4" authors = ["larry ", "PFC "] edition = "2018" description = "Liquid steaking protocol for the cosmos" diff --git a/packages/steak/src/hub.rs b/packages/steak/src/hub.rs index ec70fc6..a9a0b6b 100644 --- a/packages/steak/src/hub.rs +++ b/packages/steak/src/hub.rs @@ -179,6 +179,7 @@ pub struct ConfigResponse { pub validators: Vec, pub paused_validators: Vec, pub dust_collector: Option, + pub token_factory: Option } #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, JsonSchema)] diff --git a/packages/steak/src/hub_tf.rs b/packages/steak/src/hub_tf.rs index daa35bc..1a63eac 100644 --- a/packages/steak/src/hub_tf.rs +++ b/packages/steak/src/hub_tf.rs @@ -1,16 +1,44 @@ +use std::str::FromStr; use cosmwasm_schema::cw_serde; // use cosmwasm_std::{Decimal, Uint128}; use crate::hub::CallbackMsg; +#[cw_serde] +pub enum TokenFactoryType { + CosmWasm =1, + Kujira =2, + Injective =3 +} +impl ToString for TokenFactoryType { + fn to_string(&self) -> String { + match &self { + TokenFactoryType::CosmWasm => String::from("CosmWasm"), + TokenFactoryType::Kujira => String::from("Kujira"), + TokenFactoryType::Injective => String::from("Injective"), + } + } +} +impl FromStr for TokenFactoryType { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "CosmWasm" => Ok(TokenFactoryType::CosmWasm), + "Kujira" => Ok(TokenFactoryType::Kujira), + "Injective" => Ok(TokenFactoryType::Injective), + _ => Err(()), + } + } +} + #[cw_serde] pub struct InstantiateMsg { /// Account who can call certain privileged functions pub owner: String, - /// Name of the liquid staking token + /// How often the un-bonding queue is to be executed, in seconds pub epoch_period: u64, - /// The staking module's unbonding time, in seconds + /// The staking module's un-bonding time, in seconds pub unbond_period: u64, /// Initial set of validators who will receive the delegations pub validators: Vec, @@ -26,8 +54,8 @@ pub struct InstantiateMsg { pub fee_amount: Decimal, /// Max Fee "1.00 = 100%" pub max_fee_amount: Decimal, - /// kuji_token_factory - uses token factory, but it's at a different module - pub kuji_token_factory: bool, + // different chains have different token factory implementations + pub token_factory: String, /// The Dust collector contract pub dust_collector: Option, } From 30ef7f00c635f9e4d94adc96aecce91e951b00a8 Mon Sep 17 00:00:00 2001 From: The PFC <81114960+PFC-developer@users.noreply.github.com> Date: Wed, 5 Apr 2023 08:32:05 -0500 Subject: [PATCH 38/77] prep for 3.0.3 --- Cargo.lock | 108 +++++++++++++++++++++++++++++------------------------ 1 file changed, 59 insertions(+), 49 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6649969..a3c3440 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "anyhow" -version = "1.0.69" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" [[package]] name = "base16ct" @@ -70,9 +70,9 @@ checksum = "722e23542a15cea1f65d4a1419c4cfd7a26706c70871a13a04238ca3f40f1661" [[package]] name = "cosmwasm-crypto" -version = "1.2.1" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fecd74d3a0041114110d1260f77fcb644c5d2403549b37096c44f0e643a5177" +checksum = "f22add0f9b2a5416df98c1d0248a8d8eedb882c38fbf0c5052b64eebe865df6d" dependencies = [ "digest 0.10.5", "ed25519-zebra", @@ -83,18 +83,18 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.1" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5abeeb891e6d0098402e4d3d042f90451db52651d2fe14b170e69a1dd3e4115" +checksum = "c2e64f710a18ef90d0a632cf27842e98ffc2d005a38a6f76c12fd0bc03bc1a2d" dependencies = [ - "syn", + "syn 1.0.109", ] [[package]] name = "cosmwasm-schema" -version = "1.2.1" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9118e36843df6648fd0a626c46438f87038f296ec750cef3832cafc483c483f9" +checksum = "fe5ad2e23a971b9e4cd57b20cee3e2e79c33799bed4b128e473aca3702bfe5dd" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -105,20 +105,20 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.1" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78d6fc9854ac14e46cb69b0f396547893f93d2847aef975950ebbe73342324f3" +checksum = "2926d159a9bb1a716a592b40280f1663f2491a9de3b6da77c0933cee2a2655b8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "cosmwasm-std" -version = "1.2.1" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5034c772c1369b160731aa00bb81f93733ab2884928edd8d588733d607ac5af4" +checksum = "76fee88ff5bf7bef55bd37ac0619974701b99bf6bd4b16cf56ee8810718abd71" dependencies = [ "base64", "cosmwasm-crypto", @@ -186,18 +186,18 @@ dependencies = [ [[package]] name = "cw-address-like" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b137a72f7ed4eaf591ec1bba7aedb062cde38ff08086276d8de6e37dc9b98621" +checksum = "451a4691083a88a3c0630a8a88799e9d4cd6679b7ce8ff22b8da2873ff31d380" dependencies = [ "cosmwasm-std", ] [[package]] name = "cw-item-set" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2fe22a9086c61c6b41ee46669c6f61ca4142111d38551ca156be4fa9d9fa39d" +checksum = "dea5a233bd67babedbe96a514178a64b0c597f1f38bc474fa8d63e3f26bdceb2" dependencies = [ "cosmwasm-std", "cw-storage-plus 1.0.1", @@ -205,9 +205,9 @@ dependencies = [ [[package]] name = "cw-ownable" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27fc0c75fc61cf268e5dc5f23ff24bd84b629c575b5a1522e2349f61b46874b4" +checksum = "093dfb4520c48b5848274dd88ea99e280a04bc08729603341c7fb0d758c74321" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -220,13 +220,13 @@ dependencies = [ [[package]] name = "cw-ownable-derive" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac447d5b5314ee18c2663b5436451c030c072db1eb392a167d19c04e247d77f6" +checksum = "a1d3bf2e0f341bb6cc100d7d441d31cf713fbd3ce0c511f91e79f14b40a889af" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -471,7 +471,7 @@ checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -681,7 +681,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -774,18 +774,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.51" +version = "1.0.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +checksum = "e472a104799c74b514a57226160104aa483546de37e839ec50e3c2e41dd87534" dependencies = [ "unicode-ident", ] [[package]] name = "prost" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21dc42e00223fc37204bd4aa177e69420c604ca4a183209a8f9de30c6d934698" +checksum = "e48e50df39172a3e7eb17e14642445da64996989bc212b583015435d39a58537" dependencies = [ "bytes", "prost-derive", @@ -793,24 +793,23 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda8c0881ea9f722eb9629376db3d0b903b462477c1aafcb0566610ac28ac5d" +checksum = "4ea9b0f8cbe5e15a8a042d030bd96668db28ecb567ec37d691971ff5731d2b1b" dependencies = [ "anyhow", "itertools", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "prost-types" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e0526209433e96d83d750dd81a99118edbc55739e7e61a46764fd2ad537788" +checksum = "379119666929a1afd7a043aa6cf96fa67a6dce9af60c88095a4686dbce4c9c88" dependencies = [ - "bytes", "prost", ] @@ -837,9 +836,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.23" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] @@ -900,7 +899,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn", + "syn 1.0.109", ] [[package]] @@ -949,7 +948,7 @@ checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -960,7 +959,7 @@ checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1032,9 +1031,20 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.107" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "21e3787bb71465627110e7d87ed4faaa36c1f61042ee67badb9e2ef173accc40" dependencies = [ "proc-macro2", "quote", @@ -1043,22 +1053,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.30" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.30" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.11", ] [[package]] @@ -1081,9 +1091,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "version_check" From 6fbc9f9190da6e179ba9f84494e3b7e929d38842 Mon Sep 17 00:00:00 2001 From: The PFC <81114960+PFC-developer@users.noreply.github.com> Date: Wed, 5 Apr 2023 08:33:52 -0500 Subject: [PATCH 39/77] build 3.0.3 --- build_x86.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_x86.sh b/build_x86.sh index bc7e848..e071f94 100755 --- a/build_x86.sh +++ b/build_x86.sh @@ -2,4 +2,4 @@ docker run --rm -v "$(pwd)":/code \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ - cosmwasm/workspace-optimizer:0.12.11 + cosmwasm/workspace-optimizer:0.12.13 From 0da0b0863a2b0e9754f336fea40a4f158a78557a Mon Sep 17 00:00:00 2001 From: The PFC <81114960+PFC-developer@users.noreply.github.com> Date: Wed, 10 May 2023 16:05:21 -0500 Subject: [PATCH 40/77] feat: v3.0.4 - special message to TRANSFER instead of SEND --- Cargo.lock | 1607 ++++++++++++++++++++++++++++-- build_arm.sh | 2 +- contracts/hub-tf/Cargo.toml | 2 +- contracts/hub-tf/src/contract.rs | 1 + contracts/hub-tf/src/execute.rs | 30 +- contracts/hub/Cargo.toml | 2 +- contracts/hub/src/contract.rs | 1 + contracts/hub/src/execute.rs | 31 +- 8 files changed, 1577 insertions(+), 99 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a3c3440..10b2b14 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,59 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli 0.27.2", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom 0.2.6", + "once_cell", + "version_check", +] + [[package]] name = "anyhow" version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object 0.30.3", + "rustc-demangle", +] + [[package]] name = "base16ct" version = "0.1.1" @@ -26,6 +73,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dea908e7347a8c64e378c17e30ef880ad73e3b4498346b055c2c00ea342f3179" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "block-buffer" version = "0.9.0" @@ -44,6 +97,34 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bumpalo" +version = "3.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" + +[[package]] +name = "bytecheck" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13fe11640a23eb24562225322cd3e452b93a3d4091d62fab69c70542fcd17d1f" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31225543cb46f81a7e224762764f4a6a0f097b1db0b175f69e8065efaa42de5" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "byteorder" version = "1.4.3" @@ -56,23 +137,48 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clru" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "591ff76ca0691bd91c1b0b5b987e5cf93b21ec810ad96665c5a569c60846dd93" + [[package]] name = "const-oid" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "722e23542a15cea1f65d4a1419c4cfd7a26706c70871a13a04238ca3f40f1661" +[[package]] +name = "corosensei" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9847f90f32a50b0dcbd68bc23ff242798b13080b97b0569f6ed96a45ce4cf2cd" +dependencies = [ + "autocfg", + "cfg-if", + "libc", + "scopeguard", + "windows-sys 0.33.0", +] + [[package]] name = "cosmwasm-crypto" -version = "1.2.3" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f22add0f9b2a5416df98c1d0248a8d8eedb882c38fbf0c5052b64eebe865df6d" +checksum = "b76d2207945b8aa3ce0735da53ab9a74f75fe3e7794754c216a9edfa04e1e627" dependencies = [ "digest 0.10.5", "ed25519-zebra", @@ -83,18 +189,18 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.3" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e64f710a18ef90d0a632cf27842e98ffc2d005a38a6f76c12fd0bc03bc1a2d" +checksum = "1dd07af7736164d2d8126dc67fdb33b1b5c54fb5a3190395c47f46d24fc6d592" dependencies = [ "syn 1.0.109", ] [[package]] name = "cosmwasm-schema" -version = "1.2.3" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe5ad2e23a971b9e4cd57b20cee3e2e79c33799bed4b128e473aca3702bfe5dd" +checksum = "3e9e92cdce475a91659d0dc4d17836a484fc534e80e787281aa4adde4cb1798b" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -105,9 +211,9 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.3" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2926d159a9bb1a716a592b40280f1663f2491a9de3b6da77c0933cee2a2655b8" +checksum = "69681bac3dbeb0b00990279e3ed39e3d4406b29f538b16b712f6771322a45048" dependencies = [ "proc-macro2", "quote", @@ -116,9 +222,9 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.3" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76fee88ff5bf7bef55bd37ac0619974701b99bf6bd4b16cf56ee8810718abd71" +checksum = "8d39f20967baeb94709123f7bba13a25ae2fa166bc5e7813f734914df3f8f6a1" dependencies = [ "base64", "cosmwasm-crypto", @@ -134,6 +240,40 @@ dependencies = [ "uint", ] +[[package]] +name = "cosmwasm-storage" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88f7944b5204c3a998f73248925a097ace887bdf01c2f70de00d8b92cd69e807" +dependencies = [ + "cosmwasm-std", + "serde", +] + +[[package]] +name = "cosmwasm-vm" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cb647b2f444c88b02da06794f27fc4270989fb50969c47998a493a79d77ccef" +dependencies = [ + "bitflags", + "bytecheck", + "clru", + "cosmwasm-crypto", + "cosmwasm-std", + "enumset", + "hex", + "loupe", + "parity-wasm", + "schemars", + "serde", + "serde_json", + "sha2 0.10.6", + "thiserror", + "wasmer", + "wasmer-middlewares", +] + [[package]] name = "cpufeatures" version = "0.2.2" @@ -143,6 +283,117 @@ dependencies = [ "libc", ] +[[package]] +name = "cranelift-bforest" +version = "0.82.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38faa2a16616c8e78a18d37b4726b98bfd2de192f2fdc8a39ddf568a408a0f75" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen" +version = "0.82.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26f192472a3ba23860afd07d2b0217dc628f21fcc72617aa1336d98e1671f33b" +dependencies = [ + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-entity", + "gimli 0.26.2", + "log", + "regalloc", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.82.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f32ddb89e9b89d3d9b36a5b7d7ea3261c98235a76ac95ba46826b8ec40b1a24" +dependencies = [ + "cranelift-codegen-shared", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.82.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01fd0d9f288cc1b42d9333b7a776b17e278fc888c28e6a0f09b5573d45a150bc" + +[[package]] +name = "cranelift-entity" +version = "0.82.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e3bfe172b83167604601faf9dc60453e0d0a93415b57a9c4d1a7ae6849185cf" + +[[package]] +name = "cranelift-frontend" +version = "0.82.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a006e3e32d80ce0e4ba7f1f9ddf66066d052a8c884a110b91d05404d6ce26dce" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset 0.8.0", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +dependencies = [ + "cfg-if", +] + [[package]] name = "crunchy" version = "0.2.2" @@ -453,6 +704,40 @@ dependencies = [ "thiserror", ] +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core", + "quote", + "syn 1.0.109", +] + [[package]] name = "der" version = "0.6.0" @@ -500,6 +785,32 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21e50f3adc76d6a43f5ed73b698a87d0760ca74617f60f7c3b879003536fdd28" +[[package]] +name = "dynasm" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add9a102807b524ec050363f09e06f1504214b0e1c7797f64261c891022dce8b" +dependencies = [ + "bitflags", + "byteorder", + "lazy_static", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "dynasmrt" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" +dependencies = [ + "byteorder", + "dynasm", + "memmap2", +] + [[package]] name = "ecdsa" version = "0.14.8" @@ -553,6 +864,83 @@ dependencies = [ "zeroize", ] +[[package]] +name = "enum-iterator" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eeac5c5edb79e4e39fe8439ef35207780a11f69c52cbe424ce3dfad4cb78de6" +dependencies = [ + "enum-iterator-derive", +] + +[[package]] +name = "enum-iterator-derive" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c134c37760b27a871ba422106eedbb8247da973a09e82558bf26d619c882b159" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "enumset" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19be8061a06ab6f3a6cf21106c873578bf01bd42ad15e0311a9c76161cb1c753" +dependencies = [ + "enumset_derive", +] + +[[package]] +name = "enumset_derive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03e7b551eba279bf0fa88b83a46330168c1560a52a94f5126f892f0b364ab3e0" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + [[package]] name = "ff" version = "0.12.0" @@ -563,6 +951,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "forward_ref" version = "1.0.0" @@ -601,6 +995,23 @@ dependencies = [ "wasi 0.10.2+wasi-snapshot-preview1", ] +[[package]] +name = "gimli" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +dependencies = [ + "fallible-iterator", + "indexmap", + "stable_deref_trait", +] + +[[package]] +name = "gimli" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" + [[package]] name = "group" version = "0.12.1" @@ -613,38 +1024,117 @@ dependencies = [ ] [[package]] -name = "hex" -version = "0.4.3" +name = "hashbrown" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] [[package]] -name = "hmac" -version = "0.12.1" +name = "hashbrown" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "digest 0.10.5", + "ahash", ] [[package]] -name = "itertools" -version = "0.10.5" +name = "hermit-abi" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" dependencies = [ - "either", + "libc", ] [[package]] -name = "itoa" -version = "1.0.1" +name = "hermit-abi" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" [[package]] -name = "k256" -version = "0.11.6" +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.5", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" dependencies = [ @@ -654,11 +1144,157 @@ dependencies = [ "sha2 0.10.6", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + [[package]] name = "libc" -version = "0.2.124" +version = "0.2.142" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "linux-raw-sys" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b64f40e5e03e0d54f03845c8197d0291253cdbedfb1cb46b13c2c117554a9f4c" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "loupe" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6a72dfa44fe15b5e76b94307eeb2ff995a8c5b283b55008940c02e0c5b634d" +dependencies = [ + "indexmap", + "loupe-derive", + "rustversion", +] + +[[package]] +name = "loupe-derive" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fbfc88337168279f2e9ae06e157cfed4efd3316e14dc96ed074d4f2e6c5952" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "more-asserts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "object" +version = "0.28.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" +dependencies = [ + "crc32fast", + "hashbrown 0.11.2", + "indexmap", + "memchr", +] + +[[package]] +name = "object" +version = "0.30.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a41fed9d98f27ab1c6d161da622a4fa35e8a54a8adc24bbf3ddd0ef70b0e50" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +dependencies = [ + "memchr", +] [[package]] name = "once_cell" @@ -684,6 +1320,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "parity-wasm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" + [[package]] name = "pfc-dust-collector" version = "3.0.0" @@ -706,6 +1348,30 @@ dependencies = [ "serde", ] +[[package]] +name = "pfc-ibc-reflect" +version = "0.0.1" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cosmwasm-storage", + "cosmwasm-vm", + "schemars", + "serde", +] + +[[package]] +name = "pfc-ibc-reflect-send" +version = "0.0.1" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cosmwasm-storage", + "cosmwasm-vm", + "schemars", + "serde", +] + [[package]] name = "pfc-steak" version = "2.2.4" @@ -720,7 +1386,7 @@ dependencies = [ [[package]] name = "pfc-steak-hub" -version = "3.0.3" +version = "3.0.4" dependencies = [ "cosmwasm-std", "cw-storage-plus 0.13.4", @@ -735,7 +1401,7 @@ dependencies = [ [[package]] name = "pfc-steak-hub-tf" -version = "3.0.3" +version = "3.0.4" dependencies = [ "cosmwasm-std", "cw-item-set", @@ -762,6 +1428,12 @@ dependencies = [ "cw20-base 0.13.4", ] +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + [[package]] name = "pkcs8" version = "0.9.0" @@ -772,6 +1444,30 @@ dependencies = [ "spki", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.54" @@ -834,6 +1530,26 @@ dependencies = [ "thiserror", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "quote" version = "1.0.26" @@ -862,87 +1578,228 @@ dependencies = [ ] [[package]] -name = "rfc6979" -version = "0.3.0" +name = "rayon" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88c86280f057430a52f4861551b092a01b419b8eacefc7c995eacb9dc132fe32" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" dependencies = [ - "crypto-bigint", - "hmac", - "zeroize", + "either", + "rayon-core", ] [[package]] -name = "ryu" -version = "1.0.9" +name = "rayon-core" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] [[package]] -name = "schemars" -version = "0.8.11" +name = "redox_syscall" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a5fb6c61f29e723026dc8e923d94c694313212abbecbbe5f55a7748eec5b307" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "dyn-clone", - "schemars_derive", - "serde", - "serde_json", + "bitflags", ] [[package]] -name = "schemars_derive" -version = "0.8.11" +name = "regalloc" +version = "0.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f188d036977451159430f3b8dc82ec76364a42b7e289c2b18a9a18f4470058e9" +checksum = "62446b1d3ebf980bdc68837700af1d77b37bc430e524bf95319c6eada2a4cc02" dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn 1.0.109", + "log", + "rustc-hash", + "smallvec", ] [[package]] -name = "sec1" -version = "0.3.0" +name = "region" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" dependencies = [ - "base16ct", - "der", - "generic-array", - "pkcs8", - "subtle", - "zeroize", + "bitflags", + "libc", + "mach", + "winapi", ] [[package]] -name = "semver" -version = "1.0.14" +name = "rend" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" +checksum = "581008d2099240d37fb08d77ad713bcaec2c4d89d50b5b21a8bb1996bbab68ab" +dependencies = [ + "bytecheck", +] [[package]] -name = "serde" -version = "1.0.136" +name = "rfc6979" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +checksum = "88c86280f057430a52f4861551b092a01b419b8eacefc7c995eacb9dc132fe32" dependencies = [ - "serde_derive", + "crypto-bigint", + "hmac", + "zeroize", ] [[package]] -name = "serde-json-wasm" -version = "0.5.0" +name = "rkyv" +version = "0.7.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15bee9b04dd165c3f4e142628982ddde884c2022a89e8ddf99c4829bf2c3a58" +checksum = "21499ed91807f07ae081880aabb2ccc0235e9d88011867d984525e9a4c3cfa3e" dependencies = [ - "serde", + "bytecheck", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", ] [[package]] -name = "serde_derive" -version = "1.0.136" +name = "rkyv_derive" +version = "0.7.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1c672430eb41556291981f45ca900a0239ad007242d1cb4b4167af842db666" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.37.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bbfc1d1c7c40c01715f47d71444744a81669ca84e8b63e25a55e169b1f86433" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustversion" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "schemars" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a5fb6c61f29e723026dc8e923d94c694313212abbecbbe5f55a7748eec5b307" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f188d036977451159430f3b8dc82ec76364a42b7e289c2b18a9a18f4470058e9" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 1.0.109", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" + +[[package]] +name = "serde" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-json-wasm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15bee9b04dd165c3f4e142628982ddde884c2022a89e8ddf99c4829bf2c3a58" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_bytes" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" dependencies = [ @@ -1007,6 +1864,18 @@ dependencies = [ "rand_core 0.6.3", ] +[[package]] +name = "simdutf8" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + [[package]] name = "spki" version = "0.6.0" @@ -1017,6 +1886,12 @@ dependencies = [ "der", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" @@ -1051,6 +1926,25 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "target-lexicon" +version = "0.12.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd1ba337640d60c3e96bc6f0638a939b9c9a7f2c316a1598c279828b3d1dc8c5" + +[[package]] +name = "tempfile" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys 0.45.0", +] + [[package]] name = "thiserror" version = "1.0.40" @@ -1071,6 +1965,39 @@ dependencies = [ "syn 2.0.11", ] +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.11", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + [[package]] name = "typenum" version = "1.15.0" @@ -1113,6 +2040,536 @@ version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 1.0.109", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "wasmer" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea8d8361c9d006ea3d7797de7bd6b1492ffd0f91a22430cfda6c1658ad57bedf" +dependencies = [ + "cfg-if", + "indexmap", + "js-sys", + "loupe", + "more-asserts", + "target-lexicon", + "thiserror", + "wasm-bindgen", + "wasmer-artifact", + "wasmer-compiler", + "wasmer-compiler-cranelift", + "wasmer-compiler-singlepass", + "wasmer-derive", + "wasmer-engine", + "wasmer-engine-dylib", + "wasmer-engine-universal", + "wasmer-types", + "wasmer-vm", + "winapi", +] + +[[package]] +name = "wasmer-artifact" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aaf9428c29c1d8ad2ac0e45889ba8a568a835e33fd058964e5e500f2f7ce325" +dependencies = [ + "enumset", + "loupe", + "thiserror", + "wasmer-compiler", + "wasmer-types", +] + +[[package]] +name = "wasmer-compiler" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67a6cd866aed456656db2cfea96c18baabbd33f676578482b85c51e1ee19d2c" +dependencies = [ + "enumset", + "loupe", + "rkyv", + "serde", + "serde_bytes", + "smallvec", + "target-lexicon", + "thiserror", + "wasmer-types", + "wasmparser", +] + +[[package]] +name = "wasmer-compiler-cranelift" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48be2f9f6495f08649e4f8b946a2cbbe119faf5a654aa1457f9504a99d23dae0" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "gimli 0.26.2", + "loupe", + "more-asserts", + "rayon", + "smallvec", + "target-lexicon", + "tracing", + "wasmer-compiler", + "wasmer-types", +] + +[[package]] +name = "wasmer-compiler-singlepass" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ca2a35204d8befa85062bc7aac259a8db8070b801b8a783770ba58231d729e" +dependencies = [ + "byteorder", + "dynasm", + "dynasmrt", + "gimli 0.26.2", + "lazy_static", + "loupe", + "more-asserts", + "rayon", + "smallvec", + "wasmer-compiler", + "wasmer-types", +] + +[[package]] +name = "wasmer-derive" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00e50405cc2a2f74ff574584710a5f2c1d5c93744acce2ca0866084739284b51" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "wasmer-engine" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f98f010978c244db431b392aeab0661df7ea0822343334f8f2a920763548e45" +dependencies = [ + "backtrace", + "enumset", + "lazy_static", + "loupe", + "memmap2", + "more-asserts", + "rustc-demangle", + "serde", + "serde_bytes", + "target-lexicon", + "thiserror", + "wasmer-artifact", + "wasmer-compiler", + "wasmer-types", + "wasmer-vm", +] + +[[package]] +name = "wasmer-engine-dylib" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0358af9c154724587731175553805648d9acb8f6657880d165e378672b7e53" +dependencies = [ + "cfg-if", + "enum-iterator", + "enumset", + "leb128", + "libloading", + "loupe", + "object 0.28.4", + "rkyv", + "serde", + "tempfile", + "tracing", + "wasmer-artifact", + "wasmer-compiler", + "wasmer-engine", + "wasmer-object", + "wasmer-types", + "wasmer-vm", + "which", +] + +[[package]] +name = "wasmer-engine-universal" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "440dc3d93c9ca47865a4f4edd037ea81bf983b5796b59b3d712d844b32dbef15" +dependencies = [ + "cfg-if", + "enumset", + "leb128", + "loupe", + "region", + "rkyv", + "wasmer-compiler", + "wasmer-engine", + "wasmer-engine-universal-artifact", + "wasmer-types", + "wasmer-vm", + "winapi", +] + +[[package]] +name = "wasmer-engine-universal-artifact" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f1db3f54152657eb6e86c44b66525ff7801dad8328fe677da48dd06af9ad41" +dependencies = [ + "enum-iterator", + "enumset", + "loupe", + "rkyv", + "thiserror", + "wasmer-artifact", + "wasmer-compiler", + "wasmer-types", +] + +[[package]] +name = "wasmer-middlewares" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7812438ed2f37203a37007cdb5332b8475cb2b16e15d51299b2647894e9ed3a" +dependencies = [ + "loupe", + "wasmer", + "wasmer-types", + "wasmer-vm", +] + +[[package]] +name = "wasmer-object" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d831335ff3a44ecf451303f6f891175c642488036b92ceceb24ac8623a8fa8b" +dependencies = [ + "object 0.28.4", + "thiserror", + "wasmer-compiler", + "wasmer-types", +] + +[[package]] +name = "wasmer-types" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39df01ea05dc0a9bab67e054c7cb01521e53b35a7bb90bd02eca564ed0b2667f" +dependencies = [ + "backtrace", + "enum-iterator", + "indexmap", + "loupe", + "more-asserts", + "rkyv", + "serde", + "thiserror", +] + +[[package]] +name = "wasmer-vm" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d965fa61f4dc4cdb35a54daaf7ecec3563fbb94154a6c35433f879466247dd" +dependencies = [ + "backtrace", + "cc", + "cfg-if", + "corosensei", + "enum-iterator", + "indexmap", + "lazy_static", + "libc", + "loupe", + "mach", + "memoffset 0.6.5", + "more-asserts", + "region", + "rkyv", + "scopeguard", + "serde", + "thiserror", + "wasmer-artifact", + "wasmer-types", + "winapi", +] + +[[package]] +name = "wasmparser" +version = "0.83.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "718ed7c55c2add6548cca3ddd6383d738cd73b892df400e96b9aa876f0141d7a" + +[[package]] +name = "which" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +dependencies = [ + "either", + "libc", + "once_cell", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43dbb096663629518eb1dfa72d80243ca5a6aca764cae62a2df70af760a9be75" +dependencies = [ + "windows_aarch64_msvc 0.33.0", + "windows_i686_gnu 0.33.0", + "windows_i686_msvc 0.33.0", + "windows_x86_64_gnu 0.33.0", + "windows_x86_64_msvc 0.33.0", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd761fd3eb9ab8cc1ed81e56e567f02dd82c4c837e48ac3b2181b9ffc5060807" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab0cf703a96bab2dc0c02c0fa748491294bf9b7feb27e1f4f96340f208ada0e" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cfdbe89cc9ad7ce618ba34abc34bbb6c36d99e96cae2245b7943cd75ee773d0" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4dd9b0c0e9ece7bb22e84d70d01b71c6d6248b81a3c60d11869451b4cb24784" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff1e4aa646495048ec7f3ffddc411e1d829c026a2ec62b39da15c1055e406eaa" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + [[package]] name = "zeroize" version = "1.5.7" diff --git a/build_arm.sh b/build_arm.sh index af37350..346acbc 100755 --- a/build_arm.sh +++ b/build_arm.sh @@ -2,4 +2,4 @@ docker run --rm -v "$(pwd)":/code \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ - cosmwasm/workspace-optimizer-arm64:0.12.11 + cosmwasm/workspace-optimizer-arm64:0.12.13 diff --git a/contracts/hub-tf/Cargo.toml b/contracts/hub-tf/Cargo.toml index 748ae68..a51cfd1 100644 --- a/contracts/hub-tf/Cargo.toml +++ b/contracts/hub-tf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub-tf" -version = "3.0.3" +version = "3.0.4" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub-tf/src/contract.rs b/contracts/hub-tf/src/contract.rs index 64841eb..cec9241 100644 --- a/contracts/hub-tf/src/contract.rs +++ b/contracts/hub-tf/src/contract.rs @@ -15,6 +15,7 @@ pub const CONTRACT_NAME: &str = "steak-hub-tf"; pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); pub const REPLY_INSTANTIATE_TOKEN: u64 = 1; pub const REPLY_REGISTER_RECEIVED_COINS: u64 = 2; +pub const SPECIAL_SEND_MESSAGE_TO_TRANSFER: &str = "PFC_TRANSFER_NOT_SEND"; #[entry_point] pub fn instantiate( diff --git a/contracts/hub-tf/src/execute.rs b/contracts/hub-tf/src/execute.rs index dbb1f56..1bdc138 100644 --- a/contracts/hub-tf/src/execute.rs +++ b/contracts/hub-tf/src/execute.rs @@ -11,7 +11,7 @@ use pfc_steak::hub::{ use pfc_steak::hub_tf::{ExecuteMsg, InstantiateMsg, TokenFactoryType}; use crate::{injective, token_factory}; -use crate::contract::REPLY_REGISTER_RECEIVED_COINS; +use crate::contract::{REPLY_REGISTER_RECEIVED_COINS, SPECIAL_SEND_MESSAGE_TO_TRANSFER}; use crate::helpers::{get_denom_balance, parse_received_fund, query_all_delegations, query_delegation, query_delegations}; use crate::kujira; use crate::math::{ @@ -207,17 +207,25 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec, bond_msg: // send the uSteak, optionally calling a smart contract let send_transfer_msg: CosmosMsg = match contract_info { Ok(_) => { - - //CosmosMsg::Bank(BankMsg::Send { to_address: "".to_string(), amount: vec![] }) if let Some(exec_msg) = bond_msg { - CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: receiver.to_string(), - msg: to_binary(&exec_msg)?, - funds: vec![Coin { - denom: steak_denom, - amount: usteak_to_mint, - }], - }) + if exec_msg == SPECIAL_SEND_MESSAGE_TO_TRANSFER { // this is for backwards compatibility only + CosmosMsg::Bank(BankMsg::Send { + to_address: receiver.to_string(), + amount: vec![Coin { + denom: steak_denom, + amount: usteak_to_mint, + }], + }) + } else { + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: receiver.to_string(), + msg: to_binary(&exec_msg)?, + funds: vec![Coin { + denom: steak_denom, + amount: usteak_to_mint, + }], + }) + } } else { CosmosMsg::Bank(BankMsg::Send { to_address: receiver.to_string(), diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index 99fc6df..654dde3 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub" -version = "3.0.3" +version = "3.0.4" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub/src/contract.rs b/contracts/hub/src/contract.rs index 5f31196..d14a2ad 100644 --- a/contracts/hub/src/contract.rs +++ b/contracts/hub/src/contract.rs @@ -18,6 +18,7 @@ pub const CONTRACT_NAME: &str = "steak-hub"; pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); pub const REPLY_INSTANTIATE_TOKEN: u64 = 1; pub const REPLY_REGISTER_RECEIVED_COINS: u64 = 2; +pub const SPECIAL_SEND_MESSAGE_TO_TRANSFER: &str = "PFC_TRANSFER_NOT_SEND"; #[entry_point] pub fn instantiate( diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index 7574807..b38628c 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -14,7 +14,7 @@ use pfc_steak::hub::{ Batch, CallbackMsg, ExecuteMsg, FeeType, InstantiateMsg, PendingBatch, UnbondRequest, }; -use crate::contract::{REPLY_INSTANTIATE_TOKEN, REPLY_REGISTER_RECEIVED_COINS}; +use crate::contract::{REPLY_INSTANTIATE_TOKEN, REPLY_REGISTER_RECEIVED_COINS, SPECIAL_SEND_MESSAGE_TO_TRANSFER}; use crate::helpers::{ get_denom_balance, parse_received_fund, query_cw20_total_supply, query_delegation, query_delegations, @@ -189,15 +189,26 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec,bond_msg: let send_transfer_msg: CosmosMsg = match contract_info { Ok(_) => { if let Some(exec_msg) = bond_msg { - CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: steak_token.to_string(), - msg: to_binary(&Cw20ExecuteMsg::Send { - contract: receiver.to_string(), - amount: usteak_to_mint, - msg: to_binary(&exec_msg)?, - })?, - funds: vec![], - }) + if exec_msg == SPECIAL_SEND_MESSAGE_TO_TRANSFER { + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: steak_token.to_string(), + msg: to_binary(&Cw20ExecuteMsg::Transfer { + recipient: receiver.to_string(), + amount: usteak_to_mint, + })?, + funds: vec![], + }) + } else { + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: steak_token.to_string(), + msg: to_binary(&Cw20ExecuteMsg::Send { + contract: receiver.to_string(), + amount: usteak_to_mint, + msg: to_binary(&exec_msg)?, + })?, + funds: vec![], + }) + } } else { CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: steak_token.to_string(), From ccc6ffc6b469a868e0215a405ae699daefa51a23 Mon Sep 17 00:00:00 2001 From: The PFC <81114960+PFC-developer@users.noreply.github.com> Date: Wed, 10 May 2023 16:09:26 -0500 Subject: [PATCH 41/77] feat: v3.0.4 - special message to TRANSFER instead of SEND --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f2a5378..376ff53 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,9 @@ Terra liquid staking derivative. Of the community, by the community, for the community. -A previous version ([v1.0.0-rc0](https://github.com/st4k3h0us3/steak-contracts/releases/tag/v1.0.0-rc0)) of Steak was audited by [SCV Security](https://twitter.com/TerraSCV) ([link](https://github.com/SCV-Security/PublicReports/blob/main/CW/St4k3h0us3/St4k3h0us3%20-%20Steak%20Contracts%20Audit%20Review%20-%20%20v1.0.pdf)). - +Previous versions of Steak have been audited: +* [v1.0.0-rc0](https://github.com/st4k3h0us3/steak-contracts/releases/tag/v1.0.0-rc0) by [SCV Security](https://twitter.com/TerraSCV) ([link](https://github.com/SCV-Security/PublicReports/blob/main/CW/St4k3h0us3/St4k3h0us3%20-%20Steak%20Contracts%20Audit%20Review%20-%20%20v1.0.pdf)). +* [v3.0.3-cargo](https://github.com/PFC-developer/steak-contracts/tree/v3.0.3-cargo) by [SCV](https://github.com/SCV-Security/PublicReports/blob/a2d955cac6398f78a3cd067a04bda147ec7ba5c3/CW%2FPFC%2FPFC%20-%20Steak%20%26%20Fee%20Split%20-%20Audit%20Report%20v1.0.pdf) ## Contracts | Contract | Description | From 2d3fdd9fe35b1d40d534c0e87abaaa9c5d03822a Mon Sep 17 00:00:00 2001 From: The PFC <81114960+PFC-developer@users.noreply.github.com> Date: Fri, 12 May 2023 13:51:52 -0500 Subject: [PATCH 42/77] feat: v3.0.5 - change bond message to Binary, not String --- Cargo.lock | 4 ++-- contracts/hub/Cargo.toml | 2 +- contracts/hub/src/execute.rs | 9 +++------ packages/steak/Cargo.toml | 2 +- packages/steak/src/hub.rs | 4 ++-- 5 files changed, 9 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 10b2b14..1ffdfbb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1374,7 +1374,7 @@ dependencies = [ [[package]] name = "pfc-steak" -version = "2.2.4" +version = "3.0.1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1386,7 +1386,7 @@ dependencies = [ [[package]] name = "pfc-steak-hub" -version = "3.0.4" +version = "3.0.5" dependencies = [ "cosmwasm-std", "cw-storage-plus 0.13.4", diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index 654dde3..affcc3e 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub" -version = "3.0.4" +version = "3.0.5" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index b38628c..9a63e66 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -2,10 +2,7 @@ use std::collections::HashSet; use std::iter::FromIterator; use std::str::FromStr; -use cosmwasm_std::{ - Addr, BankMsg, Coin, CosmosMsg, Decimal, DepsMut, DistributionMsg, Env, Event, Order, - Response, StdError, StdResult, SubMsg, SubMsgResponse, to_binary, Uint128, WasmMsg, -}; +use cosmwasm_std::{Addr, BankMsg, Binary, Coin, CosmosMsg, Decimal, DepsMut, DistributionMsg, Env, Event, Order, Response, StdError, StdResult, SubMsg, SubMsgResponse, to_binary, Uint128, WasmMsg}; use cw20::{Cw20ExecuteMsg, MinterResponse}; use cw20_base::msg::InstantiateMsg as Cw20InstantiateMsg; @@ -128,7 +125,7 @@ pub fn register_steak_token(deps: DepsMut, response: SubMsgResponse) -> StdResul /// smallest amount of delegation. If delegations become severely unbalance as a result of this /// (e.g. when a single user makes a very big deposit), anyone can invoke `ExecuteMsg::Rebalance` /// to balance the delegations. -pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec,bond_msg: Option) -> StdResult { +pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec,bond_msg: Option) -> StdResult { let state = State::default(); let denom = state.denom.load(deps.storage)?; let amount_to_bond = parse_received_fund(&funds, &denom)?; @@ -189,7 +186,7 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec,bond_msg: let send_transfer_msg: CosmosMsg = match contract_info { Ok(_) => { if let Some(exec_msg) = bond_msg { - if exec_msg == SPECIAL_SEND_MESSAGE_TO_TRANSFER { + if exec_msg == to_binary(SPECIAL_SEND_MESSAGE_TO_TRANSFER)? { CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: steak_token.to_string(), msg: to_binary(&Cw20ExecuteMsg::Transfer { diff --git a/packages/steak/Cargo.toml b/packages/steak/Cargo.toml index 522e765..ec1dd94 100644 --- a/packages/steak/Cargo.toml +++ b/packages/steak/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak" -version = "2.2.4" +version = "3.0.1" authors = ["larry ", "PFC "] edition = "2018" description = "Liquid steaking protocol for the cosmos" diff --git a/packages/steak/src/hub.rs b/packages/steak/src/hub.rs index a9a0b6b..52eb738 100644 --- a/packages/steak/src/hub.rs +++ b/packages/steak/src/hub.rs @@ -1,6 +1,6 @@ use std::str::FromStr; -use cosmwasm_std::{Addr, Coin, CosmosMsg, Decimal, Empty, StdResult, to_binary, Uint128, WasmMsg}; +use cosmwasm_std::{Addr, Binary, Coin, CosmosMsg, Decimal, Empty, StdResult, to_binary, Uint128, WasmMsg}; use cw20::Cw20ReceiveMsg; use cw20_base::msg::InstantiateMarketingInfo as Cw20InstantiateMarketingInfo; use schemars::JsonSchema; @@ -47,7 +47,7 @@ pub enum ExecuteMsg { /// Implements the Cw20 receiver interface Receive(Cw20ReceiveMsg), /// Bond specified amount of Luna - Bond { receiver: Option, exec_msg: Option }, + Bond { receiver: Option, exec_msg: Option }, /// Withdraw Luna that have finished unbonding in previous batches WithdrawUnbonded { receiver: Option }, /// Withdraw Luna that has finished unbonding in previous batches, for given address From f0cc13b6b922600ec22c5827fa526bfe5ea02057 Mon Sep 17 00:00:00 2001 From: The PFC <81114960+PFC-developer@users.noreply.github.com> Date: Fri, 12 May 2023 15:52:05 -0500 Subject: [PATCH 43/77] feat: v3.0.6 - still playing with messages --- contracts/hub/Cargo.toml | 2 +- contracts/hub/src/execute.rs | 51 +++++++++++++++++++----------------- packages/steak/Cargo.toml | 4 +-- packages/steak/src/hub.rs | 6 +++++ 4 files changed, 36 insertions(+), 27 deletions(-) diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index affcc3e..5b9e0f9 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub" -version = "3.0.5" +version = "3.0.6" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index 9a63e66..96a12a6 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -2,16 +2,14 @@ use std::collections::HashSet; use std::iter::FromIterator; use std::str::FromStr; -use cosmwasm_std::{Addr, BankMsg, Binary, Coin, CosmosMsg, Decimal, DepsMut, DistributionMsg, Env, Event, Order, Response, StdError, StdResult, SubMsg, SubMsgResponse, to_binary, Uint128, WasmMsg}; +use cosmwasm_std::{Addr, BankMsg, Binary, Coin, CosmosMsg, Decimal, DepsMut, DistributionMsg, Env, Event, from_binary, Order, Response, StdError, StdResult, SubMsg, SubMsgResponse, to_binary, Uint128, WasmMsg}; use cw20::{Cw20ExecuteMsg, MinterResponse}; use cw20_base::msg::InstantiateMsg as Cw20InstantiateMsg; use pfc_steak::DecimalCheckedOps; -use pfc_steak::hub::{ - Batch, CallbackMsg, ExecuteMsg, FeeType, InstantiateMsg, PendingBatch, UnbondRequest, -}; +use pfc_steak::hub::{Batch, CallbackMsg, Cw20HookMsg, ExecuteMsg, FeeType, InstantiateMsg, PendingBatch, UnbondRequest}; -use crate::contract::{REPLY_INSTANTIATE_TOKEN, REPLY_REGISTER_RECEIVED_COINS, SPECIAL_SEND_MESSAGE_TO_TRANSFER}; +use crate::contract::{REPLY_INSTANTIATE_TOKEN, REPLY_REGISTER_RECEIVED_COINS}; use crate::helpers::{ get_denom_balance, parse_received_fund, query_cw20_total_supply, query_delegation, query_delegations, @@ -185,27 +183,32 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec,bond_msg: let send_transfer_msg: CosmosMsg = match contract_info { Ok(_) => { + if let Some(exec_msg) = bond_msg { - if exec_msg == to_binary(SPECIAL_SEND_MESSAGE_TO_TRANSFER)? { - CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: steak_token.to_string(), - msg: to_binary(&Cw20ExecuteMsg::Transfer { - recipient: receiver.to_string(), - amount: usteak_to_mint, - })?, - funds: vec![], - }) - } else { - CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: steak_token.to_string(), - msg: to_binary(&Cw20ExecuteMsg::Send { - contract: receiver.to_string(), - amount: usteak_to_mint, - msg: to_binary(&exec_msg)?, - })?, - funds: vec![], - }) + match from_binary(&exec_msg)? + { + Cw20HookMsg::Transfer {} => { + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: steak_token.to_string(), + msg: to_binary(&Cw20ExecuteMsg::Transfer { + recipient: receiver.to_string(), + amount: usteak_to_mint, + })?, + funds: vec![], + }) + }, + _=> CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: steak_token.to_string(), + msg: to_binary(&Cw20ExecuteMsg::Send { + contract: receiver.to_string(), + amount: usteak_to_mint, + msg: to_binary(&exec_msg)?, + })?, + funds: vec![], + }) + } + } else { CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: steak_token.to_string(), diff --git a/packages/steak/Cargo.toml b/packages/steak/Cargo.toml index ec1dd94..3ff8bde 100644 --- a/packages/steak/Cargo.toml +++ b/packages/steak/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak" -version = "3.0.1" +version = "3.0.2" authors = ["larry ", "PFC "] edition = "2018" description = "Liquid steaking protocol for the cosmos" @@ -9,7 +9,7 @@ homepage = "https://liquidsteaking.app" repository = "https://github.com/PFC-developer/steak-contracts" [dependencies] -cosmwasm-std = {version="1.2.1", features=["ibc3"]} +cosmwasm-std = { version = "1.2.1", features = ["ibc3"] } cosmwasm-schema = "1.2.1" cw20 = "1.0.0" cw20-base = { version = "1.0.0", features = ["library"] } diff --git a/packages/steak/src/hub.rs b/packages/steak/src/hub.rs index 52eb738..8b83b20 100644 --- a/packages/steak/src/hub.rs +++ b/packages/steak/src/hub.rs @@ -6,6 +6,12 @@ use cw20_base::msg::InstantiateMarketingInfo as Cw20InstantiateMarketingInfo; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +pub enum Cw20HookMsg { + Distribute {}, + Transfer{} +} + #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct InstantiateMsg { /// Code ID of the CW20 token contract From 8b0d4860e19d936c3d7a378e971f1751a6d084d1 Mon Sep 17 00:00:00 2001 From: The PFC <81114960+PFC-developer@users.noreply.github.com> Date: Fri, 12 May 2023 15:58:03 -0500 Subject: [PATCH 44/77] feat: v3.0.6 - still playing with messages --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1ffdfbb..223c2bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1374,7 +1374,7 @@ dependencies = [ [[package]] name = "pfc-steak" -version = "3.0.1" +version = "3.0.2" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1386,7 +1386,7 @@ dependencies = [ [[package]] name = "pfc-steak-hub" -version = "3.0.5" +version = "3.0.6" dependencies = [ "cosmwasm-std", "cw-storage-plus 0.13.4", From c6d51313f988b36a3d0651427aabe0d5f1c07db1 Mon Sep 17 00:00:00 2001 From: The PFC <81114960+PFC-developer@users.noreply.github.com> Date: Fri, 12 May 2023 16:50:12 -0500 Subject: [PATCH 45/77] feat: v3.0.6 - messages hardcoded. --- Cargo.lock | 24 ++++++++++---- contracts/hub/Cargo.toml | 3 +- contracts/hub/src/execute.rs | 63 ++++++++++++++++++------------------ packages/steak/src/hub.rs | 4 +++ 4 files changed, 55 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 223c2bf..1d76277 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -659,13 +659,13 @@ dependencies = [ [[package]] name = "cw20" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a4d69a9980e2295077c08fd3f797149658f357167f5898e27c00b9465fcbde" +checksum = "91666da6c7b40c8dd5ff94df655a28114efc10c79b70b4d06f13c31e37d60609" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw-utils 0.16.0", + "cw-utils 1.0.1", "schemars", "serde", ] @@ -697,7 +697,7 @@ dependencies = [ "cw-storage-plus 0.16.0", "cw-utils 0.16.0", "cw2 1.0.1", - "cw20 1.0.0", + "cw20 1.0.1", "schemars", "semver", "serde", @@ -963,6 +963,17 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" +[[package]] +name = "funds-distributor-api" +version = "0.1.0" +source = "git+https://github.com/terra-money/enterprise-contracts.git?branch=main#6028fa7bf7110df3a8e82cfd9cd67686401d0061" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw20 1.0.1", + "thiserror", +] + [[package]] name = "generic-array" version = "0.14.5" @@ -1378,7 +1389,7 @@ version = "3.0.2" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw20 1.0.0", + "cw20 1.0.1", "cw20-base 1.0.0", "schemars", "serde", @@ -1391,8 +1402,9 @@ dependencies = [ "cosmwasm-std", "cw-storage-plus 0.13.4", "cw2 1.0.1", - "cw20 1.0.0", + "cw20 1.0.1", "cw20-base 1.0.0", + "funds-distributor-api", "pfc-dust-collector", "pfc-fee-split", "pfc-steak", diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index 5b9e0f9..71353f6 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -15,12 +15,13 @@ backtraces = ["cosmwasm-std/backtraces"] [dependencies] cosmwasm-std = { version = "1.0", features = ["staking"] } cw2= "1.0.0" -cw20 = "1.0.0" +cw20 = "1.0.1" cw20-base = { version = "1.0.0", features = ["library"] } cw-storage-plus = "0.13" pfc-steak = { path = "../../packages/steak" } serde = { version = "1.0.103", default-features = false, features = ["derive"] } pfc-fee-split = "0.2.3" pfc-dust-collector = { path = "../../packages/dust_collector" } +funds-distributor-api = {git="https://github.com/terra-money/enterprise-contracts.git" , branch="main"} #[dev-dependencies] #serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index 96a12a6..dccdac4 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -123,14 +123,14 @@ pub fn register_steak_token(deps: DepsMut, response: SubMsgResponse) -> StdResul /// smallest amount of delegation. If delegations become severely unbalance as a result of this /// (e.g. when a single user makes a very big deposit), anyone can invoke `ExecuteMsg::Rebalance` /// to balance the delegations. -pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec,bond_msg: Option) -> StdResult { +pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec, bond_msg: Option) -> StdResult { let state = State::default(); let denom = state.denom.load(deps.storage)?; let amount_to_bond = parse_received_fund(&funds, &denom)?; let steak_token = state.steak_token.load(deps.storage)?; let validators = state.validators_active.load(deps.storage)?; - let mut validators_wl:HashSet = HashSet::from_iter(state.validators.load(deps.storage)?); + let mut validators_wl: HashSet = HashSet::from_iter(state.validators.load(deps.storage)?); for v in validators.iter() { validators_wl.remove(v); } @@ -183,32 +183,31 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec,bond_msg: let send_transfer_msg: CosmosMsg = match contract_info { Ok(_) => { - if let Some(exec_msg) = bond_msg { - match from_binary(&exec_msg)? - { - Cw20HookMsg::Transfer {} => { - CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: steak_token.to_string(), - msg: to_binary(&Cw20ExecuteMsg::Transfer { - recipient: receiver.to_string(), - amount: usteak_to_mint, - })?, - funds: vec![], - }) - }, - _=> CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: steak_token.to_string(), - msg: to_binary(&Cw20ExecuteMsg::Send { - contract: receiver.to_string(), - amount: usteak_to_mint, - msg: to_binary(&exec_msg)?, - })?, - funds: vec![], - }) - + match from_binary(&exec_msg)? + { + Cw20HookMsg::Transfer {} => { + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: steak_token.to_string(), + msg: to_binary(&Cw20ExecuteMsg::Transfer { + recipient: receiver.to_string(), + amount: usteak_to_mint, + })?, + funds: vec![], + }) + } + Cw20HookMsg::Distribute {} => { + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: steak_token.to_string(), + msg: to_binary(&Cw20ExecuteMsg::Send { + contract: receiver.to_string(), + amount: usteak_to_mint, + msg: to_binary(&funds_distributor_api::msg::Cw20HookMsg::Distribute {})?, + })?, + funds: vec![], + }) + } } - } else { CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: steak_token.to_string(), @@ -490,7 +489,7 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { pending_batch.est_unbond_start_time ))); } - let mut validators_active:HashSet = HashSet::from_iter(state.validators_active.load(deps.storage)?); + let mut validators_active: HashSet = HashSet::from_iter(state.validators_active.load(deps.storage)?); for v in validators.iter() { validators_active.remove(v); } @@ -1025,13 +1024,13 @@ pub fn set_dust_collector( let state = State::default(); state.assert_owner(deps.storage, &sender)?; - if let Some(ref dust_addr ) = dust_collector { - state.dust_collector.save(deps.storage, &Some(deps.api.addr_validate(dust_addr )? ))?; + if let Some(ref dust_addr) = dust_collector { + state.dust_collector.save(deps.storage, &Some(deps.api.addr_validate(dust_addr)?))?; } else { state.dust_collector.save(deps.storage, &None)?; }; let event = Event::new("steak/set_dust_collector") - .add_attribute("dust_collector", dust_collector.unwrap_or("-cleared-".into())); + .add_attribute("dust_collector", dust_collector.unwrap_or("-cleared-".into())); Ok(Response::new() .add_event(event) @@ -1044,7 +1043,7 @@ pub fn collect_dust(deps: DepsMut, _env: Env) -> StdResult { if let Some(_dust_addr) = state.dust_collector.load(deps.storage)? { Ok(Response::new().add_attribute("dust", "tbd")) } else { - Err(StdError::generic_err("No dust collector set")) + Err(StdError::generic_err("No dust collector set")) } } @@ -1054,6 +1053,6 @@ pub fn return_denom(deps: DepsMut, _env: Env, _funds: Vec) -> StdResult Date: Thu, 1 Jun 2023 10:29:35 -0500 Subject: [PATCH 46/77] add admin-only redelegate --- Cargo.lock | 4 ++-- contracts/hub/Cargo.toml | 2 +- contracts/hub/src/contract.rs | 4 +++- contracts/hub/src/execute.rs | 41 ++++++++++++++++++++++++++++++++++- init/narwhal_ibc_init.json | 3 +++ packages/steak/Cargo.toml | 2 +- packages/steak/src/hub.rs | 2 ++ 7 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 init/narwhal_ibc_init.json diff --git a/Cargo.lock b/Cargo.lock index 1d76277..1daf225 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1385,7 +1385,7 @@ dependencies = [ [[package]] name = "pfc-steak" -version = "3.0.2" +version = "3.0.3" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1397,7 +1397,7 @@ dependencies = [ [[package]] name = "pfc-steak-hub" -version = "3.0.6" +version = "3.0.7" dependencies = [ "cosmwasm-std", "cw-storage-plus 0.13.4", diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index 71353f6..8258df3 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub" -version = "3.0.6" +version = "3.0.7" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub/src/contract.rs b/contracts/hub/src/contract.rs index d14a2ad..46f6709 100644 --- a/contracts/hub/src/contract.rs +++ b/contracts/hub/src/contract.rs @@ -67,7 +67,9 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S ExecuteMsg::RemoveValidatorEx { validator } => { execute::remove_validator_ex(deps, env, info.sender, validator) } - + ExecuteMsg::Redelegate { validator_from,validator_to } => { + execute::redelegate(deps, env, info.sender, validator_from,validator_to) + } ExecuteMsg::TransferOwnership { new_owner } => { execute::transfer_ownership(deps, info.sender, new_owner) } diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index dccdac4..c903a97 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -19,7 +19,7 @@ use crate::math::{ compute_unbond_amount, compute_undelegations, reconcile_batches, }; use crate::state::State; -use crate::types::{Coins, Delegation}; +use crate::types::{Coins, Delegation, Redelegation}; //-------------------------------------------------------------------------------------------------- // Instantiation @@ -1055,4 +1055,43 @@ pub fn return_denom(deps: DepsMut, _env: Env, _funds: Vec) -> StdResult StdResult { + let state = State::default(); + + state.assert_owner(deps.storage, &sender)?; + let denom = state.denom.load(deps.storage)?; + + let delegation = query_delegation(&deps.querier, &validator_from, &env.contract.address, &denom)?; + + let redelegation_msg = SubMsg::reply_on_success(Redelegation::new( + &validator_from, + &validator_to, + delegation.amount, + &denom, + ).to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS); + + state.prev_denom.save( + deps.storage, + &get_denom_balance(&deps.querier, env.contract.address, denom)?, + )?; + + + let event = Event::new("steak/redelegate") + .add_attribute("validator_from", validator_from) + .add_attribute("validator_to", validator_to) + .add_attribute("amount", delegation.amount.to_string()); + + Ok(Response::new() + .add_submessage(redelegation_msg) + .add_event(event) + .add_attribute("action", "steakhub/redelegate")) } \ No newline at end of file diff --git a/init/narwhal_ibc_init.json b/init/narwhal_ibc_init.json new file mode 100644 index 0000000..081e651 --- /dev/null +++ b/init/narwhal_ibc_init.json @@ -0,0 +1,3 @@ +{ +"reflect_code_id": 128 +} \ No newline at end of file diff --git a/packages/steak/Cargo.toml b/packages/steak/Cargo.toml index 3ff8bde..1afd47d 100644 --- a/packages/steak/Cargo.toml +++ b/packages/steak/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak" -version = "3.0.2" +version = "3.0.3" authors = ["larry ", "PFC "] edition = "2018" description = "Liquid steaking protocol for the cosmos" diff --git a/packages/steak/src/hub.rs b/packages/steak/src/hub.rs index ba7e08c..c245c53 100644 --- a/packages/steak/src/hub.rs +++ b/packages/steak/src/hub.rs @@ -82,6 +82,8 @@ pub enum ExecuteMsg { Harvest {}, /// Use redelegations to balance the amounts of Luna delegated to validators Rebalance { minimum: Uint128 }, + /// redelegate stake from one validator to another + Redelegate { validator_from: String, validator_to: String }, /// Update Luna amounts in unbonding batches to reflect any slashing or rounding errors Reconcile {}, /// Submit the current pending batch of unbonding requests to be unbonded From 16784d55ca262957c930d8e15f86f0c8993e63f7 Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Sun, 30 Jul 2023 15:36:33 -0500 Subject: [PATCH 47/77] feat: v3.0.8 - bond_ex - mint directly to receiver instead of SEND/TRANSFERing them --- Cargo.lock | 2 +- contracts/hub/Cargo.toml | 2 +- contracts/hub/src/contract.rs | 12 ++++ contracts/hub/src/execute.rs | 111 ++++++++++++++++------------- contracts/hub/src/testing/tests.rs | 42 +++++++++++ packages/steak/src/hub.rs | 2 + 6 files changed, 119 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1daf225..7a26399 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1397,7 +1397,7 @@ dependencies = [ [[package]] name = "pfc-steak-hub" -version = "3.0.7" +version = "3.0.8" dependencies = [ "cosmwasm-std", "cw-storage-plus 0.13.4", diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index 8258df3..bb86e6d 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub" -version = "3.0.7" +version = "3.0.8" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub/src/contract.rs b/contracts/hub/src/contract.rs index 46f6709..28967d9 100644 --- a/contracts/hub/src/contract.rs +++ b/contracts/hub/src/contract.rs @@ -45,6 +45,18 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S .unwrap_or(info.sender), info.funds, exec_msg, + false + ), + ExecuteMsg::BondEx { receiver, } => execute::bond( + deps, + env, + receiver + .map(|s| api.addr_validate(&s)) + .transpose()? + .unwrap_or(info.sender), + info.funds, + None, + true ), ExecuteMsg::WithdrawUnbonded { receiver } => execute::withdraw_unbonded( deps, diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index c903a97..9c6b24f 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -123,7 +123,7 @@ pub fn register_steak_token(deps: DepsMut, response: SubMsgResponse) -> StdResul /// smallest amount of delegation. If delegations become severely unbalance as a result of this /// (e.g. when a single user makes a very big deposit), anyone can invoke `ExecuteMsg::Rebalance` /// to balance the delegations. -pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec, bond_msg: Option) -> StdResult { +pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec, bond_msg: Option, mint_it: bool) -> StdResult { let state = State::default(); let denom = state.denom.load(deps.storage)?; let amount_to_bond = parse_received_fund(&funds, &denom)?; @@ -169,70 +169,81 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec, bond_msg: REPLY_REGISTER_RECEIVED_COINS, ); - - let mint_msg: CosmosMsg = CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: steak_token.to_string(), - msg: to_binary(&Cw20ExecuteMsg::Mint { - recipient: env.contract.address.to_string(),//receiver.to_string(), - amount: usteak_to_mint, - })?, - funds: vec![], - }); - - let contract_info = deps.querier.query_wasm_contract_info(receiver.to_string()); - - let send_transfer_msg: CosmosMsg = match contract_info { - Ok(_) => { - if let Some(exec_msg) = bond_msg { - match from_binary(&exec_msg)? - { - Cw20HookMsg::Transfer {} => { - CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: steak_token.to_string(), - msg: to_binary(&Cw20ExecuteMsg::Transfer { - recipient: receiver.to_string(), - amount: usteak_to_mint, - })?, - funds: vec![], - }) - } - Cw20HookMsg::Distribute {} => { - CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: steak_token.to_string(), - msg: to_binary(&Cw20ExecuteMsg::Send { - contract: receiver.to_string(), - amount: usteak_to_mint, - msg: to_binary(&funds_distributor_api::msg::Cw20HookMsg::Distribute {})?, - })?, - funds: vec![], - }) + let mint_msgs: Vec = if mint_it { + vec!(CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: steak_token.to_string(), + msg: to_binary(&Cw20ExecuteMsg::Mint { + recipient: receiver.to_string(), + amount: usteak_to_mint, + })?, + funds: vec![], + })) + } else { + let contract_info = deps.querier.query_wasm_contract_info(receiver.to_string()); + + let send_transfer_msg: CosmosMsg = match contract_info { + Ok(_) => { + if let Some(exec_msg) = bond_msg { + match from_binary(&exec_msg)? + { + Cw20HookMsg::Transfer {} => { + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: steak_token.to_string(), + msg: to_binary(&Cw20ExecuteMsg::Transfer { + recipient: receiver.to_string(), + amount: usteak_to_mint, + })?, + funds: vec![], + }) + } + Cw20HookMsg::Distribute {} => { + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: steak_token.to_string(), + msg: to_binary(&Cw20ExecuteMsg::Send { + contract: receiver.to_string(), + amount: usteak_to_mint, + msg: to_binary(&funds_distributor_api::msg::Cw20HookMsg::Distribute {})?, + })?, + funds: vec![], + }) + } } + } else { + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: steak_token.to_string(), + msg: to_binary(&Cw20ExecuteMsg::Send { + contract: receiver.to_string(), + amount: usteak_to_mint, + msg: Default::default(), + })?, + funds: vec![], + }) } - } else { + } + Err(_) => { CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: steak_token.to_string(), - msg: to_binary(&Cw20ExecuteMsg::Send { - contract: receiver.to_string(), + msg: to_binary(&Cw20ExecuteMsg::Transfer { + recipient: receiver.to_string(), amount: usteak_to_mint, - msg: Default::default(), + // msg: Default::default(), })?, funds: vec![], }) } - } - Err(_) => { + }; + vec!( CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: steak_token.to_string(), - msg: to_binary(&Cw20ExecuteMsg::Transfer { - recipient: receiver.to_string(), + msg: to_binary(&Cw20ExecuteMsg::Mint { + recipient: env.contract.address.to_string(),//receiver.to_string(), amount: usteak_to_mint, - // msg: Default::default(), })?, funds: vec![], - }) - } + }), send_transfer_msg) }; + let event = Event::new("steakhub/bonded") .add_attribute("time", env.block.time.seconds().to_string()) .add_attribute("height", env.block.height.to_string()) @@ -243,7 +254,7 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec, bond_msg: Ok(Response::new() .add_submessage(delegate_submsg) - .add_messages(vec![mint_msg, send_transfer_msg]) + .add_messages(mint_msgs) // .add_message(send_msg) .add_event(event) .add_attribute("action", "steakhub/bond")) diff --git a/contracts/hub/src/testing/tests.rs b/contracts/hub/src/testing/tests.rs index 1c7181d..4ea9d87 100644 --- a/contracts/hub/src/testing/tests.rs +++ b/contracts/hub/src/testing/tests.rs @@ -413,6 +413,48 @@ fn bonding() { } ); } +#[test] +fn bond_ex() { + let mut deps = setup_test(); + + // Bond when no delegation has been made + // In this case, the full deposit simply goes to the first validator + let res = execute( + deps.as_mut(), + mock_env(), + mock_info("user_1", &[Coin::new(1_000_000, "uxyz")]), + ExecuteMsg::BondEx { receiver: None}, + ) + .unwrap(); + + // 3 messages. (switched to 3 so we can 'send' instead of 'transfer' minted tokens, so contract will know about it + // 1 - delegate + // 2 - mint token (to ourselves) + // 3 - send/transfer it + assert_eq!(res.messages.len(), 2); + assert_eq!( + res.messages[0], + SubMsg::reply_on_success(Delegation::new("alice", 1_000_000, "uxyz").to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS) + ); + assert_eq!( + res.messages[1], + SubMsg { + id: 0, + msg: CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: "steak_token".to_string(), + msg: to_binary(&Cw20ExecuteMsg::Mint { + recipient: "user_1".to_string(), + amount: Uint128::new(1_000_000), + }) + .unwrap(), + funds: vec![], + }), + gas_limit: None, + reply_on: ReplyOn::Never, + } + ); + +} #[test] fn harvesting() { diff --git a/packages/steak/src/hub.rs b/packages/steak/src/hub.rs index c245c53..e7c912e 100644 --- a/packages/steak/src/hub.rs +++ b/packages/steak/src/hub.rs @@ -58,6 +58,8 @@ pub enum ExecuteMsg { Receive(Cw20ReceiveMsg), /// Bond specified amount of Luna Bond { receiver: Option, exec_msg: Option }, + /// Bond specified amount of Luna, just minting it directly (cw-20 version only) + BondEx { receiver: Option }, /// Withdraw Luna that have finished unbonding in previous batches WithdrawUnbonded { receiver: Option }, /// Withdraw Luna that has finished unbonding in previous batches, for given address From 8741097228841d767653edde0b4ef5cca458c515 Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Sun, 30 Jul 2023 15:39:22 -0500 Subject: [PATCH 48/77] feat: v3.0.8 - bond_ex - mint directly to receiver instead of SEND/TRANSFERing them --- packages/steak/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/steak/Cargo.toml b/packages/steak/Cargo.toml index 1afd47d..54a8425 100644 --- a/packages/steak/Cargo.toml +++ b/packages/steak/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak" -version = "3.0.3" +version = "3.0.8" authors = ["larry ", "PFC "] edition = "2018" description = "Liquid steaking protocol for the cosmos" From 9aed59cbc403dddd55db5f64bc1dea365b59710b Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Sun, 30 Jul 2023 15:52:11 -0500 Subject: [PATCH 49/77] feat: v3.0.8 - bond_ex - mint directly to receiver instead of SEND/TRANSFERing them --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 7a26399..e6eddc9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1385,7 +1385,7 @@ dependencies = [ [[package]] name = "pfc-steak" -version = "3.0.3" +version = "3.0.8" dependencies = [ "cosmwasm-schema", "cosmwasm-std", From 77fb0c8afba86031917eb674f0958b5b2ab56e30 Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Fri, 6 Oct 2023 08:52:56 -0500 Subject: [PATCH 50/77] add admin redelegate function --- Cargo.lock | 4 ++-- contracts/hub-tf/Cargo.toml | 2 +- contracts/hub-tf/src/contract.rs | 3 +++ contracts/hub-tf/src/execute.rs | 40 +++++++++++++++++++++++++++++++- packages/steak/Cargo.toml | 2 +- packages/steak/src/hub_tf.rs | 2 ++ 6 files changed, 48 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e6eddc9..45a68fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1385,7 +1385,7 @@ dependencies = [ [[package]] name = "pfc-steak" -version = "3.0.8" +version = "3.0.9" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1413,7 +1413,7 @@ dependencies = [ [[package]] name = "pfc-steak-hub-tf" -version = "3.0.4" +version = "3.0.9" dependencies = [ "cosmwasm-std", "cw-item-set", diff --git a/contracts/hub-tf/Cargo.toml b/contracts/hub-tf/Cargo.toml index a51cfd1..9e2e4f2 100644 --- a/contracts/hub-tf/Cargo.toml +++ b/contracts/hub-tf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub-tf" -version = "3.0.4" +version = "3.0.9" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub-tf/src/contract.rs b/contracts/hub-tf/src/contract.rs index cec9241..86ad9ed 100644 --- a/contracts/hub-tf/src/contract.rs +++ b/contracts/hub-tf/src/contract.rs @@ -71,6 +71,9 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S ExecuteMsg::RemoveValidatorEx { validator } => { execute::remove_validator_ex(deps, env, info.sender, validator) } + ExecuteMsg::Redelegate { validator_from,validator_to } => { + execute::redelegate(deps, env, info.sender, validator_from,validator_to) + } ExecuteMsg::TransferOwnership { new_owner } => { execute::transfer_ownership(deps, info.sender, new_owner) diff --git a/contracts/hub-tf/src/execute.rs b/contracts/hub-tf/src/execute.rs index 1bdc138..b1f42f4 100644 --- a/contracts/hub-tf/src/execute.rs +++ b/contracts/hub-tf/src/execute.rs @@ -20,7 +20,7 @@ use crate::math::{ }; use crate::state::{previous_batches, State, unbond_requests, VALIDATORS, VALIDATORS_ACTIVE}; //use crate::token_factory::denom::{MsgBurn, MsgCreateDenom, MsgMint}; -use crate::types::{Coins, Delegation}; +use crate::types::{Coins, Delegation, Redelegation}; //-------------------------------------------------------------------------------------------------- // Instantiation @@ -1088,4 +1088,42 @@ pub fn return_denom(deps: DepsMut, _env: Env, _funds: Vec) -> StdResult StdResult { + let state = State::default(); + + state.assert_owner(deps.storage, &sender)?; + let denom = state.denom.load(deps.storage)?; + + let delegation = query_delegation(&deps.querier, &validator_from, &env.contract.address, &denom)?; + + let redelegation_msg = SubMsg::reply_on_success(Redelegation::new( + &validator_from, + &validator_to, + delegation.amount, + &denom, + ).to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS); + + state.prev_denom.save( + deps.storage, + &get_denom_balance(&deps.querier, env.contract.address, denom)?, + )?; + + + let event = Event::new("steak/redelegate") + .add_attribute("validator_from", validator_from) + .add_attribute("validator_to", validator_to) + .add_attribute("amount", delegation.amount.to_string()); + + Ok(Response::new() + .add_submessage(redelegation_msg) + .add_event(event) + .add_attribute("action", "steakhub/redelegate")) } \ No newline at end of file diff --git a/packages/steak/Cargo.toml b/packages/steak/Cargo.toml index 54a8425..33be813 100644 --- a/packages/steak/Cargo.toml +++ b/packages/steak/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak" -version = "3.0.8" +version = "3.0.9" authors = ["larry ", "PFC "] edition = "2018" description = "Liquid steaking protocol for the cosmos" diff --git a/packages/steak/src/hub_tf.rs b/packages/steak/src/hub_tf.rs index 1a63eac..6d23d0a 100644 --- a/packages/steak/src/hub_tf.rs +++ b/packages/steak/src/hub_tf.rs @@ -91,6 +91,8 @@ pub enum ExecuteMsg { Harvest {}, /// Use redelegations to balance the amounts of Luna delegated to validators Rebalance { minimum: Uint128 }, + /// redelegate stake from one validator to another + Redelegate { validator_from: String, validator_to: String }, /// Update Luna amounts in unbonding batches to reflect any slashing or rounding errors Reconcile {}, /// Submit the current pending batch of unbonding requests to be unbonded From c82d488b9b0ee8869a20c7d45d90f4b4ff20a9e9 Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Fri, 27 Oct 2023 12:01:33 -0500 Subject: [PATCH 51/77] chore: fix clippy. switch to package for dust-collector --- Cargo.lock | 108 ++++++++---------- contracts/hub-tf/Cargo.toml | 6 +- contracts/hub-tf/src/contract.rs | 4 +- contracts/hub-tf/src/execute.rs | 2 +- contracts/hub-tf/src/queries.rs | 6 +- contracts/hub/Cargo.toml | 4 +- contracts/hub/src/queries.rs | 6 +- packages/dust_collector/Cargo.toml | 17 --- packages/dust_collector/README.md | 6 - packages/dust_collector/src/dust_collector.rs | 9 -- packages/dust_collector/src/lib.rs | 2 - packages/steak/src/hub_tf.rs | 2 +- 12 files changed, 65 insertions(+), 107 deletions(-) delete mode 100644 packages/dust_collector/Cargo.toml delete mode 100644 packages/dust_collector/README.md delete mode 100644 packages/dust_collector/src/dust_collector.rs delete mode 100644 packages/dust_collector/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 45a68fe..243fd28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -491,17 +491,6 @@ dependencies = [ "serde", ] -[[package]] -name = "cw-storage-plus" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc6cf70ef7686e2da9ad7b067c5942cd3e88dd9453f7af42f54557f8af300fb0" -dependencies = [ - "cosmwasm-std", - "schemars", - "serde", -] - [[package]] name = "cw-storage-plus" version = "0.16.0" @@ -536,21 +525,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "cw-utils" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ae0b69fa7679de78825b4edeeec045066aa2b2c4b6e063d80042e565bb4da5c" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw2 0.15.1", - "schemars", - "semver", - "serde", - "thiserror", -] - [[package]] name = "cw-utils" version = "0.16.0" @@ -593,19 +567,6 @@ dependencies = [ "serde", ] -[[package]] -name = "cw2" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5abb8ecea72e09afff830252963cb60faf945ce6cef2c20a43814516082653da" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 0.15.1", - "schemars", - "serde", -] - [[package]] name = "cw2" version = "0.16.0" @@ -644,19 +605,6 @@ dependencies = [ "serde", ] -[[package]] -name = "cw20" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6025276fb6e603e974c21f3e4606982cdc646080e8fba3198816605505e1d9a" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-utils 0.15.1", - "schemars", - "serde", -] - [[package]] name = "cw20" version = "1.0.1" @@ -1339,22 +1287,40 @@ checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" [[package]] name = "pfc-dust-collector" -version = "3.0.0" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9debbb83cefcd272c751c07f984f75f70be4195e87dddbdb87247bd969d51436" dependencies = [ "cosmwasm-schema", "cosmwasm-std", + "cw-ownable", + "cw-ownable-derive", + "pfc-dust-collector-derive", + "pfc-whitelist", + "pfc-whitelist-derive", "schemars", "serde", ] +[[package]] +name = "pfc-dust-collector-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d72f8e98168a562a454569e5cc70070c4408d1a8f7828efce41fd496e52fb36d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "pfc-fee-split" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5e71d09b61b7c6babdf97bcc98cfef4f60d5e8f662b28d8a5ab42fde1b5c6db" +checksum = "659bc95805fb3ab45d6db2eda7ee71bfe5fa952e4705146e0ce3d8113daaab57" dependencies = [ "cosmwasm-std", - "cw20 0.15.1", + "cw20 1.0.1", "schemars", "serde", ] @@ -1397,7 +1363,7 @@ dependencies = [ [[package]] name = "pfc-steak-hub" -version = "3.0.8" +version = "3.0.10" dependencies = [ "cosmwasm-std", "cw-storage-plus 0.13.4", @@ -1413,7 +1379,7 @@ dependencies = [ [[package]] name = "pfc-steak-hub-tf" -version = "3.0.9" +version = "3.0.10" dependencies = [ "cosmwasm-std", "cw-item-set", @@ -1440,6 +1406,32 @@ dependencies = [ "cw20-base 0.13.4", ] +[[package]] +name = "pfc-whitelist" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "217870c932a72a63c52c9126792a5bd29773b1318b65635d900091c8f54d3a5c" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.0.1", + "pfc-whitelist-derive", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "pfc-whitelist-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8cd39f1b0792d2a383d072795fc88ee82bc671374cac0a1ec229ff8abe5da1" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "pin-project-lite" version = "0.2.9" diff --git a/contracts/hub-tf/Cargo.toml b/contracts/hub-tf/Cargo.toml index 9e2e4f2..f5d5369 100644 --- a/contracts/hub-tf/Cargo.toml +++ b/contracts/hub-tf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub-tf" -version = "3.0.9" +version = "3.0.10" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" @@ -26,9 +26,9 @@ prost = {version = "0.11.0", default-features = false, features = ["prost-derive prost-types = {version = "0.11.1", default-features = false} schemars = "0.8.11" pfc-steak = { path = "../../packages/steak" } -pfc-dust-collector = { path = "../../packages/dust_collector" } serde = { version = "1.0.103", default-features = false, features = ["derive"] } -pfc-fee-split = "0.2.3" +pfc-fee-split = "0.2.5" +pfc-dust-collector="0.1.0" #kujira = "0.7.25" osmosis-std-derive="0.13.2" protobuf = { version = "3.1.0", features = ["with-bytes"] } \ No newline at end of file diff --git a/contracts/hub-tf/src/contract.rs b/contracts/hub-tf/src/contract.rs index 86ad9ed..8b47d08 100644 --- a/contracts/hub-tf/src/contract.rs +++ b/contracts/hub-tf/src/contract.rs @@ -97,7 +97,7 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S ExecuteMsg::SetUnbondPeriod { unbond_period } => execute::set_unbond_period(deps, env, info.sender, unbond_period), ExecuteMsg::SetDustCollector { dust_collector } => { execute::set_dust_collector(deps, env, info.sender, dust_collector) } - ExecuteMsg::CollectDust {} => { execute::collect_dust(deps, env) } + ExecuteMsg::CollectDust { max_tokens } => { execute::collect_dust(deps, env, max_tokens) } ExecuteMsg::ReturnDenom {} => { execute::return_denom(deps, env, info.funds) } } } @@ -124,7 +124,7 @@ fn callback( pub fn reply(deps: DepsMut, env: Env, reply: Reply) -> StdResult { match reply.id { REPLY_REGISTER_RECEIVED_COINS => { - execute::collect_dust(deps, env) + execute::collect_dust(deps, env,10) } _ => { Err(StdError::generic_err(format!( diff --git a/contracts/hub-tf/src/execute.rs b/contracts/hub-tf/src/execute.rs index b1f42f4..1561ae7 100644 --- a/contracts/hub-tf/src/execute.rs +++ b/contracts/hub-tf/src/execute.rs @@ -1070,7 +1070,7 @@ pub fn set_dust_collector( .add_attribute("action", "steakhub/set_dust_collector")) } -pub fn collect_dust(deps: DepsMut, _env: Env) -> StdResult { +pub fn collect_dust(deps: DepsMut, _env: Env, max_tokens: u64) -> StdResult { let state = State::default(); if let Some(_dust_addr) = state.dust_collector.load(deps.storage)? { diff --git a/contracts/hub-tf/src/queries.rs b/contracts/hub-tf/src/queries.rs index 0642e37..3db704d 100644 --- a/contracts/hub-tf/src/queries.rs +++ b/contracts/hub-tf/src/queries.rs @@ -31,8 +31,8 @@ pub fn config(deps: Deps) -> StdResult { } - let validator_active_vec: Vec = Vec::from_iter(validators_active.into_iter()); - let paused_validators: Vec = Vec::from_iter(validators.into_iter()); + let validator_active_vec: Vec = Vec::from_iter(validators_active); + let paused_validators: Vec = Vec::from_iter(validators); Ok(ConfigResponse { owner: state.owner.load(deps.storage)?.into(), @@ -68,7 +68,7 @@ pub fn state(deps: Deps, env: Env) -> StdResult { validators_active.insert(res?); } validators.extend(validators_active); - let validator_vec: Vec = Vec::from_iter(validators.into_iter()); + let validator_vec: Vec = Vec::from_iter(validators); let delegations = query_delegations(&deps.querier, &validator_vec, &env.contract.address, &denom)?; let total_native: u128 = delegations.iter().map(|d| d.amount).sum(); diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index bb86e6d..1820cf7 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub" -version = "3.0.8" +version = "3.0.10" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" @@ -21,7 +21,7 @@ cw-storage-plus = "0.13" pfc-steak = { path = "../../packages/steak" } serde = { version = "1.0.103", default-features = false, features = ["derive"] } pfc-fee-split = "0.2.3" -pfc-dust-collector = { path = "../../packages/dust_collector" } +pfc-dust-collector = "0.1.0" funds-distributor-api = {git="https://github.com/terra-money/enterprise-contracts.git" , branch="main"} #[dev-dependencies] #serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/contracts/hub/src/queries.rs b/contracts/hub/src/queries.rs index 16d1e6b..09ac999 100644 --- a/contracts/hub/src/queries.rs +++ b/contracts/hub/src/queries.rs @@ -23,8 +23,8 @@ pub fn config(deps: Deps) -> StdResult { for v in validators_active.iter() { validators.remove(v); } - let validator_active_vec: Vec = Vec::from_iter(validators_active.into_iter()); - let paused_validators: Vec = Vec::from_iter(validators.into_iter()); + let validator_active_vec: Vec = Vec::from_iter(validators_active); + let paused_validators: Vec = Vec::from_iter(validators); Ok(ConfigResponse { owner: state.owner.load(deps.storage)?.into(), @@ -57,7 +57,7 @@ pub fn state(deps: Deps, env: Env) -> StdResult { let mut validators: HashSet = HashSet::from_iter(state.validators.load(deps.storage)?); let validators_active: HashSet = HashSet::from_iter(state.validators_active.load(deps.storage)?); validators.extend(validators_active); - let validator_vec: Vec = Vec::from_iter(validators.into_iter()); + let validator_vec: Vec = Vec::from_iter(validators); let delegations = query_delegations(&deps.querier, &validator_vec, &env.contract.address, &denom)?; let total_native: u128 = delegations.iter().map(|d| d.amount).sum(); diff --git a/packages/dust_collector/Cargo.toml b/packages/dust_collector/Cargo.toml deleted file mode 100644 index 20b3bf2..0000000 --- a/packages/dust_collector/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "pfc-dust-collector" -version = "3.0.0" -authors = ["PFC "] -edition = "2018" -description = "Liquid steaking protocol for the cosmos" -license = "GPL-3.0-or-later" -homepage = "https://liquidsteaking.app" -repository = "https://github.com/PFC-developer/steak-contracts" - -[dependencies] -cosmwasm-std = {version="1.2.1", features=["ibc3"]} -cosmwasm-schema = "1.2.1" - -schemars = "0.8.1" -serde = { version = "1.0.103", default-features = false, features = ["derive"] } - diff --git a/packages/dust_collector/README.md b/packages/dust_collector/README.md deleted file mode 100644 index 6b7571e..0000000 --- a/packages/dust_collector/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# Dust Collector Shared Messages -Messages used to call the Dust collector - -## License - -Contents of this repository are open source under [GNU General Public License v3](https://www.gnu.org/licenses/gpl-3.0.en.html) or later. diff --git a/packages/dust_collector/src/dust_collector.rs b/packages/dust_collector/src/dust_collector.rs deleted file mode 100644 index 9f006bc..0000000 --- a/packages/dust_collector/src/dust_collector.rs +++ /dev/null @@ -1,9 +0,0 @@ - -use cosmwasm_schema::cw_serde; - - -#[cw_serde] -pub enum ExecuteMsg { - /// get some dust - DustReceived(), -} diff --git a/packages/dust_collector/src/lib.rs b/packages/dust_collector/src/lib.rs deleted file mode 100644 index f2f0a6c..0000000 --- a/packages/dust_collector/src/lib.rs +++ /dev/null @@ -1,2 +0,0 @@ - -pub mod dust_collector; diff --git a/packages/steak/src/hub_tf.rs b/packages/steak/src/hub_tf.rs index 6d23d0a..fda9bdb 100644 --- a/packages/steak/src/hub_tf.rs +++ b/packages/steak/src/hub_tf.rs @@ -112,7 +112,7 @@ pub enum ExecuteMsg { /// Set Dust Collector Contract SetDustCollector { dust_collector: Option }, /// Collect the Dust - CollectDust {}, + CollectDust { max_tokens: u64}, /// Return the Dust in shiny 'base denom' ReturnDenom {}, } From d1b8240dfab489cf1da043947dd8f9e91b6b1b94 Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Sat, 28 Oct 2023 15:12:50 -0500 Subject: [PATCH 52/77] feat: dust-collection functional/tested --- Cargo.lock | 1588 +------------------------ Cargo.toml | 9 +- contracts/hub-tf/Cargo.toml | 5 +- contracts/hub-tf/src/contract.rs | 82 +- contracts/hub-tf/src/execute.rs | 435 ++++--- contracts/hub-tf/src/testing/tests.rs | 425 +++++-- contracts/hub/Cargo.toml | 2 +- contracts/hub/src/contract.rs | 74 +- contracts/hub/src/execute.rs | 2 +- packages/steak/src/hub.rs | 75 +- packages/steak/src/hub_tf.rs | 21 +- 11 files changed, 813 insertions(+), 1905 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 243fd28..8767aca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,59 +2,12 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "addr2line" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" -dependencies = [ - "gimli 0.27.2", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "ahash" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" -dependencies = [ - "getrandom 0.2.6", - "once_cell", - "version_check", -] - [[package]] name = "anyhow" version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "backtrace" -version = "0.3.67" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object 0.30.3", - "rustc-demangle", -] - [[package]] name = "base16ct" version = "0.1.1" @@ -73,12 +26,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dea908e7347a8c64e378c17e30ef880ad73e3b4498346b055c2c00ea342f3179" -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "block-buffer" version = "0.9.0" @@ -97,34 +44,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "bumpalo" -version = "3.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" - -[[package]] -name = "bytecheck" -version = "0.6.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13fe11640a23eb24562225322cd3e452b93a3d4091d62fab69c70542fcd17d1f" -dependencies = [ - "bytecheck_derive", - "ptr_meta", - "simdutf8", -] - -[[package]] -name = "bytecheck_derive" -version = "0.6.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31225543cb46f81a7e224762764f4a6a0f097b1db0b175f69e8065efaa42de5" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "byteorder" version = "1.4.3" @@ -137,43 +56,18 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" -[[package]] -name = "cc" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" - [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "clru" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591ff76ca0691bd91c1b0b5b987e5cf93b21ec810ad96665c5a569c60846dd93" - [[package]] name = "const-oid" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "722e23542a15cea1f65d4a1419c4cfd7a26706c70871a13a04238ca3f40f1661" -[[package]] -name = "corosensei" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9847f90f32a50b0dcbd68bc23ff242798b13080b97b0569f6ed96a45ce4cf2cd" -dependencies = [ - "autocfg", - "cfg-if", - "libc", - "scopeguard", - "windows-sys 0.33.0", -] - [[package]] name = "cosmwasm-crypto" version = "1.2.4" @@ -240,40 +134,6 @@ dependencies = [ "uint", ] -[[package]] -name = "cosmwasm-storage" -version = "1.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88f7944b5204c3a998f73248925a097ace887bdf01c2f70de00d8b92cd69e807" -dependencies = [ - "cosmwasm-std", - "serde", -] - -[[package]] -name = "cosmwasm-vm" -version = "1.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cb647b2f444c88b02da06794f27fc4270989fb50969c47998a493a79d77ccef" -dependencies = [ - "bitflags", - "bytecheck", - "clru", - "cosmwasm-crypto", - "cosmwasm-std", - "enumset", - "hex", - "loupe", - "parity-wasm", - "schemars", - "serde", - "serde_json", - "sha2 0.10.6", - "thiserror", - "wasmer", - "wasmer-middlewares", -] - [[package]] name = "cpufeatures" version = "0.2.2" @@ -283,117 +143,6 @@ dependencies = [ "libc", ] -[[package]] -name = "cranelift-bforest" -version = "0.82.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38faa2a16616c8e78a18d37b4726b98bfd2de192f2fdc8a39ddf568a408a0f75" -dependencies = [ - "cranelift-entity", -] - -[[package]] -name = "cranelift-codegen" -version = "0.82.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26f192472a3ba23860afd07d2b0217dc628f21fcc72617aa1336d98e1671f33b" -dependencies = [ - "cranelift-bforest", - "cranelift-codegen-meta", - "cranelift-codegen-shared", - "cranelift-entity", - "gimli 0.26.2", - "log", - "regalloc", - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cranelift-codegen-meta" -version = "0.82.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f32ddb89e9b89d3d9b36a5b7d7ea3261c98235a76ac95ba46826b8ec40b1a24" -dependencies = [ - "cranelift-codegen-shared", -] - -[[package]] -name = "cranelift-codegen-shared" -version = "0.82.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01fd0d9f288cc1b42d9333b7a776b17e278fc888c28e6a0f09b5573d45a150bc" - -[[package]] -name = "cranelift-entity" -version = "0.82.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e3bfe172b83167604601faf9dc60453e0d0a93415b57a9c4d1a7ae6849185cf" - -[[package]] -name = "cranelift-frontend" -version = "0.82.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a006e3e32d80ce0e4ba7f1f9ddf66066d052a8c884a110b91d05404d6ce26dce" -dependencies = [ - "cranelift-codegen", - "log", - "smallvec", - "target-lexicon", -] - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" -dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", - "memoffset 0.8.0", - "scopeguard", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" -dependencies = [ - "cfg-if", -] - [[package]] name = "crunchy" version = "0.2.2" @@ -652,40 +401,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "darling" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "darling_macro" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" -dependencies = [ - "darling_core", - "quote", - "syn 1.0.109", -] - [[package]] name = "der" version = "0.6.0" @@ -733,32 +448,6 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21e50f3adc76d6a43f5ed73b698a87d0760ca74617f60f7c3b879003536fdd28" -[[package]] -name = "dynasm" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add9a102807b524ec050363f09e06f1504214b0e1c7797f64261c891022dce8b" -dependencies = [ - "bitflags", - "byteorder", - "lazy_static", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "dynasmrt" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" -dependencies = [ - "byteorder", - "dynasm", - "memmap2", -] - [[package]] name = "ecdsa" version = "0.14.8" @@ -812,83 +501,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "enum-iterator" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eeac5c5edb79e4e39fe8439ef35207780a11f69c52cbe424ce3dfad4cb78de6" -dependencies = [ - "enum-iterator-derive", -] - -[[package]] -name = "enum-iterator-derive" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c134c37760b27a871ba422106eedbb8247da973a09e82558bf26d619c882b159" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "enumset" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19be8061a06ab6f3a6cf21106c873578bf01bd42ad15e0311a9c76161cb1c753" -dependencies = [ - "enumset_derive", -] - -[[package]] -name = "enumset_derive" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03e7b551eba279bf0fa88b83a46330168c1560a52a94f5126f892f0b364ab3e0" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "errno" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "fallible-iterator" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" - -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - [[package]] name = "ff" version = "0.12.0" @@ -899,12 +511,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "forward_ref" version = "1.0.0" @@ -914,7 +520,7 @@ checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" [[package]] name = "funds-distributor-api" version = "0.1.0" -source = "git+https://github.com/terra-money/enterprise-contracts.git?branch=main#6028fa7bf7110df3a8e82cfd9cd67686401d0061" +source = "git+https://github.com/terra-money/enterprise-contracts.git?branch=main#6d736c0bbc1a6ac52d862c8b80d63be6bb43d419" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -954,23 +560,6 @@ dependencies = [ "wasi 0.10.2+wasi-snapshot-preview1", ] -[[package]] -name = "gimli" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" -dependencies = [ - "fallible-iterator", - "indexmap", - "stable_deref_trait", -] - -[[package]] -name = "gimli" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" - [[package]] name = "group" version = "0.12.1" @@ -983,117 +572,38 @@ dependencies = [ ] [[package]] -name = "hashbrown" -version = "0.11.2" +name = "hex" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" -dependencies = [ - "ahash", -] +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] -name = "hashbrown" -version = "0.12.3" +name = "hmac" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "ahash", + "digest 0.10.5", ] [[package]] -name = "hermit-abi" -version = "0.2.6" +name = "itertools" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ - "libc", + "either", ] [[package]] -name = "hermit-abi" -version = "0.3.1" +name = "itoa" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" [[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest 0.10.5", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" -dependencies = [ - "hermit-abi 0.3.1", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" - -[[package]] -name = "js-sys" -version = "0.3.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "k256" -version = "0.11.6" +name = "k256" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" dependencies = [ @@ -1103,158 +613,12 @@ dependencies = [ "sha2 0.10.6", ] -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "leb128" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" - [[package]] name = "libc" version = "0.2.142" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" -[[package]] -name = "libloading" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" -dependencies = [ - "cfg-if", - "winapi", -] - -[[package]] -name = "linux-raw-sys" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b64f40e5e03e0d54f03845c8197d0291253cdbedfb1cb46b13c2c117554a9f4c" - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "loupe" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6a72dfa44fe15b5e76b94307eeb2ff995a8c5b283b55008940c02e0c5b634d" -dependencies = [ - "indexmap", - "loupe-derive", - "rustversion", -] - -[[package]] -name = "loupe-derive" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fbfc88337168279f2e9ae06e157cfed4efd3316e14dc96ed074d4f2e6c5952" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "mach" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" -dependencies = [ - "libc", -] - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "memmap2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" -dependencies = [ - "libc", -] - -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - -[[package]] -name = "memoffset" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" -dependencies = [ - "autocfg", -] - -[[package]] -name = "miniz_oxide" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" -dependencies = [ - "adler", -] - -[[package]] -name = "more-asserts" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" - -[[package]] -name = "num_cpus" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" -dependencies = [ - "hermit-abi 0.2.6", - "libc", -] - -[[package]] -name = "object" -version = "0.28.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" -dependencies = [ - "crc32fast", - "hashbrown 0.11.2", - "indexmap", - "memchr", -] - -[[package]] -name = "object" -version = "0.30.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" version = "1.17.1" @@ -1279,12 +643,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "parity-wasm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" - [[package]] name = "pfc-dust-collector" version = "0.1.0" @@ -1325,30 +683,6 @@ dependencies = [ "serde", ] -[[package]] -name = "pfc-ibc-reflect" -version = "0.0.1" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cosmwasm-storage", - "cosmwasm-vm", - "schemars", - "serde", -] - -[[package]] -name = "pfc-ibc-reflect-send" -version = "0.0.1" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cosmwasm-storage", - "cosmwasm-vm", - "schemars", - "serde", -] - [[package]] name = "pfc-steak" version = "3.0.9" @@ -1371,7 +705,6 @@ dependencies = [ "cw20 1.0.1", "cw20-base 1.0.0", "funds-distributor-api", - "pfc-dust-collector", "pfc-fee-split", "pfc-steak", "serde", @@ -1432,12 +765,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "pin-project-lite" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" - [[package]] name = "pkcs8" version = "0.9.0" @@ -1448,30 +775,6 @@ dependencies = [ "spki", ] -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro2" version = "1.0.54" @@ -1534,26 +837,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "ptr_meta" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" -dependencies = [ - "ptr_meta_derive", -] - -[[package]] -name = "ptr_meta_derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "quote" version = "1.0.26" @@ -1582,98 +865,89 @@ dependencies = [ ] [[package]] -name = "rayon" -version = "1.7.0" +name = "rfc6979" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +checksum = "88c86280f057430a52f4861551b092a01b419b8eacefc7c995eacb9dc132fe32" dependencies = [ - "either", - "rayon-core", + "crypto-bigint", + "hmac", + "zeroize", ] [[package]] -name = "rayon-core" -version = "1.11.0" +name = "ryu" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "num_cpus", -] +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" [[package]] -name = "redox_syscall" -version = "0.3.5" +name = "schemars" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "2a5fb6c61f29e723026dc8e923d94c694313212abbecbbe5f55a7748eec5b307" dependencies = [ - "bitflags", + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", ] [[package]] -name = "regalloc" -version = "0.0.34" +name = "schemars_derive" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62446b1d3ebf980bdc68837700af1d77b37bc430e524bf95319c6eada2a4cc02" +checksum = "f188d036977451159430f3b8dc82ec76364a42b7e289c2b18a9a18f4470058e9" dependencies = [ - "log", - "rustc-hash", - "smallvec", + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 1.0.109", ] [[package]] -name = "region" -version = "3.0.0" +name = "sec1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ - "bitflags", - "libc", - "mach", - "winapi", + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", ] [[package]] -name = "rend" -version = "0.4.0" +name = "semver" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581008d2099240d37fb08d77ad713bcaec2c4d89d50b5b21a8bb1996bbab68ab" -dependencies = [ - "bytecheck", -] +checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" [[package]] -name = "rfc6979" -version = "0.3.0" +name = "serde" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88c86280f057430a52f4861551b092a01b419b8eacefc7c995eacb9dc132fe32" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" dependencies = [ - "crypto-bigint", - "hmac", - "zeroize", + "serde_derive", ] [[package]] -name = "rkyv" -version = "0.7.41" +name = "serde-json-wasm" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21499ed91807f07ae081880aabb2ccc0235e9d88011867d984525e9a4c3cfa3e" +checksum = "a15bee9b04dd165c3f4e142628982ddde884c2022a89e8ddf99c4829bf2c3a58" dependencies = [ - "bytecheck", - "hashbrown 0.12.3", - "ptr_meta", - "rend", - "rkyv_derive", - "seahash", + "serde", ] [[package]] -name = "rkyv_derive" -version = "0.7.41" +name = "serde_derive" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1c672430eb41556291981f45ca900a0239ad007242d1cb4b4167af842db666" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" dependencies = [ "proc-macro2", "quote", @@ -1681,140 +955,8 @@ dependencies = [ ] [[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustix" -version = "0.37.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bbfc1d1c7c40c01715f47d71444744a81669ca84e8b63e25a55e169b1f86433" -dependencies = [ - "bitflags", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustversion" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" - -[[package]] -name = "ryu" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" - -[[package]] -name = "schemars" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a5fb6c61f29e723026dc8e923d94c694313212abbecbbe5f55a7748eec5b307" -dependencies = [ - "dyn-clone", - "schemars_derive", - "serde", - "serde_json", -] - -[[package]] -name = "schemars_derive" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f188d036977451159430f3b8dc82ec76364a42b7e289c2b18a9a18f4470058e9" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn 1.0.109", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "seahash" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" - -[[package]] -name = "sec1" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" -dependencies = [ - "base16ct", - "der", - "generic-array", - "pkcs8", - "subtle", - "zeroize", -] - -[[package]] -name = "semver" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" - -[[package]] -name = "serde" -version = "1.0.136" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-json-wasm" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15bee9b04dd165c3f4e142628982ddde884c2022a89e8ddf99c4829bf2c3a58" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_bytes" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.136" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "serde_derive_internals" -version = "0.26.0" +name = "serde_derive_internals" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" dependencies = [ @@ -1868,18 +1010,6 @@ dependencies = [ "rand_core 0.6.3", ] -[[package]] -name = "simdutf8" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" - -[[package]] -name = "smallvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" - [[package]] name = "spki" version = "0.6.0" @@ -1890,12 +1020,6 @@ dependencies = [ "der", ] -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - [[package]] name = "static_assertions" version = "1.1.0" @@ -1930,25 +1054,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "target-lexicon" -version = "0.12.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd1ba337640d60c3e96bc6f0638a939b9c9a7f2c316a1598c279828b3d1dc8c5" - -[[package]] -name = "tempfile" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" -dependencies = [ - "cfg-if", - "fastrand", - "redox_syscall", - "rustix", - "windows-sys 0.45.0", -] - [[package]] name = "thiserror" version = "1.0.40" @@ -1969,39 +1074,6 @@ dependencies = [ "syn 2.0.11", ] -[[package]] -name = "tracing" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" -dependencies = [ - "cfg-if", - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.11", -] - -[[package]] -name = "tracing-core" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" -dependencies = [ - "once_cell", -] - [[package]] name = "typenum" version = "1.15.0" @@ -2044,536 +1116,6 @@ version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" -[[package]] -name = "wasm-bindgen" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 1.0.109", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" - -[[package]] -name = "wasmer" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea8d8361c9d006ea3d7797de7bd6b1492ffd0f91a22430cfda6c1658ad57bedf" -dependencies = [ - "cfg-if", - "indexmap", - "js-sys", - "loupe", - "more-asserts", - "target-lexicon", - "thiserror", - "wasm-bindgen", - "wasmer-artifact", - "wasmer-compiler", - "wasmer-compiler-cranelift", - "wasmer-compiler-singlepass", - "wasmer-derive", - "wasmer-engine", - "wasmer-engine-dylib", - "wasmer-engine-universal", - "wasmer-types", - "wasmer-vm", - "winapi", -] - -[[package]] -name = "wasmer-artifact" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aaf9428c29c1d8ad2ac0e45889ba8a568a835e33fd058964e5e500f2f7ce325" -dependencies = [ - "enumset", - "loupe", - "thiserror", - "wasmer-compiler", - "wasmer-types", -] - -[[package]] -name = "wasmer-compiler" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e67a6cd866aed456656db2cfea96c18baabbd33f676578482b85c51e1ee19d2c" -dependencies = [ - "enumset", - "loupe", - "rkyv", - "serde", - "serde_bytes", - "smallvec", - "target-lexicon", - "thiserror", - "wasmer-types", - "wasmparser", -] - -[[package]] -name = "wasmer-compiler-cranelift" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48be2f9f6495f08649e4f8b946a2cbbe119faf5a654aa1457f9504a99d23dae0" -dependencies = [ - "cranelift-codegen", - "cranelift-entity", - "cranelift-frontend", - "gimli 0.26.2", - "loupe", - "more-asserts", - "rayon", - "smallvec", - "target-lexicon", - "tracing", - "wasmer-compiler", - "wasmer-types", -] - -[[package]] -name = "wasmer-compiler-singlepass" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ca2a35204d8befa85062bc7aac259a8db8070b801b8a783770ba58231d729e" -dependencies = [ - "byteorder", - "dynasm", - "dynasmrt", - "gimli 0.26.2", - "lazy_static", - "loupe", - "more-asserts", - "rayon", - "smallvec", - "wasmer-compiler", - "wasmer-types", -] - -[[package]] -name = "wasmer-derive" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00e50405cc2a2f74ff574584710a5f2c1d5c93744acce2ca0866084739284b51" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "wasmer-engine" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f98f010978c244db431b392aeab0661df7ea0822343334f8f2a920763548e45" -dependencies = [ - "backtrace", - "enumset", - "lazy_static", - "loupe", - "memmap2", - "more-asserts", - "rustc-demangle", - "serde", - "serde_bytes", - "target-lexicon", - "thiserror", - "wasmer-artifact", - "wasmer-compiler", - "wasmer-types", - "wasmer-vm", -] - -[[package]] -name = "wasmer-engine-dylib" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0358af9c154724587731175553805648d9acb8f6657880d165e378672b7e53" -dependencies = [ - "cfg-if", - "enum-iterator", - "enumset", - "leb128", - "libloading", - "loupe", - "object 0.28.4", - "rkyv", - "serde", - "tempfile", - "tracing", - "wasmer-artifact", - "wasmer-compiler", - "wasmer-engine", - "wasmer-object", - "wasmer-types", - "wasmer-vm", - "which", -] - -[[package]] -name = "wasmer-engine-universal" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "440dc3d93c9ca47865a4f4edd037ea81bf983b5796b59b3d712d844b32dbef15" -dependencies = [ - "cfg-if", - "enumset", - "leb128", - "loupe", - "region", - "rkyv", - "wasmer-compiler", - "wasmer-engine", - "wasmer-engine-universal-artifact", - "wasmer-types", - "wasmer-vm", - "winapi", -] - -[[package]] -name = "wasmer-engine-universal-artifact" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f1db3f54152657eb6e86c44b66525ff7801dad8328fe677da48dd06af9ad41" -dependencies = [ - "enum-iterator", - "enumset", - "loupe", - "rkyv", - "thiserror", - "wasmer-artifact", - "wasmer-compiler", - "wasmer-types", -] - -[[package]] -name = "wasmer-middlewares" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7812438ed2f37203a37007cdb5332b8475cb2b16e15d51299b2647894e9ed3a" -dependencies = [ - "loupe", - "wasmer", - "wasmer-types", - "wasmer-vm", -] - -[[package]] -name = "wasmer-object" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d831335ff3a44ecf451303f6f891175c642488036b92ceceb24ac8623a8fa8b" -dependencies = [ - "object 0.28.4", - "thiserror", - "wasmer-compiler", - "wasmer-types", -] - -[[package]] -name = "wasmer-types" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39df01ea05dc0a9bab67e054c7cb01521e53b35a7bb90bd02eca564ed0b2667f" -dependencies = [ - "backtrace", - "enum-iterator", - "indexmap", - "loupe", - "more-asserts", - "rkyv", - "serde", - "thiserror", -] - -[[package]] -name = "wasmer-vm" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d965fa61f4dc4cdb35a54daaf7ecec3563fbb94154a6c35433f879466247dd" -dependencies = [ - "backtrace", - "cc", - "cfg-if", - "corosensei", - "enum-iterator", - "indexmap", - "lazy_static", - "libc", - "loupe", - "mach", - "memoffset 0.6.5", - "more-asserts", - "region", - "rkyv", - "scopeguard", - "serde", - "thiserror", - "wasmer-artifact", - "wasmer-types", - "winapi", -] - -[[package]] -name = "wasmparser" -version = "0.83.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718ed7c55c2add6548cca3ddd6383d738cd73b892df400e96b9aa876f0141d7a" - -[[package]] -name = "which" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" -dependencies = [ - "either", - "libc", - "once_cell", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43dbb096663629518eb1dfa72d80243ca5a6aca764cae62a2df70af760a9be75" -dependencies = [ - "windows_aarch64_msvc 0.33.0", - "windows_i686_gnu 0.33.0", - "windows_i686_msvc 0.33.0", - "windows_x86_64_gnu 0.33.0", - "windows_x86_64_msvc 0.33.0", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-targets" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" -dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd761fd3eb9ab8cc1ed81e56e567f02dd82c4c837e48ac3b2181b9ffc5060807" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - -[[package]] -name = "windows_i686_gnu" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab0cf703a96bab2dc0c02c0fa748491294bf9b7feb27e1f4f96340f208ada0e" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" - -[[package]] -name = "windows_i686_msvc" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cfdbe89cc9ad7ce618ba34abc34bbb6c36d99e96cae2245b7943cd75ee773d0" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4dd9b0c0e9ece7bb22e84d70d01b71c6d6248b81a3c60d11869451b4cb24784" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff1e4aa646495048ec7f3ffddc411e1d829c026a2ec62b39da15c1055e406eaa" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" - [[package]] name = "zeroize" version = "1.5.7" diff --git a/Cargo.toml b/Cargo.toml index 20494ae..1f043b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] -members = ["contracts/*", "packages/*"] +members = ["contracts/token", "contracts/hub", "contracts/hub-tf", "packages/*"] +resolver = '1' [profile.release.package.pfc-steak] opt-level = 3 @@ -9,6 +10,12 @@ codegen-units = 1 incremental = false [profile.release] +opt-level = 3 +debug = false rpath = false lto = true +debug-assertions = false +codegen-units = 1 +panic = 'abort' +incremental = false overflow-checks = true diff --git a/contracts/hub-tf/Cargo.toml b/contracts/hub-tf/Cargo.toml index f5d5369..4b068fa 100644 --- a/contracts/hub-tf/Cargo.toml +++ b/contracts/hub-tf/Cargo.toml @@ -14,7 +14,7 @@ backtraces = ["cosmwasm-std/backtraces"] [dependencies] #cosmwasm-std = { version = "1.2.1", features=["iterator","stargate","cosmwasm_1_1","staking"]} -cosmwasm-std = { version = "1.2.1", features=["iterator","stargate","staking"]} +cosmwasm-std = { version = "1.0.1", features=["iterator","stargate","staking"]} cw2= "1.0.1" #cw20 = "1.0.0" @@ -29,6 +29,7 @@ pfc-steak = { path = "../../packages/steak" } serde = { version = "1.0.103", default-features = false, features = ["derive"] } pfc-fee-split = "0.2.5" pfc-dust-collector="0.1.0" -#kujira = "0.7.25" +#{ path="../../packages/pfc-dust-collector" } osmosis-std-derive="0.13.2" +[dev-dependencies] protobuf = { version = "3.1.0", features = ["with-bytes"] } \ No newline at end of file diff --git a/contracts/hub-tf/src/contract.rs b/contracts/hub-tf/src/contract.rs index 8b47d08..06d94c6 100644 --- a/contracts/hub-tf/src/contract.rs +++ b/contracts/hub-tf/src/contract.rs @@ -1,11 +1,15 @@ -use cosmwasm_std::{Binary, Deps, DepsMut, entry_point, Env, MessageInfo, Reply, Response, StdError, StdResult, to_binary}; -use cw2::{ContractVersion, get_contract_version, set_contract_version}; +use cosmwasm_std::{ + entry_point, to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdError, + StdResult, +}; +use cw2::{get_contract_version, set_contract_version, ContractVersion}; +use std::convert::TryInto; use pfc_steak::hub::{CallbackMsg, MigrateMsg, QueryMsg}; use pfc_steak::hub_tf::{ExecuteMsg, InstantiateMsg, TokenFactoryType}; -use crate::{execute, queries}; use crate::state::State; +use crate::{execute, queries}; //use crate::helpers::{ unwrap_reply}; @@ -41,6 +45,7 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S .unwrap_or(info.sender), info.funds, exec_msg, + true, ), ExecuteMsg::Unbond { receiver } => execute::queue_unbond( deps, @@ -48,7 +53,8 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S receiver .map(|s| api.addr_validate(&s)) .transpose()? - .unwrap_or(info.sender), info.funds, + .unwrap_or(info.sender), + info.funds, ), ExecuteMsg::WithdrawUnbonded { receiver } => execute::withdraw_unbonded( deps, @@ -71,9 +77,10 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S ExecuteMsg::RemoveValidatorEx { validator } => { execute::remove_validator_ex(deps, env, info.sender, validator) } - ExecuteMsg::Redelegate { validator_from,validator_to } => { - execute::redelegate(deps, env, info.sender, validator_from,validator_to) - } + ExecuteMsg::Redelegate { + validator_from, + validator_to, + } => execute::redelegate(deps, env, info.sender, validator_from, validator_to), ExecuteMsg::TransferOwnership { new_owner } => { execute::transfer_ownership(deps, info.sender, new_owner) @@ -83,9 +90,10 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S ExecuteMsg::Rebalance { minimum } => execute::rebalance(deps, env, minimum), ExecuteMsg::Reconcile {} => execute::reconcile(deps, env), ExecuteMsg::SubmitBatch {} => execute::submit_batch(deps, env), - ExecuteMsg::TransferFeeAccount { fee_account_type, new_fee_account } => { - execute::transfer_fee_account(deps, info.sender, fee_account_type, new_fee_account) - } + ExecuteMsg::TransferFeeAccount { + fee_account_type, + new_fee_account, + } => execute::transfer_fee_account(deps, info.sender, fee_account_type, new_fee_account), ExecuteMsg::UpdateFee { new_fee } => execute::update_fee(deps, info.sender, new_fee), ExecuteMsg::Callback(callback_msg) => callback(deps, env, info, callback_msg), ExecuteMsg::PauseValidator { validator } => { @@ -94,15 +102,27 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S ExecuteMsg::UnPauseValidator { validator } => { execute::unpause_validator(deps, env, info.sender, validator) } - ExecuteMsg::SetUnbondPeriod { unbond_period } => execute::set_unbond_period(deps, env, info.sender, unbond_period), + ExecuteMsg::SetUnbondPeriod { unbond_period } => { + execute::set_unbond_period(deps, env, info.sender, unbond_period) + } - ExecuteMsg::SetDustCollector { dust_collector } => { execute::set_dust_collector(deps, env, info.sender, dust_collector) } - ExecuteMsg::CollectDust { max_tokens } => { execute::collect_dust(deps, env, max_tokens) } - ExecuteMsg::ReturnDenom {} => { execute::return_denom(deps, env, info.funds) } + ExecuteMsg::SetDustCollector { dust_collector } => { + execute::set_dust_collector(deps, env, info.sender, dust_collector) + } + ExecuteMsg::CollectDust { max_tokens } => { + let max_tokens_usize_r = max_tokens.try_into(); + if let Ok(max_tokens_usize) = max_tokens_usize_r { + execute::collect_dust(deps, env, max_tokens_usize) + } else { + Err(StdError::generic_err("max_tokens too large")) + } + } + ExecuteMsg::ReturnDenom {} => { + execute::bond(deps, env, info.sender, info.funds, None, false) + } } } - fn callback( deps: DepsMut, env: Env, @@ -121,21 +141,14 @@ fn callback( } #[entry_point] -pub fn reply(deps: DepsMut, env: Env, reply: Reply) -> StdResult { +pub fn reply(_deps: DepsMut, _env: Env, reply: Reply) -> StdResult { match reply.id { - REPLY_REGISTER_RECEIVED_COINS => { - execute::collect_dust(deps, env,10) - } - _ => { - Err(StdError::generic_err(format!( - "invalid reply id: {} {:?}", - reply.id, - reply.result - )) - ) - } + REPLY_REGISTER_RECEIVED_COINS => Ok(Response::default()), + _ => Err(StdError::generic_err(format!( + "invalid reply id: {} {:?}", + reply.id, reply.result + ))), } - } #[entry_point] @@ -186,16 +199,19 @@ pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult {} "3.0.1" | "3.0.2" => { - let state = State::default(); + let state = State::default(); let kuji = state.kuji_token_factory.load(deps.storage)?; if kuji { - state.token_factory_type.save(deps.storage,&TokenFactoryType::Kujira)? + state + .token_factory_type + .save(deps.storage, &TokenFactoryType::Kujira)? } else { - state.token_factory_type.save(deps.storage,&TokenFactoryType::CosmWasm)? + state + .token_factory_type + .save(deps.storage, &TokenFactoryType::CosmWasm)? } - } - _ => {} + _ => {} }, _ => { return Err(StdError::generic_err( diff --git a/contracts/hub-tf/src/execute.rs b/contracts/hub-tf/src/execute.rs index 1561ae7..0da980b 100644 --- a/contracts/hub-tf/src/execute.rs +++ b/contracts/hub-tf/src/execute.rs @@ -2,23 +2,27 @@ use std::collections::HashSet; use std::iter::FromIterator; use std::str::FromStr; -use cosmwasm_std::{Addr, BankMsg, Coin, CosmosMsg, Decimal, DepsMut, DistributionMsg, Env, Event, Order, Response, StdError, StdResult, SubMsg, to_binary, Uint128, WasmMsg}; - -use pfc_steak::DecimalCheckedOps; -use pfc_steak::hub::{ - Batch, CallbackMsg, FeeType, PendingBatch, UnbondRequest, +use cosmwasm_std::{ + to_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, DepsMut, DistributionMsg, Env, Event, + Order, ReplyOn, Response, StdError, StdResult, SubMsg, Uint128, WasmMsg, }; + +use pfc_steak::hub::{Batch, CallbackMsg, FeeType, PendingBatch, UnbondRequest}; use pfc_steak::hub_tf::{ExecuteMsg, InstantiateMsg, TokenFactoryType}; +use pfc_steak::DecimalCheckedOps; -use crate::{injective, token_factory}; use crate::contract::{REPLY_REGISTER_RECEIVED_COINS, SPECIAL_SEND_MESSAGE_TO_TRANSFER}; -use crate::helpers::{get_denom_balance, parse_received_fund, query_all_delegations, query_delegation, query_delegations}; +use crate::helpers::{ + get_denom_balance, parse_received_fund, query_all_delegations, query_delegation, + query_delegations, +}; use crate::kujira; use crate::math::{ compute_mint_amount, compute_redelegations_for_rebalancing, compute_redelegations_for_removal, compute_unbond_amount, compute_undelegations, reconcile_batches, }; -use crate::state::{previous_batches, State, unbond_requests, VALIDATORS, VALIDATORS_ACTIVE}; +use crate::state::{previous_batches, unbond_requests, State, VALIDATORS, VALIDATORS_ACTIVE}; +use crate::{injective, token_factory}; //use crate::token_factory::denom::{MsgBurn, MsgCreateDenom, MsgMint}; use crate::types::{Coins, Delegation, Redelegation}; @@ -64,15 +68,17 @@ pub fn instantiate(deps: DepsMut, env: Env, msg: InstantiateMsg) -> StdResult StdResult { >::into( - token_factory::denom::MsgCreateDenom { sender: env.contract.address.to_string(), subdenom: steak_denom_msg }) - } - TokenFactoryType::Kujira => { - >::into( - kujira::denom::MsgCreateDenom { sender: env.contract.address.to_string(), subdenom: steak_denom_msg }) - } - TokenFactoryType::Injective => { - >::into( - injective::denom::MsgCreateDenom { sender: env.contract.address.to_string(), subdenom: steak_denom_msg }) + token_factory::denom::MsgCreateDenom { + sender: env.contract.address.to_string(), + subdenom: steak_denom_msg, + }, + ) } + TokenFactoryType::Kujira => >::into( + kujira::denom::MsgCreateDenom { + sender: env.contract.address.to_string(), + subdenom: steak_denom_msg, + }, + ), + TokenFactoryType::Injective => >::into( + injective::denom::MsgCreateDenom { + sender: env.contract.address.to_string(), + subdenom: steak_denom_msg, + }, + ), }; Ok(Response::new().add_message(c)) } - //-------------------------------------------------------------------------------------------------- // Bonding and harvesting logics //-------------------------------------------------------------------------------------------------- @@ -115,7 +130,15 @@ pub fn instantiate(deps: DepsMut, env: Env, msg: InstantiateMsg) -> StdResult, bond_msg: Option) -> StdResult { +/// +pub fn bond( + deps: DepsMut, + env: Env, + receiver: Addr, + funds: Vec, + bond_msg: Option, + mint_steak: bool, +) -> StdResult { let state = State::default(); let denom = state.denom.load(deps.storage)?; let amount_to_bond = parse_received_fund(&funds, &denom)?; @@ -139,7 +162,12 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec, bond_msg: // Query the current delegations made to validators, and find the validator with the smallest // delegated amount through a linear search // The code for linear search is a bit uglier than using `sort_by` but cheaper: O(n) vs O(n * log(n)) - let delegations_non_active = query_delegations(&deps.querier, &non_active_validator_list, &env.contract.address, &denom)?; + let delegations_non_active = query_delegations( + &deps.querier, + &non_active_validator_list, + &env.contract.address, + &denom, + )?; let delegations = query_delegations(&deps.querier, &validators, &env.contract.address, &denom)?; let mut validator = &delegations[0].validator; @@ -155,60 +183,88 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec, bond_msg: amount: amount_to_bond.u128(), denom: denom.clone(), }; - - // Query the current supply of Steak and compute the amount to mint - // let usteak_supply = steak_minted; - let usteak_to_mint = compute_mint_amount(steak_minted, amount_to_bond, &delegations, &delegations_non_active); - state.steak_minted.save(deps.storage, &(steak_minted + usteak_to_mint))?; - // TODO deal with multiple token returns - state.prev_denom.save( - deps.storage, - &get_denom_balance(&deps.querier, env.contract.address.clone(), denom.clone())?, - )?; - - let delegate_submsg = SubMsg::reply_on_success( - new_delegation.to_cosmos_msg(), - REPLY_REGISTER_RECEIVED_COINS, - ); - - let mint_msg = match token_factory_type { - TokenFactoryType::CosmWasm => { - >::into(token_factory::denom::MsgMint { - sender: env.contract.address.to_string(), - amount: Some(token_factory::denom::Coin { - denom: steak_denom.clone(), - amount: usteak_to_mint.to_string(), - }), - }) - } - TokenFactoryType::Kujira => { - >::into(kujira::denom::MsgMint { - sender: env.contract.address.to_string(), - recipient: env.contract.address.to_string(), - amount: Some(kujira::denom::Coin { - denom: steak_denom.clone(), - amount: usteak_to_mint.to_string(), - }), - }) - } - TokenFactoryType::Injective => { - >::into(injective::denom::MsgMint { - sender: env.contract.address.to_string(), - amount: Some(injective::denom::Coin { - denom: steak_denom.clone(), - amount: usteak_to_mint.to_string(), - }), - }) - } + let delegate_submsg = SubMsg { + msg: new_delegation.to_cosmos_msg(), + id: REPLY_REGISTER_RECEIVED_COINS, + gas_limit: None, + reply_on: ReplyOn::Never, }; - let contract_info = deps.querier.query_wasm_contract_info(receiver.to_string()); + if mint_steak { + // Query the current supply of Steak and compute the amount to mint + // let usteak_supply = steak_minted; + let usteak_to_mint = compute_mint_amount( + steak_minted, + amount_to_bond, + &delegations, + &delegations_non_active, + ); + state + .steak_minted + .save(deps.storage, &(steak_minted + usteak_to_mint))?; + // TODO deal with multiple token returns + state.prev_denom.save( + deps.storage, + &get_denom_balance(&deps.querier, env.contract.address.clone(), denom.clone())?, + )?; + + let mint_msg = match token_factory_type { + TokenFactoryType::CosmWasm => >::into( + token_factory::denom::MsgMint { + sender: env.contract.address.to_string(), + amount: Some(token_factory::denom::Coin { + denom: steak_denom.clone(), + amount: usteak_to_mint.to_string(), + }), + }, + ), + TokenFactoryType::Kujira => { + >::into(kujira::denom::MsgMint { + sender: env.contract.address.to_string(), + recipient: env.contract.address.to_string(), + amount: Some(kujira::denom::Coin { + denom: steak_denom.clone(), + amount: usteak_to_mint.to_string(), + }), + }) + } + TokenFactoryType::Injective => { + >::into(injective::denom::MsgMint { + sender: env.contract.address.to_string(), + amount: Some(injective::denom::Coin { + denom: steak_denom.clone(), + amount: usteak_to_mint.to_string(), + }), + }) + } + }; - // send the uSteak, optionally calling a smart contract - let send_transfer_msg: CosmosMsg = match contract_info { - Ok(_) => { - if let Some(exec_msg) = bond_msg { - if exec_msg == SPECIAL_SEND_MESSAGE_TO_TRANSFER { // this is for backwards compatibility only + let contract_info = deps.querier.query_wasm_contract_info(receiver.to_string()); + + // send the uSteak, optionally calling a smart contract + let send_transfer_msg: CosmosMsg = match contract_info { + Ok(_) => { + if let Some(exec_msg) = bond_msg { + if exec_msg == SPECIAL_SEND_MESSAGE_TO_TRANSFER { + // this is for backwards compatibility only + CosmosMsg::Bank(BankMsg::Send { + to_address: receiver.to_string(), + amount: vec![Coin { + denom: steak_denom, + amount: usteak_to_mint, + }], + }) + } else { + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: receiver.to_string(), + msg: to_binary(&exec_msg)?, + funds: vec![Coin { + denom: steak_denom, + amount: usteak_to_mint, + }], + }) + } + } else { CosmosMsg::Bank(BankMsg::Send { to_address: receiver.to_string(), amount: vec![Coin { @@ -216,51 +272,44 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec, bond_msg: amount: usteak_to_mint, }], }) - } else { - CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: receiver.to_string(), - msg: to_binary(&exec_msg)?, - funds: vec![Coin { - denom: steak_denom, - amount: usteak_to_mint, - }], - }) } - } else { - CosmosMsg::Bank(BankMsg::Send { - to_address: receiver.to_string(), - amount: vec![Coin { - denom: steak_denom, - amount: usteak_to_mint, - }], - }) } - } - Err(_) => { - CosmosMsg::Bank(BankMsg::Send { + Err(_) => CosmosMsg::Bank(BankMsg::Send { to_address: receiver.to_string(), amount: vec![Coin { denom: steak_denom, amount: usteak_to_mint, }], - }) - } - }; + }), + }; - let event = Event::new("steakhub/bonded") - .add_attribute("time", env.block.time.seconds().to_string()) - .add_attribute("height", env.block.height.to_string()) - .add_attribute("steak_receiver", receiver) - .add_attribute("denom_bonded", denom) - .add_attribute("denom_amount", amount_to_bond) - .add_attribute("usteak_minted", usteak_to_mint); + let event = Event::new("steakhub/bonded") + .add_attribute("time", env.block.time.seconds().to_string()) + .add_attribute("height", env.block.height.to_string()) + .add_attribute("steak_receiver", receiver) + .add_attribute("denom_bonded", denom) + .add_attribute("denom_amount", amount_to_bond) + .add_attribute("usteak_minted", usteak_to_mint); - Ok(Response::new() - .add_submessage(delegate_submsg) - .add_messages(vec![mint_msg, send_transfer_msg]) - // .add_message(send_msg) - .add_event(event) - .add_attribute("action", "steakhub/bond")) + Ok(Response::new() + .add_submessage(delegate_submsg) + .add_messages(vec![mint_msg, send_transfer_msg]) + // .add_message(send_msg) + .add_event(event) + .add_attribute("action", "steakhub/bond")) + } else { + let event = Event::new("steakhub/bonded-nomint") + .add_attribute("time", env.block.time.seconds().to_string()) + .add_attribute("height", env.block.height.to_string()) + .add_attribute("from", receiver) + .add_attribute("denom_bonded", denom) + .add_attribute("denom_amount", amount_to_bond); + + Ok(Response::new() + .add_submessage(delegate_submsg) + .add_event(event) + .add_attribute("action", "steakhub/bond-nomint")) + } } pub fn harvest(deps: DepsMut, env: Env) -> StdResult { @@ -275,13 +324,13 @@ pub fn harvest(deps: DepsMut, env: Env) -> StdResult { .querier .query_all_delegations(&env.contract.address)? .into_iter() - .map(|d| { - SubMsg::reply_on_success( - CosmosMsg::Distribution(DistributionMsg::WithdrawDelegatorReward { - validator: d.validator, - }), - REPLY_REGISTER_RECEIVED_COINS, - ) + .map(|d| SubMsg { + id: REPLY_REGISTER_RECEIVED_COINS, + msg: CosmosMsg::Distribution(DistributionMsg::WithdrawDelegatorReward { + validator: d.validator, + }), + gas_limit: None, + reply_on: ReplyOn::Never, }) .collect::>(); @@ -362,11 +411,10 @@ pub fn reinvest(deps: DepsMut, env: Env) -> StdResult { let fee_type = state.fee_account_type.load(deps.storage)?; let send_msgs = match fee_type { - FeeType::Wallet => - vec![CosmosMsg::Bank(BankMsg::Send { - to_address: fee_account.to_string(), - amount: vec![Coin::new(fee_amount.into(), &denom)], - })], + FeeType::Wallet => vec![CosmosMsg::Bank(BankMsg::Send { + to_address: fee_account.to_string(), + amount: vec![Coin::new(fee_amount.into(), &denom)], + })], FeeType::FeeSplit => { let msg = pfc_fee_split::fee_split_msg::ExecuteMsg::Deposit { flush: false }; @@ -451,7 +499,11 @@ pub fn queue_unbond( let state = State::default(); let steak_denom = state.steak_denom.load(deps.storage)?; - let usteak_to_burn = funds.iter().filter(|p| p.denom == steak_denom).map(|steak_funds| steak_funds.amount).sum::(); + let usteak_to_burn = funds + .iter() + .filter(|p| p.denom == steak_denom) + .map(|steak_funds| steak_funds.amount) + .sum::(); if funds.len() != 1 || usteak_to_burn.is_zero() { return Err(StdError::generic_err(format!( "you can only send {} tokens to unbond", @@ -531,11 +583,20 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { // for unbonding we still need to look at // TODO verify denom let delegations = query_all_delegations(&deps.querier, &env.contract.address)?; - let delegations_active = query_delegations(&deps.querier, &active_validator_list, &env.contract.address, &denom)?; + let delegations_active = query_delegations( + &deps.querier, + &active_validator_list, + &env.contract.address, + &denom, + )?; // let usteak_supply = query_cw20_total_supply(&deps.querier, &steak_token)?; - let amount_to_unbond = - compute_unbond_amount(usteak_supply, pending_batch.usteak_to_burn, &delegations, &delegations_active); + let amount_to_unbond = compute_unbond_amount( + usteak_supply, + pending_batch.usteak_to_burn, + &delegations, + &delegations_active, + ); let new_undelegations = compute_undelegations(amount_to_unbond, &delegations, &denom); @@ -576,28 +637,34 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { let undelegate_submsgs = new_undelegations .iter() - .map(|d| SubMsg::reply_on_success(d.to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS)) + .map(|d| SubMsg { + id: REPLY_REGISTER_RECEIVED_COINS, + msg: d.to_cosmos_msg(), + gas_limit: None, + reply_on: ReplyOn::Never, + }) .collect::>(); - let burn_msg = match token_factory_type { - TokenFactoryType::Kujira => + TokenFactoryType::Kujira => { >::into(kujira::denom::MsgBurn { sender: env.contract.address.to_string(), amount: Some(kujira::denom::Coin { denom: steak_denom, amount: pending_batch.usteak_to_burn.to_string(), }), - }), - TokenFactoryType::CosmWasm => - >::into(token_factory::denom::MsgBurn { + }) + } + TokenFactoryType::CosmWasm => >::into( + token_factory::denom::MsgBurn { sender: env.contract.address.to_string(), amount: Some(token_factory::denom::Coin { denom: steak_denom, amount: pending_batch.usteak_to_burn.to_string(), }), - }), - TokenFactoryType::Injective => + }, + ), + TokenFactoryType::Injective => { >::into(injective::denom::MsgBurn { sender: env.contract.address.to_string(), amount: Some(injective::denom::Coin { @@ -605,10 +672,13 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { amount: pending_batch.usteak_to_burn.to_string(), }), }) + } }; // yes.. this will fail if supply is less than the amount to burn. this is intentional. - state.steak_minted.save(deps.storage, &(usteak_supply - pending_batch.usteak_to_burn))?; - + state.steak_minted.save( + deps.storage, + &(usteak_supply - pending_batch.usteak_to_burn), + )?; let event = Event::new("steakhub/unbond_submitted") .add_attribute("time", env.block.time.seconds().to_string()) @@ -745,11 +815,9 @@ pub fn withdraw_unbonded( if batch.total_shares.is_zero() { previous_batches().remove(deps.storage, request.id)?; } else { - previous_batches() - .save(deps.storage, batch.id, &batch)?; + previous_batches().save(deps.storage, batch.id, &batch)?; } - unbond_requests() - .remove(deps.storage, (request.id, user.as_ref()))?; + unbond_requests().remove(deps.storage, (request.id, user.as_ref()))?; } } } @@ -805,7 +873,12 @@ pub fn rebalance(deps: DepsMut, env: Env, minimum: Uint128) -> StdResult>(); let amount: u128 = new_redelegations.iter().map(|rd| rd.amount).sum(); @@ -871,7 +944,12 @@ pub fn remove_validator( let redelegate_submsgs = new_redelegations .iter() - .map(|d| SubMsg::reply_on_success(d.to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS)) + .map(|d| SubMsg { + id: REPLY_REGISTER_RECEIVED_COINS, + msg: d.to_cosmos_msg(), + gas_limit: None, + reply_on: ReplyOn::Never, + }) .collect::>(); let event = Event::new("steak/validator_removed").add_attribute("validator", validator); @@ -899,7 +977,6 @@ pub fn remove_validator_ex( } VALIDATORS.remove(deps.storage, &validator)?; - let event = Event::new("steak/validator_removed_ex").add_attribute("validator", validator); Ok(Response::new() @@ -1054,39 +1131,50 @@ pub fn set_dust_collector( ) -> StdResult { let state = State::default(); - state.assert_owner(deps.storage, &sender)?; if let Some(ref dust_addr) = dust_collector { - state.dust_collector.save(deps.storage, &Some(deps.api.addr_validate(dust_addr)?))?; + state + .dust_collector + .save(deps.storage, &Some(deps.api.addr_validate(dust_addr)?))?; } else { state.dust_collector.save(deps.storage, &None)?; }; - let event = Event::new("steak/set_dust_collector") - .add_attribute("dust_collector", dust_collector.unwrap_or("-cleared-".into())); + let event = Event::new("steak/set_dust_collector").add_attribute( + "dust_collector", + dust_collector.unwrap_or("-cleared-".into()), + ); Ok(Response::new() .add_event(event) .add_attribute("action", "steakhub/set_dust_collector")) } -pub fn collect_dust(deps: DepsMut, _env: Env, max_tokens: u64) -> StdResult { +pub fn collect_dust(deps: DepsMut, env: Env, max_tokens: usize) -> StdResult { let state = State::default(); - - if let Some(_dust_addr) = state.dust_collector.load(deps.storage)? { - Ok(Response::new().add_attribute("dust", "tbd")) - } else { - Ok(Response::new().add_attribute("dust", "dust-collector-called")) + let denom = state.denom.load(deps.storage)?; + let steak_denom = state.steak_denom.load(deps.storage)?; + let balances = deps.querier.query_all_balances(env.contract.address)?; + let balances_filtered = balances + .into_iter() + .filter(|p| !(p.denom == denom || p.denom == steak_denom)) + .take(max_tokens) + .collect::>(); + if balances_filtered.is_empty() { + return Ok(Response::new().add_attribute("dust", "no-dust")); } -} - -pub fn return_denom(deps: DepsMut, _env: Env, _funds: Vec) -> StdResult { - let state = State::default(); - - if let Some(_dust_addr) = state.dust_collector.load(deps.storage)? { - Ok(Response::new().add_attribute("dust", "returned")) + if let Some(dust_addr) = state.dust_collector.load(deps.storage)? { + let balances_count = balances_filtered.len(); + let msg = CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: dust_addr.to_string(), + funds: balances_filtered, + msg: to_binary(&pfc_dust_collector::dust_collector::ExecuteMsg::DustReceived {})?, + }); + Ok(Response::new() + .add_attribute("dust", format!("sent {} tokens", balances_count)) + .add_message(msg)) } else { - Err(StdError::generic_err("No Dust collector set")) + Ok(Response::new().add_attribute("dust", "dust-collector-called")) } } @@ -1102,21 +1190,26 @@ pub fn redelegate( state.assert_owner(deps.storage, &sender)?; let denom = state.denom.load(deps.storage)?; - let delegation = query_delegation(&deps.querier, &validator_from, &env.contract.address, &denom)?; - - let redelegation_msg = SubMsg::reply_on_success(Redelegation::new( + let delegation = query_delegation( + &deps.querier, &validator_from, - &validator_to, - delegation.amount, + &env.contract.address, &denom, - ).to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS); + )?; + + let redelegation_msg = SubMsg { + id: REPLY_REGISTER_RECEIVED_COINS, + msg: Redelegation::new(&validator_from, &validator_to, delegation.amount, &denom) + .to_cosmos_msg(), + gas_limit: None, + reply_on: ReplyOn::Never, + }; state.prev_denom.save( deps.storage, &get_denom_balance(&deps.querier, env.contract.address, denom)?, )?; - let event = Event::new("steak/redelegate") .add_attribute("validator_from", validator_from) .add_attribute("validator_to", validator_to) @@ -1126,4 +1219,4 @@ pub fn redelegate( .add_submessage(redelegation_msg) .add_event(event) .add_attribute("action", "steakhub/redelegate")) -} \ No newline at end of file +} diff --git a/contracts/hub-tf/src/testing/tests.rs b/contracts/hub-tf/src/testing/tests.rs index f838f88..9ebc0fd 100644 --- a/contracts/hub-tf/src/testing/tests.rs +++ b/contracts/hub-tf/src/testing/tests.rs @@ -1,21 +1,23 @@ use std::str::FromStr; -use cosmwasm_std::{Addr, BankMsg, Coin, CosmosMsg, Decimal, DistributionMsg, Order, OwnedDeps, ReplyOn, StdError, SubMsg, to_binary, Uint128, WasmMsg}; -use cosmwasm_std::testing::{MOCK_CONTRACT_ADDR, mock_env, mock_info, MockApi, MockStorage}; +use cosmwasm_std::testing::{mock_env, mock_info, MockApi, MockStorage, MOCK_CONTRACT_ADDR}; +use cosmwasm_std::{ + to_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, DistributionMsg, Order, OwnedDeps, ReplyOn, + StdError, SubMsg, Uint128, WasmMsg, +}; use pfc_steak::hub::{ - Batch, CallbackMsg, ConfigResponse, PendingBatch, QueryMsg, - StateResponse, UnbondRequest, UnbondRequestsByBatchResponseItem, - UnbondRequestsByUserResponseItem, + Batch, CallbackMsg, ConfigResponse, PendingBatch, QueryMsg, StateResponse, UnbondRequest, + UnbondRequestsByBatchResponseItem, UnbondRequestsByUserResponseItem, }; use pfc_steak::hub_tf::{ExecuteMsg, InstantiateMsg}; -use crate::contract::{execute, instantiate, REPLY_REGISTER_RECEIVED_COINS}; +use crate::contract::{execute, instantiate, REPLY_REGISTER_RECEIVED_COINS}; use crate::helpers::{parse_coin, parse_received_fund}; use crate::math::{ compute_redelegations_for_rebalancing, compute_redelegations_for_removal, compute_undelegations, }; -use crate::state::{previous_batches, State, unbond_requests, VALIDATORS}; +use crate::state::{previous_batches, unbond_requests, State, VALIDATORS}; use crate::token_factory::denom; use crate::token_factory::denom::{MsgBurn, MsgCreateDenom, MsgMint}; use crate::types::{Coins, Delegation, Redelegation, Undelegation}; @@ -51,15 +53,16 @@ fn setup_test() -> OwnedDeps { "charlie".to_string(), ], token_factory: "CosmWasm".to_string(), - dust_collector: Some("dusty_1".to_string()) + dust_collector: Some("dusty_1".to_string()), }, ) - .unwrap(); - + .unwrap(); assert_eq!(res.messages.len(), 1); - let c = >::into( - MsgCreateDenom { sender: "cosmos2contract".to_string(), subdenom: "boneXYZ".to_string() }); + let c = >::into(MsgCreateDenom { + sender: "cosmos2contract".to_string(), + subdenom: "boneXYZ".to_string(), + }); assert_eq!(res.messages[0], SubMsg::new(c)); deps @@ -80,8 +83,8 @@ fn setup_test_fee_split() -> OwnedDeps { fee_account: "fee_split_contract".to_string(), fee_amount: Decimal::from_ratio(10_u128, 100_u128), //10% max_fee_amount: Decimal::from_ratio(20_u128, 100_u128), //20% - epoch_period: 259200, // 3 * 24 * 60 * 60 = 3 days - unbond_period: 1814400, // 21 * 24 * 60 * 60 = 21 days + epoch_period: 259200, // 3 * 24 * 60 * 60 = 3 days + unbond_period: 1814400, // 21 * 24 * 60 * 60 = 21 days validators: vec![ "alice".to_string(), "bob".to_string(), @@ -89,14 +92,16 @@ fn setup_test_fee_split() -> OwnedDeps { ], token_factory: "CosmWasm".to_string(), - dust_collector: Some("dusty_2".to_string()) + dust_collector: Some("dusty_2".to_string()), }, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 1); - let c = >::into( - MsgCreateDenom { sender: "cosmos2contract".to_string(), subdenom: "sXYZ".to_string() }); + let c = >::into(MsgCreateDenom { + sender: "cosmos2contract".to_string(), + subdenom: "sXYZ".to_string(), + }); assert_eq!(res.messages[0], SubMsg::new(c)); deps @@ -130,7 +135,7 @@ fn proper_instantiation() { "charlie".to_string(), ], paused_validators: vec![], - dust_collector:Some("dusty_1".to_string()), + dust_collector: Some("dusty_1".to_string()), token_factory: Some("CosmWasm".to_string()) } ); @@ -193,9 +198,12 @@ fn bonding() { deps.as_mut(), mock_env(), mock_info("user_1", &[Coin::new(1_000_000, "uxyz")]), - ExecuteMsg::Bond { receiver: None, exec_msg: None }, + ExecuteMsg::Bond { + receiver: None, + exec_msg: None, + }, ) - .unwrap(); + .unwrap(); // 3 messages. (switched to 3 so we can 'send' instead of 'transfer' minted tokens, so contract will know about it // 1 - delegate @@ -204,7 +212,12 @@ fn bonding() { assert_eq!(res.messages.len(), 3); assert_eq!( res.messages[0], - SubMsg::reply_on_success(Delegation::new("alice", 1_000_000, "uxyz").to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS) + SubMsg { + msg: Delegation::new("alice", 1_000_000, "uxyz").to_cosmos_msg(), + gas_limit: None, + id: REPLY_REGISTER_RECEIVED_COINS, + reply_on: ReplyOn::Never, + } ); let mint_msg = >::into(MsgMint { sender: "cosmos2contract".to_string(), @@ -229,14 +242,16 @@ fn bonding() { id: 0, msg: CosmosMsg::Bank(BankMsg::Send { to_address: "user_1".to_string(), - amount: vec![Coin { denom: "factory/cosmos2contract/boneXYZ".to_string(), amount: Uint128::new(1_000_000) }], + amount: vec![Coin { + denom: "factory/cosmos2contract/boneXYZ".to_string(), + amount: Uint128::new(1_000_000) + }], }), gas_limit: None, reply_on: ReplyOn::Never, } ); - // Bond when there are existing delegations, and Luna:Steak exchange rate is >1 // Previously user 1 delegated 1,000,000 uluna. We assume we have accumulated 2.5% yield at 1025000 staked deps.querier.set_staking_delegations(&[ @@ -255,12 +270,17 @@ fn bonding() { exec_msg: None, }, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 3); assert_eq!( res.messages[0], - SubMsg::reply_on_success(Delegation::new("charlie", 12345, "uxyz").to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS) + SubMsg { + msg: Delegation::new("charlie", 12345, "uxyz").to_cosmos_msg(), + id: REPLY_REGISTER_RECEIVED_COINS, + gas_limit: None, + reply_on: ReplyOn::Never + } ); let mint_msg = >::into(MsgMint { sender: "cosmos2contract".to_string(), @@ -286,7 +306,10 @@ fn bonding() { msg: CosmosMsg::Bank(BankMsg::Send { to_address: "user_3".to_string(), - amount: vec![Coin { denom: "factory/cosmos2contract/boneXYZ".to_string(), amount: Uint128::new(12_043) }], + amount: vec![Coin { + denom: "factory/cosmos2contract/boneXYZ".to_string(), + amount: Uint128::new(12_043) + }], }), gas_limit: None, reply_on: ReplyOn::Never, @@ -323,42 +346,47 @@ fn harvesting() { Delegation::new("charlie", 341666, "uxyz"), ]); - let res = execute( deps.as_mut(), mock_env(), mock_info("worker", &[]), ExecuteMsg::Harvest {}, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 4); assert_eq!( res.messages[0], - SubMsg::reply_on_success( - CosmosMsg::Distribution(DistributionMsg::WithdrawDelegatorReward { + SubMsg { + msg: CosmosMsg::Distribution(DistributionMsg::WithdrawDelegatorReward { validator: "alice".to_string(), }), - REPLY_REGISTER_RECEIVED_COINS, - ) + gas_limit: None, + id: REPLY_REGISTER_RECEIVED_COINS, + reply_on: ReplyOn::Never, + } ); assert_eq!( res.messages[1], - SubMsg::reply_on_success( - CosmosMsg::Distribution(DistributionMsg::WithdrawDelegatorReward { + SubMsg { + msg: CosmosMsg::Distribution(DistributionMsg::WithdrawDelegatorReward { validator: "bob".to_string(), }), - REPLY_REGISTER_RECEIVED_COINS, - ) + id: REPLY_REGISTER_RECEIVED_COINS, + gas_limit: None, + reply_on: ReplyOn::Never + } ); assert_eq!( res.messages[2], - SubMsg::reply_on_success( - CosmosMsg::Distribution(DistributionMsg::WithdrawDelegatorReward { + SubMsg { + msg: CosmosMsg::Distribution(DistributionMsg::WithdrawDelegatorReward { validator: "charlie".to_string(), }), - REPLY_REGISTER_RECEIVED_COINS, - ) + id: REPLY_REGISTER_RECEIVED_COINS, + gas_limit: None, + reply_on: ReplyOn::Never + } ); assert_eq!( res.messages[3], @@ -375,7 +403,6 @@ fn harvesting() { ); } - #[test] fn reinvesting() { let mut deps = setup_test(); @@ -386,8 +413,12 @@ fn reinvesting() { Delegation::new("bob", 333333, "uxyz"), Delegation::new("charlie", 333333, "uxyz"), ]); - state.prev_denom.save(deps.as_mut().storage, &Uint128::from(0 as u32)).unwrap(); - deps.querier.set_bank_balances(&[Coin::new(234u128, "uxyz")]); + state + .prev_denom + .save(deps.as_mut().storage, &Uint128::from(0 as u32)) + .unwrap(); + deps.querier + .set_bank_balances(&[Coin::new(234u128, "uxyz")]); // After the swaps, `unlocked_coins` should contain only uxyz and unknown denoms state @@ -411,7 +442,7 @@ fn reinvesting() { mock_info(MOCK_CONTRACT_ADDR, &[]), ExecuteMsg::Callback(CallbackMsg::Reinvest {}), ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 2); assert_eq!( @@ -458,8 +489,12 @@ fn reinvesting_fee_split() { Delegation::new("bob", 333333, "uxyz"), Delegation::new("charlie", 333333, "uxyz"), ]); - state.prev_denom.save(deps.as_mut().storage, &Uint128::from(0 as u32)).unwrap(); - deps.querier.set_bank_balances(&[Coin::new(234u128, "uxyz")]); + state + .prev_denom + .save(deps.as_mut().storage, &Uint128::from(0 as u32)) + .unwrap(); + deps.querier + .set_bank_balances(&[Coin::new(234u128, "uxyz")]); // After the swaps, `unlocked_coins` should contain only uxyz and unknown denoms state @@ -483,7 +518,7 @@ fn reinvesting_fee_split() { mock_info(MOCK_CONTRACT_ADDR, &[]), ExecuteMsg::Callback(CallbackMsg::Reinvest {}), ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 2); assert_eq!( @@ -501,7 +536,9 @@ fn reinvesting_fee_split() { res.messages[1], SubMsg { id: 0, - msg: send_msg.into_cosmos_msg("fee_split_contract", vec![Coin::new(23u128, "uxyz")]).unwrap(), + msg: send_msg + .into_cosmos_msg("fee_split_contract", vec![Coin::new(23u128, "uxyz")]) + .unwrap(), gas_limit: None, reply_on: ReplyOn::Never, } @@ -527,10 +564,16 @@ fn queuing_unbond() { let err = execute( deps.as_mut(), mock_env(), - mock_info("random_token", &[Coin { denom: "asldkj".into(), amount: Uint128::new(69_420) }]), + mock_info( + "random_token", + &[Coin { + denom: "asldkj".into(), + amount: Uint128::new(69_420), + }], + ), ExecuteMsg::Unbond { receiver: None }, ) - .unwrap_err(); + .unwrap_err(); assert_eq!( err, @@ -542,12 +585,18 @@ fn queuing_unbond() { let res = execute( deps.as_mut(), mock_env_at_timestamp(12345), // est_unbond_start_time = 269200 - mock_info("steak_token", &[Coin { denom: "factory/cosmos2contract/boneXYZ".into(), amount: Uint128::new(23_456) }]), + mock_info( + "steak_token", + &[Coin { + denom: "factory/cosmos2contract/boneXYZ".into(), + amount: Uint128::new(23_456), + }], + ), ExecuteMsg::Unbond { receiver: Some("user_1".to_string()), }, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 0); @@ -556,10 +605,16 @@ fn queuing_unbond() { let res = execute( deps.as_mut(), mock_env_at_timestamp(269201), // est_unbond_start_time = 269200 - mock_info("user_3", &[Coin { denom: "factory/cosmos2contract/boneXYZ".into(), amount: Uint128::new(69_420) }]), + mock_info( + "user_3", + &[Coin { + denom: "factory/cosmos2contract/boneXYZ".into(), + amount: Uint128::new(69_420), + }], + ), ExecuteMsg::Unbond { receiver: None }, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 1); assert_eq!( @@ -632,8 +687,10 @@ fn submitting_batch() { Delegation::new("bob", 345_782, "uxyz"), Delegation::new("charlie", 345_781, "uxyz"), ]); - state.steak_minted.save(deps.as_mut().storage, &Uint128::new(1_012_043)).unwrap(); - + state + .steak_minted + .save(deps.as_mut().storage, &Uint128::new(1_012_043)) + .unwrap(); // We continue from the contract state at the end of the last test let unbond_reqs = vec![ @@ -691,23 +748,35 @@ fn submitting_batch() { mock_info(MOCK_CONTRACT_ADDR, &[]), ExecuteMsg::SubmitBatch {}, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 4); assert_eq!( res.messages[0], - SubMsg::reply_on_success(Undelegation::new("alice", 31732, "uxyz").to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS) + SubMsg { + msg: Undelegation::new("alice", 31732, "uxyz").to_cosmos_msg(), + id: REPLY_REGISTER_RECEIVED_COINS, + gas_limit: None, + reply_on: ReplyOn::Never + } ); assert_eq!( res.messages[1], - SubMsg::reply_on_success(Undelegation::new("bob", 31733, "uxyz").to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS) + SubMsg { + msg: Undelegation::new("bob", 31733, "uxyz").to_cosmos_msg(), + id: REPLY_REGISTER_RECEIVED_COINS, + gas_limit: None, + reply_on: ReplyOn::Never + } ); assert_eq!( res.messages[2], - SubMsg::reply_on_success( - Undelegation::new("charlie", 31732, "uxyz").to_cosmos_msg(), - REPLY_REGISTER_RECEIVED_COINS, - ) + SubMsg { + msg: Undelegation::new("charlie", 31732, "uxyz").to_cosmos_msg(), + id: REPLY_REGISTER_RECEIVED_COINS, + gas_limit: None, + reply_on: ReplyOn::Never + } ); let burn_msg = >::into(MsgBurn { @@ -719,8 +788,7 @@ fn submitting_batch() { }); assert_eq!( res.messages[3], - - SubMsg { + SubMsg { id: 0, msg: burn_msg, gas_limit: None, @@ -833,7 +901,7 @@ fn reconciling() { mock_info("worker", &[]), ExecuteMsg::Reconcile {}, ) - .unwrap(); + .unwrap(); // Expected received: batch 2 + batch 3 = 1385 + 1506 = 2891 // Expected unlocked: 10000 @@ -995,7 +1063,7 @@ fn withdrawing_unbonded() { mock_info("user_1", &[]), ExecuteMsg::WithdrawUnbonded { receiver: None }, ) - .unwrap_err(); + .unwrap_err(); assert_eq!(err, StdError::generic_err("withdrawable amount is zero")); @@ -1016,7 +1084,7 @@ fn withdrawing_unbonded() { mock_info("user_1", &[]), ExecuteMsg::WithdrawUnbonded { receiver: None }, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 1); assert_eq!( @@ -1093,7 +1161,7 @@ fn withdrawing_unbonded() { receiver: Some("user_2".to_string()), }, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 1); assert_eq!( @@ -1148,7 +1216,7 @@ fn adding_validator() { validator: "dave".to_string(), }, ) - .unwrap_err(); + .unwrap_err(); assert_eq!( err, @@ -1163,7 +1231,7 @@ fn adding_validator() { validator: "alice".to_string(), }, ) - .unwrap_err(); + .unwrap_err(); assert_eq!( err, @@ -1178,7 +1246,7 @@ fn adding_validator() { validator: "dave".to_string(), }, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 0); @@ -1208,7 +1276,7 @@ fn removing_validator() { validator: "charlie".to_string(), }, ) - .unwrap_err(); + .unwrap_err(); assert_eq!( err, @@ -1223,7 +1291,7 @@ fn removing_validator() { validator: "dave".to_string(), }, ) - .unwrap_err(); + .unwrap_err(); assert_eq!( err, @@ -1242,22 +1310,26 @@ fn removing_validator() { validator: "charlie".to_string(), }, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 2); assert_eq!( res.messages[0], - SubMsg::reply_on_success( - Redelegation::new("charlie", "alice", 170833, "uxyz").to_cosmos_msg(), - REPLY_REGISTER_RECEIVED_COINS, - ), + SubMsg { + msg: Redelegation::new("charlie", "alice", 170833, "uxyz").to_cosmos_msg(), + id: REPLY_REGISTER_RECEIVED_COINS, + gas_limit: None, + reply_on: ReplyOn::Never + }, ); assert_eq!( res.messages[1], - SubMsg::reply_on_success( - Redelegation::new("charlie", "bob", 170833, "uxyz").to_cosmos_msg(), - REPLY_REGISTER_RECEIVED_COINS, - ), + SubMsg { + msg: Redelegation::new("charlie", "bob", 170833, "uxyz").to_cosmos_msg(), + id: REPLY_REGISTER_RECEIVED_COINS, + gas_limit: None, + reply_on: ReplyOn::Never + }, ); assert!(VALIDATORS.contains(deps.as_ref().storage, "alice")); @@ -1278,7 +1350,7 @@ fn transferring_ownership() { new_owner: "jake".to_string(), }, ) - .unwrap_err(); + .unwrap_err(); assert_eq!( err, @@ -1293,7 +1365,7 @@ fn transferring_ownership() { new_owner: "jake".to_string(), }, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 0); @@ -1306,7 +1378,7 @@ fn transferring_ownership() { mock_info("pumpkin", &[]), ExecuteMsg::AcceptOwnership {}, ) - .unwrap_err(); + .unwrap_err(); assert_eq!( err, @@ -1319,7 +1391,7 @@ fn transferring_ownership() { mock_info("jake", &[]), ExecuteMsg::AcceptOwnership {}, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 0); @@ -1331,7 +1403,6 @@ fn transferring_ownership() { fn splitting_fees() { let mut deps = setup_test(); - let err = execute( deps.as_mut(), mock_env(), @@ -1341,7 +1412,7 @@ fn splitting_fees() { new_fee_account: "charlie".to_string(), }, ) - .unwrap_err(); + .unwrap_err(); assert_eq!( err, @@ -1357,7 +1428,7 @@ fn splitting_fees() { new_fee_account: "charlie".to_string(), }, ) - .unwrap_err(); + .unwrap_err(); assert_eq!( err, @@ -1373,7 +1444,7 @@ fn splitting_fees() { new_fee_account: "charlie".to_string(), }, ) - .unwrap(); + .unwrap(); let res: ConfigResponse = query_helper(deps.as_ref(), QueryMsg::Config {}); assert_eq!( res, @@ -1394,12 +1465,11 @@ fn splitting_fees() { "charlie".to_string(), ], paused_validators: vec![], - dust_collector:Some("dusty_1".to_string()), + dust_collector: Some("dusty_1".to_string()), token_factory: Some("CosmWasm".to_string()) } ); - execute( deps.as_mut(), mock_env(), @@ -1409,7 +1479,7 @@ fn splitting_fees() { new_fee_account: "contract".to_string(), }, ) - .unwrap(); + .unwrap(); let res: ConfigResponse = query_helper(deps.as_ref(), QueryMsg::Config {}); assert_eq!( res, @@ -1625,17 +1695,14 @@ fn querying_unbond_requests() { ); assert_eq!( res, - vec![ - unbond_reqs[0].clone().into(), - unbond_reqs[3].clone().into(), - ] + vec![unbond_reqs[0].clone().into(), unbond_reqs[3].clone().into(),] ); /* - for x in unbond_requests().range(deps.as_ref().storage, None, None, Order::Ascending) { - let rec = x.unwrap(); - eprintln!("Key {}/{} = {:?}", rec.0.0, rec.0.1, rec.1) - } -*/ + for x in unbond_requests().range(deps.as_ref().storage, None, None, Order::Ascending) { + let rec = x.unwrap(); + eprintln!("Key {}/{} = {:?}", rec.0.0, rec.0.1, rec.1) + } + */ let res: Vec = query_helper( deps.as_ref(), QueryMsg::UnbondRequestsByUser { @@ -1713,9 +1780,13 @@ fn computing_redelegations_for_rebalancing() { Delegation::new("dave", 40471, "uxyz"), Delegation::new("evan", 2345, "uxyz"), ]; - let active_validators: Vec = vec!["alice".to_string(), "bob".to_string(), - "charlie".to_string(), "dave".to_string(), - "evan".to_string()]; + let active_validators: Vec = vec![ + "alice".to_string(), + "bob".to_string(), + "charlie".to_string(), + "dave".to_string(), + "evan".to_string(), + ]; // uluna_per_validator = (69420 + 88888 + 1234 + 40471 + 2345) / 4 = 40471 // remainer = 3 // src_delegations: @@ -1747,15 +1818,20 @@ fn computing_redelegations_for_rebalancing() { ]; assert_eq!( - compute_redelegations_for_rebalancing(active_validators, ¤t_delegations, Uint128::from(10 as u64)), + compute_redelegations_for_rebalancing( + active_validators, + ¤t_delegations, + Uint128::from(10 as u64) + ), expected, ); - - let partially_active = vec!["alice".to_string(), - "charlie".to_string(), - "dave".to_string(), - "evan".to_string()]; + let partially_active = vec![ + "alice".to_string(), + "charlie".to_string(), + "dave".to_string(), + "evan".to_string(), + ]; let partially_expected = vec![ Redelegation::new("alice", "dave", 10118, "uxyz"), @@ -1763,7 +1839,11 @@ fn computing_redelegations_for_rebalancing() { Redelegation::new("charlie", "evan", 38299, "uxyz"), ]; assert_eq!( - compute_redelegations_for_rebalancing(partially_active.clone(), ¤t_delegations, Uint128::from(10 as u64)), + compute_redelegations_for_rebalancing( + partially_active.clone(), + ¤t_delegations, + Uint128::from(10 as u64) + ), partially_expected, ); @@ -1772,7 +1852,11 @@ fn computing_redelegations_for_rebalancing() { Redelegation::new("charlie", "evan", 29414, "uxyz"), ]; assert_eq!( - compute_redelegations_for_rebalancing(partially_active, ¤t_delegations, Uint128::from(15_000 as u64)), + compute_redelegations_for_rebalancing( + partially_active, + ¤t_delegations, + Uint128::from(15_000 as u64) + ), partially_expected_minimums, ); } @@ -1860,7 +1944,7 @@ fn receiving_funds() { &[Coin::new(12345, "uatom"), Coin::new(23456, "uxyz")], "uxyz", ) - .unwrap_err(); + .unwrap_err(); assert_eq!( err, StdError::generic_err("must deposit exactly one coin; received 2") @@ -1882,7 +1966,6 @@ fn receiving_funds() { assert_eq!(amount, Uint128::new(69420)); } - #[test] fn reconciling_underflow() { let mut deps = setup_test(); @@ -1941,7 +2024,10 @@ fn reconciling_underflow() { Coin::new(12345, "uatom"), Coin::new(234, "ukrw"), Coin::new(345, "uusd"), - Coin::new(69420, "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B"), + Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + ), ]); execute( deps.as_mut(), @@ -1949,7 +2035,7 @@ fn reconciling_underflow() { mock_info("worker", &[]), ExecuteMsg::Reconcile {}, ) - .unwrap(); + .unwrap(); } #[test] @@ -2010,7 +2096,10 @@ fn reconciling_underflow_second() { Coin::new(12345 - 1323, "uatom"), Coin::new(234, "ukrw"), Coin::new(345, "uusd"), - Coin::new(69420, "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B"), + Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + ), ]); execute( deps.as_mut(), @@ -2018,5 +2107,97 @@ fn reconciling_underflow_second() { mock_info("worker", &[]), ExecuteMsg::Reconcile {}, ) - .unwrap(); -} \ No newline at end of file + .unwrap(); +} +#[test] +fn dust_return_denom() { + let mut deps = setup_test(); + + // Bond when no delegation has been made + // In this case, the full deposit simply goes to the first validator + let res = execute( + deps.as_mut(), + mock_env(), + mock_info("user_1", &[Coin::new(1_000_000, "uxyz")]), + ExecuteMsg::Bond { + receiver: None, + exec_msg: None, + }, + ) + .unwrap(); + + // 3 messages. (switched to 3 so we can 'send' instead of 'transfer' minted tokens, so contract will know about it + // 1 - delegate + // 2 - mint token (to ourselves) + // 3 - send/transfer it + assert_eq!(res.messages.len(), 3); + assert_eq!( + res.messages[0], + SubMsg { + msg: Delegation::new("alice", 1_000_000, "uxyz").to_cosmos_msg(), + id: REPLY_REGISTER_RECEIVED_COINS, + gas_limit: None, + reply_on: ReplyOn::Never + } + ); + let mint_msg = >::into(MsgMint { + sender: "cosmos2contract".to_string(), + amount: Some(denom::Coin { + denom: "factory/cosmos2contract/boneXYZ".to_string(), + amount: Uint128::new(1_000_000).to_string(), + }), + }); + assert_eq!( + res.messages[1], + SubMsg { + id: 0, + msg: mint_msg, + gas_limit: None, + reply_on: ReplyOn::Never, + } + ); + + assert_eq!( + res.messages[2], + SubMsg { + id: 0, + msg: CosmosMsg::Bank(BankMsg::Send { + to_address: "user_1".to_string(), + amount: vec![Coin { + denom: "factory/cosmos2contract/boneXYZ".to_string(), + amount: Uint128::new(1_000_000) + }], + }), + gas_limit: None, + reply_on: ReplyOn::Never, + } + ); + + // Bond when there are existing delegations, and Luna:Steak exchange rate is >1 + // Previously user 1 delegated 1,000,000 uluna. We assume we have accumulated 2.5% yield at 1025000 staked + deps.querier.set_staking_delegations(&[ + Delegation::new("alice", 341667, "uxyz"), + Delegation::new("bob", 341667, "uxyz"), + Delegation::new("charlie", 341666, "uxyz"), + ]); + + // Charlie has the smallest amount of delegation, so the full deposit goes to him + let res = execute( + deps.as_mut(), + mock_env(), + mock_info("user_2", &[Coin::new(12345, "uxyz")]), + ExecuteMsg::ReturnDenom {}, + ) + .unwrap(); + eprintln!("{:?}", res.messages); + assert_eq!(res.messages.len(), 1); + assert_eq!( + res.messages[0], + SubMsg { + msg: Delegation::new("charlie", 12345, "uxyz").to_cosmos_msg(), + id: REPLY_REGISTER_RECEIVED_COINS, + gas_limit: None, + reply_on: ReplyOn::Never + } + ); +} diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index 1820cf7..58c5ffd 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -21,7 +21,7 @@ cw-storage-plus = "0.13" pfc-steak = { path = "../../packages/steak" } serde = { version = "1.0.103", default-features = false, features = ["derive"] } pfc-fee-split = "0.2.3" -pfc-dust-collector = "0.1.0" +#pfc-dust-collector = {path = "../../packages/pfc-dust-collector"} funds-distributor-api = {git="https://github.com/terra-money/enterprise-contracts.git" , branch="main"} #[dev-dependencies] #serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/contracts/hub/src/contract.rs b/contracts/hub/src/contract.rs index 28967d9..8842049 100644 --- a/contracts/hub/src/contract.rs +++ b/contracts/hub/src/contract.rs @@ -1,16 +1,19 @@ use cosmwasm_std::{ - Binary, Decimal, Deps, DepsMut, entry_point, Env, from_binary, MessageInfo, Reply, Response, - StdError, StdResult, to_binary, + entry_point, from_binary, to_binary, Binary, Decimal, Deps, DepsMut, Env, MessageInfo, Reply, + Response, StdError, StdResult, }; -use cw2::{ContractVersion, get_contract_version, set_contract_version}; +use cw2::{get_contract_version, set_contract_version, ContractVersion}; use cw20::Cw20ReceiveMsg; +use std::convert::TryInto; -use pfc_steak::hub::{CallbackMsg, ExecuteMsg, FeeType, InstantiateMsg, MigrateMsg, QueryMsg, ReceiveMsg}; +use pfc_steak::hub::{ + CallbackMsg, ExecuteMsg, FeeType, InstantiateMsg, MigrateMsg, QueryMsg, ReceiveMsg, +}; -use crate::{execute, queries}; use crate::helpers::{get_denom_balance, unwrap_reply}; use crate::migrations::ConfigV100; use crate::state::State; +use crate::{execute, queries}; /// Contract name that is used for migration. pub const CONTRACT_NAME: &str = "steak-hub"; @@ -45,9 +48,9 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S .unwrap_or(info.sender), info.funds, exec_msg, - false + false, ), - ExecuteMsg::BondEx { receiver, } => execute::bond( + ExecuteMsg::BondEx { receiver } => execute::bond( deps, env, receiver @@ -56,7 +59,7 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S .unwrap_or(info.sender), info.funds, None, - true + true, ), ExecuteMsg::WithdrawUnbonded { receiver } => execute::withdraw_unbonded( deps, @@ -79,9 +82,10 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S ExecuteMsg::RemoveValidatorEx { validator } => { execute::remove_validator_ex(deps, env, info.sender, validator) } - ExecuteMsg::Redelegate { validator_from,validator_to } => { - execute::redelegate(deps, env, info.sender, validator_from,validator_to) - } + ExecuteMsg::Redelegate { + validator_from, + validator_to, + } => execute::redelegate(deps, env, info.sender, validator_from, validator_to), ExecuteMsg::TransferOwnership { new_owner } => { execute::transfer_ownership(deps, info.sender, new_owner) } @@ -90,9 +94,10 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S ExecuteMsg::Rebalance { minimum } => execute::rebalance(deps, env, minimum), ExecuteMsg::Reconcile {} => execute::reconcile(deps, env), ExecuteMsg::SubmitBatch {} => execute::submit_batch(deps, env), - ExecuteMsg::TransferFeeAccount { fee_account_type, new_fee_account } => { - execute::transfer_fee_account(deps, info.sender, fee_account_type, new_fee_account) - } + ExecuteMsg::TransferFeeAccount { + fee_account_type, + new_fee_account, + } => execute::transfer_fee_account(deps, info.sender, fee_account_type, new_fee_account), ExecuteMsg::UpdateFee { new_fee } => execute::update_fee(deps, info.sender, new_fee), ExecuteMsg::Callback(callback_msg) => callback(deps, env, info, callback_msg), ExecuteMsg::PauseValidator { validator } => { @@ -101,10 +106,21 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S ExecuteMsg::UnPauseValidator { validator } => { execute::unpause_validator(deps, env, info.sender, validator) } - ExecuteMsg::SetUnbondPeriod { unbond_period } => execute::set_unbond_period(deps, env, info.sender, unbond_period), - ExecuteMsg::SetDustCollector { dust_collector } => { execute::set_dust_collector(deps, env, info.sender, dust_collector) } - ExecuteMsg::CollectDust {} => { execute::collect_dust(deps, env) } - ExecuteMsg::ReturnDenom {} => { execute::return_denom(deps, env, info.funds) } + ExecuteMsg::SetUnbondPeriod { unbond_period } => { + execute::set_unbond_period(deps, env, info.sender, unbond_period) + } + ExecuteMsg::SetDustCollector { dust_collector } => { + execute::set_dust_collector(deps, env, info.sender, dust_collector) + } + ExecuteMsg::CollectDust { max_tokens } => { + let max_tokens_usize_r = max_tokens.try_into(); + if let Ok(max_tokens_usize) = max_tokens_usize_r { + execute::collect_dust(deps, env, max_tokens_usize) + } else { + Err(StdError::generic_err("max_tokens too large")) + } + } + ExecuteMsg::ReturnDenom {} => execute::return_denom(deps, env, info.funds), } } @@ -221,20 +237,26 @@ pub fn migrate(deps: DepsMut, env: Env, _msg: MigrateMsg) -> StdResult state.fee_account.save(deps.storage, &owner)?; state.max_fee_rate.save(deps.storage, &Decimal::zero())?; state.fee_rate.save(deps.storage, &Decimal::zero())?; - state.fee_account_type.save(deps.storage, &FeeType::Wallet)?; + state + .fee_account_type + .save(deps.storage, &FeeType::Wallet)?; ConfigV100::upgrade_stores(deps.storage, &deps.querier, env.contract.address)?; state.dust_collector.save(deps.storage, &None)?; } "2.1.4" => { let state = State::default(); ConfigV100::upgrade_stores(deps.storage, &deps.querier, env.contract.address)?; - state.fee_account_type.save(deps.storage, &FeeType::Wallet)?; + state + .fee_account_type + .save(deps.storage, &FeeType::Wallet)?; state.dust_collector.save(deps.storage, &None)?; } "2.1.5" => { ConfigV100::upgrade_stores(deps.storage, &deps.querier, env.contract.address)?; let state = State::default(); - state.fee_account_type.save(deps.storage, &FeeType::Wallet)?; + state + .fee_account_type + .save(deps.storage, &FeeType::Wallet)?; state.dust_collector.save(deps.storage, &None)?; } "2.1.6" | "2.1.7" => { @@ -246,12 +268,16 @@ pub fn migrate(deps: DepsMut, env: Env, _msg: MigrateMsg) -> StdResult &get_denom_balance(&deps.querier, env.contract.address, denom)?, )?; - state.fee_account_type.save(deps.storage, &FeeType::Wallet)?; + state + .fee_account_type + .save(deps.storage, &FeeType::Wallet)?; state.dust_collector.save(deps.storage, &None)?; } - "2.1.8" |"2.1.16"=> { + "2.1.8" | "2.1.16" => { let state = State::default(); - state.fee_account_type.save(deps.storage, &FeeType::Wallet)?; + state + .fee_account_type + .save(deps.storage, &FeeType::Wallet)?; state.dust_collector.save(deps.storage, &None)?; } "3.0.1" => { diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index 9c6b24f..4f71070 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -1048,7 +1048,7 @@ pub fn set_dust_collector( .add_attribute("action", "steakhub/set_dust_collector")) } -pub fn collect_dust(deps: DepsMut, _env: Env) -> StdResult { +pub fn collect_dust(deps: DepsMut, _env: Env,_max_tokens:usize) -> StdResult { let state = State::default(); if let Some(_dust_addr) = state.dust_collector.load(deps.storage)? { diff --git a/packages/steak/src/hub.rs b/packages/steak/src/hub.rs index e7c912e..637b8de 100644 --- a/packages/steak/src/hub.rs +++ b/packages/steak/src/hub.rs @@ -1,6 +1,8 @@ use std::str::FromStr; -use cosmwasm_std::{Addr, Binary, Coin, CosmosMsg, Decimal, Empty, StdResult, to_binary, Uint128, WasmMsg}; +use cosmwasm_std::{ + to_binary, Addr, Binary, Coin, CosmosMsg, Decimal, Empty, StdResult, Uint128, WasmMsg, +}; use cw20::Cw20ReceiveMsg; use cw20_base::msg::InstantiateMarketingInfo as Cw20InstantiateMarketingInfo; use schemars::JsonSchema; @@ -9,8 +11,9 @@ use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] pub enum Cw20HookMsg { Distribute {}, - Transfer{} + Transfer {}, } + #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] pub enum EnterpriseCw20HookMsg { Distribute {}, @@ -57,41 +60,69 @@ pub enum ExecuteMsg { /// Implements the Cw20 receiver interface Receive(Cw20ReceiveMsg), /// Bond specified amount of Luna - Bond { receiver: Option, exec_msg: Option }, + Bond { + receiver: Option, + exec_msg: Option, + }, /// Bond specified amount of Luna, just minting it directly (cw-20 version only) - BondEx { receiver: Option }, + BondEx { + receiver: Option, + }, /// Withdraw Luna that have finished unbonding in previous batches - WithdrawUnbonded { receiver: Option }, + WithdrawUnbonded { + receiver: Option, + }, /// Withdraw Luna that has finished unbonding in previous batches, for given address - WithdrawUnbondedAdmin { address: String }, + WithdrawUnbondedAdmin { + address: String, + }, /// Add a validator to the whitelist; callable by the owner - AddValidator { validator: String }, + AddValidator { + validator: String, + }, /// Remove a validator from the whitelist; callable by the owner - RemoveValidator { validator: String }, + RemoveValidator { + validator: String, + }, /// Remove a validator from the whitelist; callable by the owner. Does not undelegate. use for typos - RemoveValidatorEx { validator: String }, + RemoveValidatorEx { + validator: String, + }, /// Pause a validator from accepting new delegations - PauseValidator { validator: String }, + PauseValidator { + validator: String, + }, /// Unpause a validator from accepting new delegations - UnPauseValidator { validator: String }, + UnPauseValidator { + validator: String, + }, /// Transfer ownership to another account; will not take effect unless the new owner accepts - TransferOwnership { new_owner: String }, + TransferOwnership { + new_owner: String, + }, /// Accept an ownership transfer AcceptOwnership {}, /// Claim staking rewards, swap all for Luna, and restake Harvest {}, /// Use redelegations to balance the amounts of Luna delegated to validators - Rebalance { minimum: Uint128 }, + Rebalance { + minimum: Uint128, + }, /// redelegate stake from one validator to another - Redelegate { validator_from: String, validator_to: String }, + Redelegate { + validator_from: String, + validator_to: String, + }, /// Update Luna amounts in unbonding batches to reflect any slashing or rounding errors Reconcile {}, /// Submit the current pending batch of unbonding requests to be unbonded SubmitBatch {}, /// Set unbond period - SetUnbondPeriod { unbond_period: u64 }, + SetUnbondPeriod { + unbond_period: u64, + }, /// Transfer Fee collection account to another account TransferFeeAccount { @@ -99,13 +130,19 @@ pub enum ExecuteMsg { new_fee_account: String, }, /// Update fee collection amount - UpdateFee { new_fee: Decimal }, + UpdateFee { + new_fee: Decimal, + }, /// Callbacks; can only be invoked by the contract itself Callback(CallbackMsg), // Set The Duster. - SetDustCollector { dust_collector: Option }, + SetDustCollector { + dust_collector: Option, + }, /// Collect the Dust - CollectDust {}, + CollectDust { + max_tokens: u32, + }, /// Return the Dust in shiny 'base denom' ReturnDenom {}, } @@ -193,7 +230,7 @@ pub struct ConfigResponse { pub validators: Vec, pub paused_validators: Vec, pub dust_collector: Option, - pub token_factory: Option + pub token_factory: Option, } #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, JsonSchema)] diff --git a/packages/steak/src/hub_tf.rs b/packages/steak/src/hub_tf.rs index fda9bdb..01fbabd 100644 --- a/packages/steak/src/hub_tf.rs +++ b/packages/steak/src/hub_tf.rs @@ -1,5 +1,5 @@ -use std::str::FromStr; use cosmwasm_schema::cw_serde; +use std::str::FromStr; // use cosmwasm_std::{Decimal, Uint128}; @@ -7,9 +7,9 @@ use crate::hub::CallbackMsg; #[cw_serde] pub enum TokenFactoryType { - CosmWasm =1, - Kujira =2, - Injective =3 + CosmWasm = 1, + Kujira = 2, + Injective = 3, } impl ToString for TokenFactoryType { fn to_string(&self) -> String { @@ -63,7 +63,10 @@ pub struct InstantiateMsg { #[cw_serde] pub enum ExecuteMsg { /// Bond specified amount of Luna - Bond { receiver: Option, exec_msg: Option }, + Bond { + receiver: Option, + exec_msg: Option, + }, /// Bond specified amount of Luna Unbond { receiver: Option }, @@ -92,7 +95,10 @@ pub enum ExecuteMsg { /// Use redelegations to balance the amounts of Luna delegated to validators Rebalance { minimum: Uint128 }, /// redelegate stake from one validator to another - Redelegate { validator_from: String, validator_to: String }, + Redelegate { + validator_from: String, + validator_to: String, + }, /// Update Luna amounts in unbonding batches to reflect any slashing or rounding errors Reconcile {}, /// Submit the current pending batch of unbonding requests to be unbonded @@ -112,8 +118,7 @@ pub enum ExecuteMsg { /// Set Dust Collector Contract SetDustCollector { dust_collector: Option }, /// Collect the Dust - CollectDust { max_tokens: u64}, + CollectDust { max_tokens: u32 }, /// Return the Dust in shiny 'base denom' ReturnDenom {}, } - From f5aeb53272fe371ba2ae9ebff632c5929d98bbf6 Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Fri, 2 Feb 2024 08:59:43 -0600 Subject: [PATCH 53/77] feat: add osmosis tokenfactory support --- Cargo.lock | 4 +- contracts/hub-tf/Cargo.toml | 2 +- contracts/hub-tf/src/execute.rs | 28 ++++- contracts/hub-tf/src/lib.rs | 11 +- contracts/hub-tf/src/osmosis/denom.rs | 146 ++++++++++++++++++++++++++ contracts/hub-tf/src/osmosis/mod.rs | 2 + init/kaiyo_hub-tf_init.json | 15 +++ init/osmo-test_hub-tf_init.json | 15 +++ packages/steak/Cargo.toml | 2 +- packages/steak/src/hub_tf.rs | 3 + 10 files changed, 218 insertions(+), 10 deletions(-) create mode 100644 contracts/hub-tf/src/osmosis/denom.rs create mode 100644 contracts/hub-tf/src/osmosis/mod.rs create mode 100644 init/kaiyo_hub-tf_init.json create mode 100644 init/osmo-test_hub-tf_init.json diff --git a/Cargo.lock b/Cargo.lock index 8767aca..b1268fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -685,7 +685,7 @@ dependencies = [ [[package]] name = "pfc-steak" -version = "3.0.9" +version = "3.0.11" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -712,7 +712,7 @@ dependencies = [ [[package]] name = "pfc-steak-hub-tf" -version = "3.0.10" +version = "3.0.11" dependencies = [ "cosmwasm-std", "cw-item-set", diff --git a/contracts/hub-tf/Cargo.toml b/contracts/hub-tf/Cargo.toml index 4b068fa..7ce351d 100644 --- a/contracts/hub-tf/Cargo.toml +++ b/contracts/hub-tf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub-tf" -version = "3.0.10" +version = "3.0.11" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub-tf/src/execute.rs b/contracts/hub-tf/src/execute.rs index 0da980b..47eb22b 100644 --- a/contracts/hub-tf/src/execute.rs +++ b/contracts/hub-tf/src/execute.rs @@ -16,13 +16,13 @@ use crate::helpers::{ get_denom_balance, parse_received_fund, query_all_delegations, query_delegation, query_delegations, }; -use crate::kujira; use crate::math::{ compute_mint_amount, compute_redelegations_for_rebalancing, compute_redelegations_for_removal, compute_unbond_amount, compute_undelegations, reconcile_batches, }; use crate::state::{previous_batches, unbond_requests, State, VALIDATORS, VALIDATORS_ACTIVE}; use crate::{injective, token_factory}; +use crate::{kujira, osmosis}; //use crate::token_factory::denom::{MsgBurn, MsgCreateDenom, MsgMint}; use crate::types::{Coins, Delegation, Redelegation}; @@ -113,6 +113,12 @@ pub fn instantiate(deps: DepsMut, env: Env, msg: InstantiateMsg) -> StdResult >::into( + osmosis::denom::MsgCreateDenom { + sender: env.contract.address.to_string(), + subdenom: steak_denom_msg, + }, + ), }; Ok(Response::new().add_message(c)) @@ -237,6 +243,16 @@ pub fn bond( }), }) } + TokenFactoryType::Osmosis => { + >::into(osmosis::denom::MsgMint { + sender: env.contract.address.to_string(), + amount: Some(osmosis::denom::Coin { + denom: steak_denom.clone(), + amount: usteak_to_mint.to_string(), + }), + mint_to_address: env.contract.address.to_string(), + }) + } }; let contract_info = deps.querier.query_wasm_contract_info(receiver.to_string()); @@ -673,6 +689,16 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { }), }) } + TokenFactoryType::Osmosis => { + >::into(osmosis::denom::MsgBurn { + sender: env.contract.address.to_string(), + amount: Some(osmosis::denom::Coin { + denom: steak_denom, + amount: pending_batch.usteak_to_burn.to_string(), + }), + burn_from_address: env.contract.address.to_string(), + }) + } }; // yes.. this will fail if supply is less than the amount to burn. this is intentional. state.steak_minted.save( diff --git a/contracts/hub-tf/src/lib.rs b/contracts/hub-tf/src/lib.rs index 2c1ea75..77aa148 100644 --- a/contracts/hub-tf/src/lib.rs +++ b/contracts/hub-tf/src/lib.rs @@ -3,13 +3,14 @@ pub mod contract; pub mod execute; pub mod helpers; +pub mod injective; +pub mod kujira; pub mod math; +mod migrations; +pub mod osmosis; pub mod queries; pub mod state; -pub mod types; -pub mod token_factory; -pub mod kujira; -pub mod injective; #[cfg(test)] mod testing; -mod migrations; +pub mod token_factory; +pub mod types; diff --git a/contracts/hub-tf/src/osmosis/denom.rs b/contracts/hub-tf/src/osmosis/denom.rs new file mode 100644 index 0000000..4a4ddf4 --- /dev/null +++ b/contracts/hub-tf/src/osmosis/denom.rs @@ -0,0 +1,146 @@ +// source: https://github.com/White-Whale-Defi-Platform/white-whale-core/blob/feat/tokenfactory-lp/packages/pool-network/src/denom.rs + +use std::convert::TryFrom; +use std::convert::TryInto; + +use osmosis_std_derive::CosmwasmExt; + +// see https://github.com/notional-labs/wasmd/blob/v0.30.0-sdk469.4/proto/cosmwasm/tokenfactory/v1beta1/tx.proto + +/// Coin defines a token with a denomination and an amount. +/// +/// NOTE: The amount field is an Int which implements the custom method +/// signatures required by gogoproto. +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/cosmos.base.v1beta1.Coin")] +pub struct Coin { + #[prost(string, tag = "1")] + pub denom: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub amount: ::prost::alloc::string::String, +} + +/// MsgCreateDenom defines the message structure for the CreateDenom gRPC service +/// method. It allows an account to create a new denom. It requires a sender +/// address and a sub denomination. The (sender_address, sub_denomination) tuple +/// must be unique and cannot be re-used. +/// +/// The resulting denom created is defined as +/// . The resulting denom's admin is +/// originally set to be the creator, but this can be changed later. The token +/// denom does not indicate the current admin. +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.MsgCreateDenom")] +pub struct MsgCreateDenom { + #[prost(string, tag = "1")] + pub sender: ::prost::alloc::string::String, + /// subdenom can be up to 44 "alphanumeric" characters long. + #[prost(string, tag = "2")] + pub subdenom: ::prost::alloc::string::String, +} + +/// MsgCreateDenomResponse is the return value of MsgCreateDenom +/// It returns the full string of the newly created denom +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.MsgCreateDenomResponse")] +pub struct MsgCreateDenomResponse { + #[prost(string, tag = "1")] + pub new_token_denom: ::prost::alloc::string::String, +} + +/// MsgMint is the sdk.Msg type for allowing an admin account to mint +/// more of a token. For now, we only support minting to the sender account +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.MsgMint")] +pub struct MsgMint { + #[prost(string, tag = "1")] + pub sender: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub amount: ::core::option::Option, + #[prost(string, tag = "3")] + pub mint_to_address: ::prost::alloc::string::String, +} + +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.MsgMintResponse")] +pub struct MsgMintResponse {} + +/// MsgBurn is the sdk.Msg type for allowing an admin account to burn +/// a token. For now, we only support burning from the sender account. +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.MsgBurn")] +pub struct MsgBurn { + #[prost(string, tag = "1")] + pub sender: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub amount: ::core::option::Option, + #[prost(string, tag = "3")] + pub burn_from_address: ::prost::alloc::string::String, +} + +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.MsgBurnResponse")] +pub struct MsgBurnResponse {} diff --git a/contracts/hub-tf/src/osmosis/mod.rs b/contracts/hub-tf/src/osmosis/mod.rs new file mode 100644 index 0000000..396cc23 --- /dev/null +++ b/contracts/hub-tf/src/osmosis/mod.rs @@ -0,0 +1,2 @@ +// +pub mod denom; \ No newline at end of file diff --git a/init/kaiyo_hub-tf_init.json b/init/kaiyo_hub-tf_init.json new file mode 100644 index 0000000..47b3014 --- /dev/null +++ b/init/kaiyo_hub-tf_init.json @@ -0,0 +1,15 @@ +{ + "owner": "kujira1lu92zj8q6cmrptu09rp3343x9969r9qrva5t90", + "epoch_period": 259200, + "unbond_period": 1209600, + "validators": [ + "kujiravaloper1670dvuv348eynr9lsmdrhqu3g7vpmzx9ugf8fk" + ], + "denom": "ukuji", + "steak_denom": "boneKuji", + "fee_account": "kujira1lu92zj8q6cmrptu09rp3343x9969r9qrva5t90", + "fee_amount": "0.10", + "max_fee_amount": "0.10", + "fee_account_type": "Wallet", + "token_factory": "Kuji" +} \ No newline at end of file diff --git a/init/osmo-test_hub-tf_init.json b/init/osmo-test_hub-tf_init.json new file mode 100644 index 0000000..ae25166 --- /dev/null +++ b/init/osmo-test_hub-tf_init.json @@ -0,0 +1,15 @@ +{ + "owner": "osmo1lu92zj8q6cmrptu09rp3343x9969r9qr4w9r7h", + "epoch_period": 259200, + "unbond_period": 432000, + "validators": [ + "osmovaloper1qjy5g20ksjq9qgfgmru0aehex6lxhtee5whfcy" + ], + "denom": "uosmo", + "steak_denom": "boneOsmo", + "fee_account": "osmo1lu92zj8q6cmrptu09rp3343x9969r9qr4w9r7h", + "fee_amount": "0.10", + "max_fee_amount": "0.10", + "fee_account_type": "Wallet", + "token_factory": "Osmosis" +} \ No newline at end of file diff --git a/packages/steak/Cargo.toml b/packages/steak/Cargo.toml index 33be813..aa93336 100644 --- a/packages/steak/Cargo.toml +++ b/packages/steak/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak" -version = "3.0.9" +version = "3.0.11" authors = ["larry ", "PFC "] edition = "2018" description = "Liquid steaking protocol for the cosmos" diff --git a/packages/steak/src/hub_tf.rs b/packages/steak/src/hub_tf.rs index 01fbabd..9765ff9 100644 --- a/packages/steak/src/hub_tf.rs +++ b/packages/steak/src/hub_tf.rs @@ -10,6 +10,7 @@ pub enum TokenFactoryType { CosmWasm = 1, Kujira = 2, Injective = 3, + Osmosis = 4, } impl ToString for TokenFactoryType { fn to_string(&self) -> String { @@ -17,6 +18,7 @@ impl ToString for TokenFactoryType { TokenFactoryType::CosmWasm => String::from("CosmWasm"), TokenFactoryType::Kujira => String::from("Kujira"), TokenFactoryType::Injective => String::from("Injective"), + TokenFactoryType::Osmosis => String::from("Osmosis"), } } } @@ -27,6 +29,7 @@ impl FromStr for TokenFactoryType { "CosmWasm" => Ok(TokenFactoryType::CosmWasm), "Kujira" => Ok(TokenFactoryType::Kujira), "Injective" => Ok(TokenFactoryType::Injective), + "Osmosis" => Ok(TokenFactoryType::Osmosis), _ => Err(()), } } From 1c5a56dbbc449036568c822c6c14f01ded26bc06 Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Fri, 23 Feb 2024 12:32:22 -0500 Subject: [PATCH 54/77] feat: v3.0.12 - new function - change TF type --- Cargo.lock | 4 ++-- contracts/hub-tf/Cargo.toml | 2 +- contracts/hub-tf/src/contract.rs | 14 +++++++------- contracts/hub-tf/src/execute.rs | 21 +++++++++++++++++++++ init/narwhal_hub-tf_init.json | 17 +++++++++++++++++ init/narwhal_hub_init.json | 25 ------------------------- packages/steak/Cargo.toml | 2 +- packages/steak/src/hub_tf.rs | 2 ++ 8 files changed, 51 insertions(+), 36 deletions(-) create mode 100644 init/narwhal_hub-tf_init.json delete mode 100644 init/narwhal_hub_init.json diff --git a/Cargo.lock b/Cargo.lock index b1268fe..c1db6b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -685,7 +685,7 @@ dependencies = [ [[package]] name = "pfc-steak" -version = "3.0.11" +version = "3.0.12" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -712,7 +712,7 @@ dependencies = [ [[package]] name = "pfc-steak-hub-tf" -version = "3.0.11" +version = "3.0.12" dependencies = [ "cosmwasm-std", "cw-item-set", diff --git a/contracts/hub-tf/Cargo.toml b/contracts/hub-tf/Cargo.toml index 7ce351d..12c45de 100644 --- a/contracts/hub-tf/Cargo.toml +++ b/contracts/hub-tf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub-tf" -version = "3.0.11" +version = "3.0.12" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub-tf/src/contract.rs b/contracts/hub-tf/src/contract.rs index 06d94c6..8cb0a8f 100644 --- a/contracts/hub-tf/src/contract.rs +++ b/contracts/hub-tf/src/contract.rs @@ -120,6 +120,9 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S ExecuteMsg::ReturnDenom {} => { execute::bond(deps, env, info.sender, info.funds, None, false) } + ExecuteMsg::ChangeTokenFactory { token_factory_type } => { + execute::change_token_factory(deps, info.sender, &token_factory_type) + } } } @@ -186,13 +189,10 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { #[entry_point] pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult { - let contract_version = match get_contract_version(deps.storage) { - Ok(version) => version, - Err(_) => ContractVersion { - contract: "steak-hub-tf".to_string(), - version: "0".to_string(), - }, - }; + let contract_version = get_contract_version(deps.storage).unwrap_or_else(|_| ContractVersion { + contract: "steak-hub-tf".to_string(), + version: "0".to_string(), + }); match contract_version.contract.as_ref() { #[allow(clippy::single_match)] "steak-hub-tf" => match contract_version.version.as_ref() { diff --git a/contracts/hub-tf/src/execute.rs b/contracts/hub-tf/src/execute.rs index 47eb22b..e8dd936 100644 --- a/contracts/hub-tf/src/execute.rs +++ b/contracts/hub-tf/src/execute.rs @@ -1246,3 +1246,24 @@ pub fn redelegate( .add_event(event) .add_attribute("action", "steakhub/redelegate")) } + +pub fn change_token_factory( + deps: DepsMut, + sender: Addr, + token_factory_type: &str, +) -> StdResult { + let state = State::default(); + + state.assert_owner(deps.storage, &sender)?; + + let tf = TokenFactoryType::from_str(token_factory_type) + .map_err(|_| StdError::generic_err("Invalid Token Factory type"))?; + + state.token_factory_type.save(deps.storage, &tf)?; + let event = Event::new("steak/change_token_factory") + .add_attribute("token_factory_type", token_factory_type); + + Ok(Response::new() + .add_event(event) + .add_attribute("action", "steakhub/change_token_factory")) +} diff --git a/init/narwhal_hub-tf_init.json b/init/narwhal_hub-tf_init.json new file mode 100644 index 0000000..37bfa39 --- /dev/null +++ b/init/narwhal_hub-tf_init.json @@ -0,0 +1,17 @@ +{ + "owner": "migaloo1wpayju4jcn2mhv6yewclf6rcq9fyqzvavppjal", + "epoch_period": 259200, + "unbond_period": 300, + "validators": [ + "migaloovaloper1jt9w26mpxxjsk63mvd4m2ynj0af09csluyns75", + "migaloovaloper1wpayju4jcn2mhv6yewclf6rcq9fyqzva7s97l5", + "migaloovaloper1xzern65ppact6sgh4htllnr8pg9dpnc8uj5vuu" + ], + "denom": "uwhale", + "steak_denom": "boneWhale", + "fee_account": "migaloo1wpayju4jcn2mhv6yewclf6rcq9fyqzvavppjal", + "fee_amount": "0.10", + "max_fee_amount": "0.10", + "fee_account_type": "Wallet", + "token_factory": "Osmosis" +} \ No newline at end of file diff --git a/init/narwhal_hub_init.json b/init/narwhal_hub_init.json deleted file mode 100644 index ac66def..0000000 --- a/init/narwhal_hub_init.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "cw20_code_id": 24, - "owner": "migaloo1kdtdg0lvy8asxn8clnjfpusuvf93zuknltr2eh", - "name": "bWHALE Token", - "symbol": "bWHALE", - "decimals": 6, - "epoch_period": 259200, - "unbond_period": 1814400, - "validators": [ - "migaloovaloper16q6p6nkzc0525qnnrdd93urxv6pr8zhc4vhjy8", - "migaloovaloper1rqvctgdpafvc0k9fx4ng8ckt94x723zmp3g0jv", - "migaloovaloper18ulrp6juyj0tt0zmkrxn3ex4mkd3kkg6uk7nfx" - ], - "denom": "uwhale", - "fee_account": "migaloo1d6k8pahenpmx4spwfpf80zealtecfv7v0n7v49rxeu4lpzgzhrwq4wmhkr", - "fee_amount": "0.10", - "max_fee_amount": "0.10", - "fee_account_type": "FeeSplit", - "label": "boneWHALE", - "marketing": { - "project": "https://backbonelabs.io", - "description": "The GraveDigger is the Liquid Staking Product of BackBone Labs. It's liquid staking derivative (LSD) is boneWHALE.", - "marketing": "migaloo1kdtdg0lvy8asxn8clnjfpusuvf93zuknltr2eh" - } -} \ No newline at end of file diff --git a/packages/steak/Cargo.toml b/packages/steak/Cargo.toml index aa93336..dd3b7e1 100644 --- a/packages/steak/Cargo.toml +++ b/packages/steak/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak" -version = "3.0.11" +version = "3.0.12" authors = ["larry ", "PFC "] edition = "2018" description = "Liquid steaking protocol for the cosmos" diff --git a/packages/steak/src/hub_tf.rs b/packages/steak/src/hub_tf.rs index 9765ff9..65df430 100644 --- a/packages/steak/src/hub_tf.rs +++ b/packages/steak/src/hub_tf.rs @@ -124,4 +124,6 @@ pub enum ExecuteMsg { CollectDust { max_tokens: u32 }, /// Return the Dust in shiny 'base denom' ReturnDenom {}, + /// change tokenfactory type (ADMIN only) + ChangeTokenFactory { token_factory_type: String }, } From 150e0ed6df6e7be396dad72e872a003bdba692a3 Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Fri, 23 Feb 2024 12:33:06 -0500 Subject: [PATCH 55/77] remove ibc --- init/narwhal_ibc_init.json | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 init/narwhal_ibc_init.json diff --git a/init/narwhal_ibc_init.json b/init/narwhal_ibc_init.json deleted file mode 100644 index 081e651..0000000 --- a/init/narwhal_ibc_init.json +++ /dev/null @@ -1,3 +0,0 @@ -{ -"reflect_code_id": 128 -} \ No newline at end of file From b71a662a0999d3f716e09255bd1a88f0a7f53457 Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Wed, 6 Mar 2024 13:32:16 -0600 Subject: [PATCH 56/77] fix: v3.0.11 - hub - don't burn 0 tokens --- Cargo.lock | 2 +- build_x86.sh | 2 +- contracts/hub/Cargo.toml | 2 +- contracts/hub/src/execute.rs | 173 ++++++++++++++++++++++------------- 4 files changed, 112 insertions(+), 67 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c1db6b1..db4035d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -697,7 +697,7 @@ dependencies = [ [[package]] name = "pfc-steak-hub" -version = "3.0.10" +version = "3.0.11" dependencies = [ "cosmwasm-std", "cw-storage-plus 0.13.4", diff --git a/build_x86.sh b/build_x86.sh index e071f94..c699b58 100755 --- a/build_x86.sh +++ b/build_x86.sh @@ -2,4 +2,4 @@ docker run --rm -v "$(pwd)":/code \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ - cosmwasm/workspace-optimizer:0.12.13 + cosmwasm/workspace-optimizer:0.15.1 diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index 58c5ffd..76ef1e3 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub" -version = "3.0.10" +version = "3.0.11" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index 4f71070..7c16814 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -2,12 +2,19 @@ use std::collections::HashSet; use std::iter::FromIterator; use std::str::FromStr; -use cosmwasm_std::{Addr, BankMsg, Binary, Coin, CosmosMsg, Decimal, DepsMut, DistributionMsg, Env, Event, from_binary, Order, Response, StdError, StdResult, SubMsg, SubMsgResponse, to_binary, Uint128, WasmMsg}; +use cosmwasm_std::{ + from_binary, to_binary, Addr, BankMsg, Binary, Coin, CosmosMsg, Decimal, DepsMut, + DistributionMsg, Env, Event, Order, Response, StdError, StdResult, SubMsg, SubMsgResponse, + Uint128, WasmMsg, +}; use cw20::{Cw20ExecuteMsg, MinterResponse}; use cw20_base::msg::InstantiateMsg as Cw20InstantiateMsg; +use pfc_steak::hub::{ + Batch, CallbackMsg, Cw20HookMsg, ExecuteMsg, FeeType, InstantiateMsg, PendingBatch, + UnbondRequest, +}; use pfc_steak::DecimalCheckedOps; -use pfc_steak::hub::{Batch, CallbackMsg, Cw20HookMsg, ExecuteMsg, FeeType, InstantiateMsg, PendingBatch, UnbondRequest}; use crate::contract::{REPLY_INSTANTIATE_TOKEN, REPLY_REGISTER_RECEIVED_COINS}; use crate::helpers::{ @@ -123,14 +130,22 @@ pub fn register_steak_token(deps: DepsMut, response: SubMsgResponse) -> StdResul /// smallest amount of delegation. If delegations become severely unbalance as a result of this /// (e.g. when a single user makes a very big deposit), anyone can invoke `ExecuteMsg::Rebalance` /// to balance the delegations. -pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec, bond_msg: Option, mint_it: bool) -> StdResult { +pub fn bond( + deps: DepsMut, + env: Env, + receiver: Addr, + funds: Vec, + bond_msg: Option, + mint_it: bool, +) -> StdResult { let state = State::default(); let denom = state.denom.load(deps.storage)?; let amount_to_bond = parse_received_fund(&funds, &denom)?; let steak_token = state.steak_token.load(deps.storage)?; let validators = state.validators_active.load(deps.storage)?; - let mut validators_wl: HashSet = HashSet::from_iter(state.validators.load(deps.storage)?); + let mut validators_wl: HashSet = + HashSet::from_iter(state.validators.load(deps.storage)?); for v in validators.iter() { validators_wl.remove(v); } @@ -139,7 +154,12 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec, bond_msg: // Query the current delegations made to validators, and find the validator with the smallest // delegated amount through a linear search // The code for linear search is a bit uglier than using `sort_by` but cheaper: O(n) vs O(n * log(n)) - let delegations_non_active = query_delegations(&deps.querier, &non_active_validator_list, &env.contract.address, &denom)?; + let delegations_non_active = query_delegations( + &deps.querier, + &non_active_validator_list, + &env.contract.address, + &denom, + )?; let delegations = query_delegations(&deps.querier, &validators, &env.contract.address, &denom)?; let mut validator = &delegations[0].validator; @@ -158,7 +178,12 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec, bond_msg: // Query the current supply of Steak and compute the amount to mint let usteak_supply = query_cw20_total_supply(&deps.querier, &steak_token)?; - let usteak_to_mint = compute_mint_amount(usteak_supply, amount_to_bond, &delegations, &delegations_non_active); + let usteak_to_mint = compute_mint_amount( + usteak_supply, + amount_to_bond, + &delegations, + &delegations_non_active, + ); state.prev_denom.save( deps.storage, &get_denom_balance(&deps.querier, env.contract.address.clone(), denom.clone())?, @@ -170,43 +195,40 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec, bond_msg: ); let mint_msgs: Vec = if mint_it { - vec!(CosmosMsg::Wasm(WasmMsg::Execute { + vec![CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: steak_token.to_string(), msg: to_binary(&Cw20ExecuteMsg::Mint { recipient: receiver.to_string(), amount: usteak_to_mint, })?, funds: vec![], - })) + })] } else { let contract_info = deps.querier.query_wasm_contract_info(receiver.to_string()); let send_transfer_msg: CosmosMsg = match contract_info { Ok(_) => { if let Some(exec_msg) = bond_msg { - match from_binary(&exec_msg)? - { - Cw20HookMsg::Transfer {} => { - CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: steak_token.to_string(), - msg: to_binary(&Cw20ExecuteMsg::Transfer { - recipient: receiver.to_string(), - amount: usteak_to_mint, - })?, - funds: vec![], - }) - } - Cw20HookMsg::Distribute {} => { - CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: steak_token.to_string(), - msg: to_binary(&Cw20ExecuteMsg::Send { - contract: receiver.to_string(), - amount: usteak_to_mint, - msg: to_binary(&funds_distributor_api::msg::Cw20HookMsg::Distribute {})?, - })?, - funds: vec![], - }) - } + match from_binary(&exec_msg)? { + Cw20HookMsg::Transfer {} => CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: steak_token.to_string(), + msg: to_binary(&Cw20ExecuteMsg::Transfer { + recipient: receiver.to_string(), + amount: usteak_to_mint, + })?, + funds: vec![], + }), + Cw20HookMsg::Distribute {} => CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: steak_token.to_string(), + msg: to_binary(&Cw20ExecuteMsg::Send { + contract: receiver.to_string(), + amount: usteak_to_mint, + msg: to_binary( + &funds_distributor_api::msg::Cw20HookMsg::Distribute {}, + )?, + })?, + funds: vec![], + }), } } else { CosmosMsg::Wasm(WasmMsg::Execute { @@ -232,18 +254,19 @@ pub fn bond(deps: DepsMut, env: Env, receiver: Addr, funds: Vec, bond_msg: }) } }; - vec!( + vec![ CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: steak_token.to_string(), msg: to_binary(&Cw20ExecuteMsg::Mint { - recipient: env.contract.address.to_string(),//receiver.to_string(), + recipient: env.contract.address.to_string(), //receiver.to_string(), amount: usteak_to_mint, })?, funds: vec![], - }), send_transfer_msg) + }), + send_transfer_msg, + ] }; - let event = Event::new("steakhub/bonded") .add_attribute("time", env.block.time.seconds().to_string()) .add_attribute("height", env.block.height.to_string()) @@ -356,11 +379,10 @@ pub fn reinvest(deps: DepsMut, env: Env) -> StdResult { let fee_type = state.fee_account_type.load(deps.storage)?; let send_msgs = match fee_type { - FeeType::Wallet => - vec![CosmosMsg::Bank(BankMsg::Send { - to_address: fee_account.to_string(), - amount: vec![Coin::new(fee_amount.into(), &denom)], - })], + FeeType::Wallet => vec![CosmosMsg::Bank(BankMsg::Send { + to_address: fee_account.to_string(), + amount: vec![Coin::new(fee_amount.into(), &denom)], + })], FeeType::FeeSplit => { let msg = pfc_fee_split::fee_split_msg::ExecuteMsg::Deposit { flush: false }; @@ -500,7 +522,8 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { pending_batch.est_unbond_start_time ))); } - let mut validators_active: HashSet = HashSet::from_iter(state.validators_active.load(deps.storage)?); + let mut validators_active: HashSet = + HashSet::from_iter(state.validators_active.load(deps.storage)?); for v in validators.iter() { validators_active.remove(v); } @@ -508,11 +531,20 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { // for unbonding we still need to look at let delegations = query_delegations(&deps.querier, &validators, &env.contract.address, &denom)?; - let delegations_active = query_delegations(&deps.querier, &active_validator_list, &env.contract.address, &denom)?; + let delegations_active = query_delegations( + &deps.querier, + &active_validator_list, + &env.contract.address, + &denom, + )?; let usteak_supply = query_cw20_total_supply(&deps.querier, &steak_token)?; - let amount_to_bond = - compute_unbond_amount(usteak_supply, pending_batch.usteak_to_burn, &delegations, &delegations_active); + let amount_to_bond = compute_unbond_amount( + usteak_supply, + pending_batch.usteak_to_burn, + &delegations, + &delegations_active, + ); let new_undelegations = compute_undelegations(amount_to_bond, &delegations, &denom); // NOTE: Regarding the `amount_unclaimed` value @@ -554,7 +586,21 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { .iter() .map(|d| SubMsg::reply_on_success(d.to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS)) .collect::>(); + + let event = Event::new("steakhub/unbond_submitted") + .add_attribute("time", env.block.time.seconds().to_string()) + .add_attribute("height", env.block.height.to_string()) + .add_attribute("id", pending_batch.id.to_string()) + .add_attribute("native_unbonded", amount_to_bond) + .add_attribute("usteak_burned", pending_batch.usteak_to_burn); + if pending_batch.usteak_to_burn.is_zero() { + return Ok(Response::new() + .add_submessages(undelegate_submsgs) + // .add_message(burn_msg) + .add_event(event)); + } + let burn_msg = CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: steak_token.into(), msg: to_binary(&Cw20ExecuteMsg::Burn { @@ -563,13 +609,6 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { funds: vec![], }); - let event = Event::new("steakhub/unbond_submitted") - .add_attribute("time", env.block.time.seconds().to_string()) - .add_attribute("height", env.block.height.to_string()) - .add_attribute("id", pending_batch.id.to_string()) - .add_attribute("native_unbonded", amount_to_bond) - .add_attribute("usteak_burned", pending_batch.usteak_to_burn); - Ok(Response::new() .add_submessages(undelegate_submsgs) .add_message(burn_msg) @@ -1036,19 +1075,23 @@ pub fn set_dust_collector( state.assert_owner(deps.storage, &sender)?; if let Some(ref dust_addr) = dust_collector { - state.dust_collector.save(deps.storage, &Some(deps.api.addr_validate(dust_addr)?))?; + state + .dust_collector + .save(deps.storage, &Some(deps.api.addr_validate(dust_addr)?))?; } else { state.dust_collector.save(deps.storage, &None)?; }; - let event = Event::new("steak/set_dust_collector") - .add_attribute("dust_collector", dust_collector.unwrap_or("-cleared-".into())); + let event = Event::new("steak/set_dust_collector").add_attribute( + "dust_collector", + dust_collector.unwrap_or("-cleared-".into()), + ); Ok(Response::new() .add_event(event) .add_attribute("action", "steakhub/set_dust_collector")) } -pub fn collect_dust(deps: DepsMut, _env: Env,_max_tokens:usize) -> StdResult { +pub fn collect_dust(deps: DepsMut, _env: Env, _max_tokens: usize) -> StdResult { let state = State::default(); if let Some(_dust_addr) = state.dust_collector.load(deps.storage)? { @@ -1068,7 +1111,6 @@ pub fn return_denom(deps: DepsMut, _env: Env, _funds: Vec) -> StdResult Date: Wed, 6 Mar 2024 13:36:49 -0600 Subject: [PATCH 57/77] chore: use rustfmt. chore: switch to 'just' --- .cargo/{config => config.toml} | 0 .gitignore | 1 + contracts/hub-tf/src/contract.rs | 162 +++--- contracts/hub-tf/src/execute.rs | 253 ++++----- contracts/hub-tf/src/helpers.rs | 35 +- contracts/hub-tf/src/injective/denom.rs | 117 ++-- contracts/hub-tf/src/injective/mod.rs | 2 +- contracts/hub-tf/src/kujira/denom.rs | 119 ++-- contracts/hub-tf/src/kujira/mod.rs | 2 +- contracts/hub-tf/src/math.rs | 33 +- contracts/hub-tf/src/migrations.rs | 2 +- contracts/hub-tf/src/osmosis/denom.rs | 3 +- contracts/hub-tf/src/osmosis/mod.rs | 2 +- contracts/hub-tf/src/queries.rs | 34 +- contracts/hub-tf/src/state.rs | 36 +- .../hub-tf/src/testing/custom_querier.rs | 22 +- contracts/hub-tf/src/testing/helpers.rs | 13 +- contracts/hub-tf/src/testing/tests.rs | 380 ++++--------- contracts/hub-tf/src/token_factory/denom.rs | 117 ++-- contracts/hub-tf/src/token_factory/mod.rs | 2 +- contracts/hub/src/contract.rs | 187 +++---- contracts/hub/src/execute.rs | 282 ++++------ contracts/hub/src/helpers.rs | 18 +- contracts/hub/src/lib.rs | 2 +- contracts/hub/src/math.rs | 33 +- contracts/hub/src/migrations.rs | 26 +- contracts/hub/src/queries.rs | 36 +- contracts/hub/src/state.rs | 5 +- contracts/hub/src/testing/custom_querier.rs | 17 +- contracts/hub/src/testing/cw20_querier.rs | 5 +- contracts/hub/src/testing/helpers.rs | 13 +- contracts/hub/src/testing/tests.rs | 526 +++++++----------- contracts/token/src/lib.rs | 42 +- init/osmo-tf_init.json | 16 + justfile | 30 + packages/steak/src/hub.rs | 18 +- packages/steak/src/hub_tf.rs | 67 ++- packages/steak/src/lib.rs | 3 +- rustfmt.toml | 24 + 39 files changed, 1191 insertions(+), 1494 deletions(-) rename .cargo/{config => config.toml} (100%) create mode 100644 init/osmo-tf_init.json create mode 100644 justfile create mode 100644 rustfmt.toml diff --git a/.cargo/config b/.cargo/config.toml similarity index 100% rename from .cargo/config rename to .cargo/config.toml diff --git a/.gitignore b/.gitignore index 848aa62..a362c29 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,4 @@ scripts/keys/ # Misc notes /notes*.txt /notes +/prepared-tx.json \ No newline at end of file diff --git a/contracts/hub-tf/src/contract.rs b/contracts/hub-tf/src/contract.rs index 8cb0a8f..e32cbcb 100644 --- a/contracts/hub-tf/src/contract.rs +++ b/contracts/hub-tf/src/contract.rs @@ -1,15 +1,16 @@ +use std::convert::TryInto; + use cosmwasm_std::{ entry_point, to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdError, StdResult, }; use cw2::{get_contract_version, set_contract_version, ContractVersion}; -use std::convert::TryInto; - -use pfc_steak::hub::{CallbackMsg, MigrateMsg, QueryMsg}; -use pfc_steak::hub_tf::{ExecuteMsg, InstantiateMsg, TokenFactoryType}; +use pfc_steak::{ + hub::{CallbackMsg, MigrateMsg, QueryMsg}, + hub_tf::{ExecuteMsg, InstantiateMsg, TokenFactoryType}, +}; -use crate::state::State; -use crate::{execute, queries}; +use crate::{execute, queries, state::State}; //use crate::helpers::{ unwrap_reply}; @@ -36,93 +37,97 @@ pub fn instantiate( pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> StdResult { let api = deps.api; match msg { - ExecuteMsg::Bond { receiver, exec_msg } => execute::bond( + ExecuteMsg::Bond { + receiver, + exec_msg, + } => execute::bond( deps, env, - receiver - .map(|s| api.addr_validate(&s)) - .transpose()? - .unwrap_or(info.sender), + receiver.map(|s| api.addr_validate(&s)).transpose()?.unwrap_or(info.sender), info.funds, exec_msg, true, ), - ExecuteMsg::Unbond { receiver } => execute::queue_unbond( + ExecuteMsg::Unbond { + receiver, + } => execute::queue_unbond( deps, env, - receiver - .map(|s| api.addr_validate(&s)) - .transpose()? - .unwrap_or(info.sender), + receiver.map(|s| api.addr_validate(&s)).transpose()?.unwrap_or(info.sender), info.funds, ), - ExecuteMsg::WithdrawUnbonded { receiver } => execute::withdraw_unbonded( + ExecuteMsg::WithdrawUnbonded { + receiver, + } => execute::withdraw_unbonded( deps, env, info.sender.clone(), - receiver - .map(|s| api.addr_validate(&s)) - .transpose()? - .unwrap_or(info.sender), + receiver.map(|s| api.addr_validate(&s)).transpose()?.unwrap_or(info.sender), ), - ExecuteMsg::WithdrawUnbondedAdmin { address } => { - execute::withdraw_unbonded_admin(deps, env, info.sender, api.addr_validate(&address)?) - } - ExecuteMsg::AddValidator { validator } => { - execute::add_validator(deps, info.sender, validator) - } - ExecuteMsg::RemoveValidator { validator } => { - execute::remove_validator(deps, env, info.sender, validator) - } - ExecuteMsg::RemoveValidatorEx { validator } => { - execute::remove_validator_ex(deps, env, info.sender, validator) - } + ExecuteMsg::WithdrawUnbondedAdmin { + address, + } => execute::withdraw_unbonded_admin(deps, env, info.sender, api.addr_validate(&address)?), + ExecuteMsg::AddValidator { + validator, + } => execute::add_validator(deps, info.sender, validator), + ExecuteMsg::RemoveValidator { + validator, + } => execute::remove_validator(deps, env, info.sender, validator), + ExecuteMsg::RemoveValidatorEx { + validator, + } => execute::remove_validator_ex(deps, env, info.sender, validator), ExecuteMsg::Redelegate { validator_from, validator_to, } => execute::redelegate(deps, env, info.sender, validator_from, validator_to), - ExecuteMsg::TransferOwnership { new_owner } => { - execute::transfer_ownership(deps, info.sender, new_owner) - } + ExecuteMsg::TransferOwnership { + new_owner, + } => execute::transfer_ownership(deps, info.sender, new_owner), ExecuteMsg::AcceptOwnership {} => execute::accept_ownership(deps, info.sender), ExecuteMsg::Harvest {} => execute::harvest(deps, env), - ExecuteMsg::Rebalance { minimum } => execute::rebalance(deps, env, minimum), + ExecuteMsg::Rebalance { + minimum, + } => execute::rebalance(deps, env, minimum), ExecuteMsg::Reconcile {} => execute::reconcile(deps, env), ExecuteMsg::SubmitBatch {} => execute::submit_batch(deps, env), ExecuteMsg::TransferFeeAccount { fee_account_type, new_fee_account, } => execute::transfer_fee_account(deps, info.sender, fee_account_type, new_fee_account), - ExecuteMsg::UpdateFee { new_fee } => execute::update_fee(deps, info.sender, new_fee), + ExecuteMsg::UpdateFee { + new_fee, + } => execute::update_fee(deps, info.sender, new_fee), ExecuteMsg::Callback(callback_msg) => callback(deps, env, info, callback_msg), - ExecuteMsg::PauseValidator { validator } => { - execute::pause_validator(deps, env, info.sender, validator) - } - ExecuteMsg::UnPauseValidator { validator } => { - execute::unpause_validator(deps, env, info.sender, validator) - } - ExecuteMsg::SetUnbondPeriod { unbond_period } => { - execute::set_unbond_period(deps, env, info.sender, unbond_period) - } - - ExecuteMsg::SetDustCollector { dust_collector } => { - execute::set_dust_collector(deps, env, info.sender, dust_collector) - } - ExecuteMsg::CollectDust { max_tokens } => { + ExecuteMsg::PauseValidator { + validator, + } => execute::pause_validator(deps, env, info.sender, validator), + ExecuteMsg::UnPauseValidator { + validator, + } => execute::unpause_validator(deps, env, info.sender, validator), + ExecuteMsg::SetUnbondPeriod { + unbond_period, + } => execute::set_unbond_period(deps, env, info.sender, unbond_period), + + ExecuteMsg::SetDustCollector { + dust_collector, + } => execute::set_dust_collector(deps, env, info.sender, dust_collector), + ExecuteMsg::CollectDust { + max_tokens, + } => { let max_tokens_usize_r = max_tokens.try_into(); if let Ok(max_tokens_usize) = max_tokens_usize_r { execute::collect_dust(deps, env, max_tokens_usize) } else { Err(StdError::generic_err("max_tokens too large")) } - } + }, ExecuteMsg::ReturnDenom {} => { execute::bond(deps, env, info.sender, info.funds, None, false) - } - ExecuteMsg::ChangeTokenFactory { token_factory_type } => { - execute::change_token_factory(deps, info.sender, &token_factory_type) - } + }, + ExecuteMsg::ChangeTokenFactory { + token_factory_type, + } => execute::change_token_factory(deps, info.sender, &token_factory_type), } } @@ -133,9 +138,7 @@ fn callback( callback_msg: CallbackMsg, ) -> StdResult { if env.contract.address != info.sender { - return Err(StdError::generic_err( - "callbacks can only be invoked by the contract itself", - )); + return Err(StdError::generic_err("callbacks can only be invoked by the contract itself")); } match callback_msg { @@ -161,29 +164,20 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { QueryMsg::State {} => to_binary(&queries::state(deps, env)?), QueryMsg::PendingBatch {} => to_binary(&queries::pending_batch(deps)?), QueryMsg::PreviousBatch(id) => to_binary(&queries::previous_batch(deps, id)?), - QueryMsg::PreviousBatches { start_after, limit } => { - to_binary(&queries::previous_batches(deps, start_after, limit)?) - } - QueryMsg::UnbondRequestsByBatch { - id, + QueryMsg::PreviousBatches { start_after, limit, - } => to_binary(&queries::unbond_requests_by_batch( - deps, + } => to_binary(&queries::previous_batches(deps, start_after, limit)?), + QueryMsg::UnbondRequestsByBatch { id, start_after, limit, - )?), + } => to_binary(&queries::unbond_requests_by_batch(deps, id, start_after, limit)?), QueryMsg::UnbondRequestsByUser { user, start_after, limit, - } => to_binary(&queries::unbond_requests_by_user( - deps, - user, - start_after, - limit, - )?), + } => to_binary(&queries::unbond_requests_by_user(deps, user, start_after, limit)?), } } @@ -197,27 +191,21 @@ pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult match contract_version.version.as_ref() { #[allow(clippy::single_match)] - "0" => {} + "0" => {}, "3.0.1" | "3.0.2" => { let state = State::default(); let kuji = state.kuji_token_factory.load(deps.storage)?; if kuji { - state - .token_factory_type - .save(deps.storage, &TokenFactoryType::Kujira)? + state.token_factory_type.save(deps.storage, &TokenFactoryType::Kujira)? } else { - state - .token_factory_type - .save(deps.storage, &TokenFactoryType::CosmWasm)? + state.token_factory_type.save(deps.storage, &TokenFactoryType::CosmWasm)? } - } - _ => {} + }, + _ => {}, }, _ => { - return Err(StdError::generic_err( - "contract name is not the same. aborting {}", - )); - } + return Err(StdError::generic_err("contract name is not the same. aborting {}")); + }, } /* let state = State::default(); diff --git a/contracts/hub-tf/src/execute.rs b/contracts/hub-tf/src/execute.rs index e8dd936..202c207 100644 --- a/contracts/hub-tf/src/execute.rs +++ b/contracts/hub-tf/src/execute.rs @@ -1,30 +1,33 @@ -use std::collections::HashSet; -use std::iter::FromIterator; -use std::str::FromStr; +use std::{collections::HashSet, iter::FromIterator, str::FromStr}; use cosmwasm_std::{ to_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, DepsMut, DistributionMsg, Env, Event, Order, ReplyOn, Response, StdError, StdResult, SubMsg, Uint128, WasmMsg, }; - -use pfc_steak::hub::{Batch, CallbackMsg, FeeType, PendingBatch, UnbondRequest}; -use pfc_steak::hub_tf::{ExecuteMsg, InstantiateMsg, TokenFactoryType}; -use pfc_steak::DecimalCheckedOps; - -use crate::contract::{REPLY_REGISTER_RECEIVED_COINS, SPECIAL_SEND_MESSAGE_TO_TRANSFER}; -use crate::helpers::{ - get_denom_balance, parse_received_fund, query_all_delegations, query_delegation, - query_delegations, -}; -use crate::math::{ - compute_mint_amount, compute_redelegations_for_rebalancing, compute_redelegations_for_removal, - compute_unbond_amount, compute_undelegations, reconcile_batches, +use pfc_steak::{ + hub::{Batch, CallbackMsg, FeeType, PendingBatch, UnbondRequest}, + hub_tf::{ExecuteMsg, InstantiateMsg, TokenFactoryType}, + DecimalCheckedOps, }; -use crate::state::{previous_batches, unbond_requests, State, VALIDATORS, VALIDATORS_ACTIVE}; -use crate::{injective, token_factory}; -use crate::{kujira, osmosis}; + //use crate::token_factory::denom::{MsgBurn, MsgCreateDenom, MsgMint}; use crate::types::{Coins, Delegation, Redelegation}; +use crate::{ + contract::{REPLY_REGISTER_RECEIVED_COINS, SPECIAL_SEND_MESSAGE_TO_TRANSFER}, + helpers::{ + get_denom_balance, parse_received_fund, query_all_delegations, query_delegation, + query_delegations, + }, + injective, kujira, + math::{ + compute_mint_amount, compute_redelegations_for_rebalancing, + compute_redelegations_for_removal, compute_unbond_amount, compute_undelegations, + reconcile_batches, + }, + osmosis, + state::{previous_batches, unbond_requests, State, VALIDATORS, VALIDATORS_ACTIVE}, + token_factory, +}; //-------------------------------------------------------------------------------------------------- // Instantiation @@ -43,9 +46,7 @@ pub fn instantiate(deps: DepsMut, env: Env, msg: InstantiateMsg) -> StdResult StdResult StdResult StdResult StdResult >::into( kujira::denom::MsgCreateDenom { sender: env.contract.address.to_string(), @@ -136,7 +131,6 @@ pub fn instantiate(deps: DepsMut, env: Env, msg: InstantiateMsg) -> StdResult { >::into(injective::denom::MsgMint { sender: env.contract.address.to_string(), @@ -242,7 +235,7 @@ pub fn bond( amount: usteak_to_mint.to_string(), }), }) - } + }, TokenFactoryType::Osmosis => { >::into(osmosis::denom::MsgMint { sender: env.contract.address.to_string(), @@ -252,7 +245,7 @@ pub fn bond( }), mint_to_address: env.contract.address.to_string(), }) - } + }, }; let contract_info = deps.querier.query_wasm_contract_info(receiver.to_string()); @@ -289,7 +282,7 @@ pub fn bond( }], }) } - } + }, Err(_) => CosmosMsg::Bank(BankMsg::Send { to_address: receiver.to_string(), amount: vec![Coin { @@ -359,7 +352,8 @@ pub fn harvest(deps: DepsMut, env: Env) -> StdResult { } /// NOTE: -/// 1. When delegation Native denom here, we don't need to use a `SubMsg` to handle the received coins, +/// 1. When delegation Native denom here, we don't need to use a `SubMsg` to handle the received +/// coins, /// because we have already withdrawn all claimable staking rewards previously in the same atomic /// execution. /// 2. Same as with `bond`, in the latest implementation we only delegate staking rewards with the @@ -432,10 +426,12 @@ pub fn reinvest(deps: DepsMut, env: Env) -> StdResult { amount: vec![Coin::new(fee_amount.into(), &denom)], })], FeeType::FeeSplit => { - let msg = pfc_fee_split::fee_split_msg::ExecuteMsg::Deposit { flush: false }; + let msg = pfc_fee_split::fee_split_msg::ExecuteMsg::Deposit { + flush: false, + }; vec![msg.into_cosmos_msg(fee_account, vec![Coin::new(fee_amount.into(), &denom)])?] - } + }, }; Ok(Response::new() .add_message(new_delegation.to_cosmos_msg()) @@ -450,7 +446,8 @@ pub fn reinvest(deps: DepsMut, env: Env) -> StdResult { } } -/// NOTE: a `SubMsgResponse` may contain multiple coin-receiving events, must handle them individually +/// NOTE: a `SubMsgResponse` may contain multiple coin-receiving events, must handle them +/// individually pub fn register_received_coins( deps: DepsMut, env: Env, @@ -467,13 +464,11 @@ pub fn register_received_coins( } let state = State::default(); - state - .unlocked_coins - .update(deps.storage, |coins| -> StdResult<_> { - let mut coins = Coins(coins); - coins.add_many(&received_coins)?; - Ok(coins.0) - })?; + state.unlocked_coins.update(deps.storage, |coins| -> StdResult<_> { + let mut coins = Coins(coins); + coins.add_many(&received_coins)?; + Ok(coins.0) + })?; Ok(Response::new().add_attribute("action", "steakhub/register_received_coins")) } @@ -599,12 +594,8 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { // for unbonding we still need to look at // TODO verify denom let delegations = query_all_delegations(&deps.querier, &env.contract.address)?; - let delegations_active = query_delegations( - &deps.querier, - &active_validator_list, - &env.contract.address, - &denom, - )?; + let delegations_active = + query_delegations(&deps.querier, &active_validator_list, &env.contract.address, &denom)?; // let usteak_supply = query_cw20_total_supply(&deps.querier, &steak_token)?; let amount_to_unbond = compute_unbond_amount( @@ -621,10 +612,11 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { // If validators misbehave and get slashed during the unbonding period, the contract can receive // LESS Luna than `amount_to_unbond` when unbonding finishes! // - // In this case, users who invokes `withdraw_unbonded` will have their txs failed as the contract - // does not have enough Luna balance. + // In this case, users who invokes `withdraw_unbonded` will have their txs failed as the + // contract does not have enough Luna balance. // - // I don't have a solution for this... other than to manually fund contract with the slashed amount. + // I don't have a solution for this... other than to manually fund contract with the slashed + // amount. previous_batches().save( deps.storage, pending_batch.id, @@ -670,7 +662,7 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { amount: pending_batch.usteak_to_burn.to_string(), }), }) - } + }, TokenFactoryType::CosmWasm => >::into( token_factory::denom::MsgBurn { sender: env.contract.address.to_string(), @@ -688,7 +680,7 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { amount: pending_batch.usteak_to_burn.to_string(), }), }) - } + }, TokenFactoryType::Osmosis => { >::into(osmosis::denom::MsgBurn { sender: env.contract.address.to_string(), @@ -698,13 +690,10 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { }), burn_from_address: env.contract.address.to_string(), }) - } + }, }; // yes.. this will fail if supply is less than the amount to burn. this is intentional. - state.steak_minted.save( - deps.storage, - &(usteak_supply - pending_batch.usteak_to_burn), - )?; + state.steak_minted.save(deps.storage, &(usteak_supply - pending_batch.usteak_to_burn))?; let event = Event::new("steakhub/unbond_submitted") .add_attribute("time", env.block.time.seconds().to_string()) @@ -748,14 +737,10 @@ pub fn reconcile(deps: DepsMut, env: Env) -> StdResult { let native_expected_unlocked = Coins(unlocked_coins).find(&denom).amount; let native_expected = native_expected_received + native_expected_unlocked; - let native_actual = deps - .querier - .query_balance(&env.contract.address, &denom)? - .amount; + let native_actual = deps.querier.query_balance(&env.contract.address, &denom)?.amount; - let native_to_deduct = native_expected - .checked_sub(native_actual) - .unwrap_or_else(|_| Uint128::zero()); + let native_to_deduct = + native_expected.checked_sub(native_actual).unwrap_or_else(|_| Uint128::zero()); if !native_to_deduct.is_zero() { reconcile_batches(&mut batches, native_expected - native_actual); } @@ -765,19 +750,13 @@ pub fn reconcile(deps: DepsMut, env: Env) -> StdResult { previous_batches().save(deps.storage, batch.id, batch)?; } - let ids = batches - .iter() - .map(|b| b.id.to_string()) - .collect::>() - .join(","); + let ids = batches.iter().map(|b| b.id.to_string()).collect::>().join(","); let event = Event::new("steakhub/reconciled") .add_attribute("ids", ids) .add_attribute("native_deducted", native_to_deduct.to_string()); - Ok(Response::new() - .add_event(event) - .add_attribute("action", "steakhub/reconcile")) + Ok(Response::new().add_event(event).add_attribute("action", "steakhub/reconcile")) } pub fn withdraw_unbonded_admin( @@ -821,16 +800,15 @@ pub fn withdraw_unbonded( // - is a _previous_ batch, not a _pending_ batch // - is reconciled // - has finished unbonding - // If not sure whether the batches have been reconciled, the user should first invoke `ExecuteMsg::Reconcile` - // before withdrawing. + // If not sure whether the batches have been reconciled, the user should first invoke + // `ExecuteMsg::Reconcile` before withdrawing. let mut total_native_to_refund = Uint128::zero(); let mut ids: Vec = vec![]; for request in &requests { if let Ok(mut batch) = previous_batches().load(deps.storage, request.id) { if batch.reconciled && batch.est_unbond_end_time < current_time { - let native_to_refund = batch - .amount_unclaimed - .multiply_ratio(request.shares, batch.total_shares); + let native_to_refund = + batch.amount_unclaimed.multiply_ratio(request.shares, batch.total_shares); ids.push(request.id.to_string()); @@ -892,10 +870,9 @@ pub fn rebalance(deps: DepsMut, env: Env, minimum: Uint128) -> StdResult StdResul let event = Event::new("steakhub/validator_added").add_attribute("validator", validator); - Ok(Response::new() - .add_event(event) - .add_attribute("action", "steakhub/add_validator")) + Ok(Response::new().add_event(event).add_attribute("action", "steakhub/add_validator")) } pub fn remove_validator( @@ -946,9 +921,7 @@ pub fn remove_validator( let denom = state.denom.load(deps.storage)?; if !VALIDATORS.contains(deps.storage, &validator) { - return Err(StdError::generic_err( - "validator is not already whitelisted", - )); + return Err(StdError::generic_err("validator is not already whitelisted")); } VALIDATORS.remove(deps.storage, &validator)?; VALIDATORS_ACTIVE.insert(deps.storage, &validator)?; @@ -963,10 +936,9 @@ pub fn remove_validator( let new_redelegations = compute_redelegations_for_removal(&delegation_to_remove, &delegations, &denom); - state.prev_denom.save( - deps.storage, - &get_denom_balance(&deps.querier, env.contract.address, denom)?, - )?; + state + .prev_denom + .save(deps.storage, &get_denom_balance(&deps.querier, env.contract.address, denom)?)?; let redelegate_submsgs = new_redelegations .iter() @@ -997,17 +969,13 @@ pub fn remove_validator_ex( state.assert_owner(deps.storage, &sender)?; if !VALIDATORS.contains(deps.storage, &validator) { - return Err(StdError::generic_err( - "validator is not already whitelisted", - )); + return Err(StdError::generic_err("validator is not already whitelisted")); } VALIDATORS.remove(deps.storage, &validator)?; let event = Event::new("steak/validator_removed_ex").add_attribute("validator", validator); - Ok(Response::new() - .add_event(event) - .add_attribute("action", "steakhub/remove_validator_ex")) + Ok(Response::new().add_event(event).add_attribute("action", "steakhub/remove_validator_ex")) } pub fn pause_validator( @@ -1021,17 +989,13 @@ pub fn pause_validator( state.assert_owner(deps.storage, &sender)?; if !VALIDATORS_ACTIVE.contains(deps.storage, &validator) { - return Err(StdError::generic_err( - "validator is not already whitelisted", - )); + return Err(StdError::generic_err("validator is not already whitelisted")); } VALIDATORS_ACTIVE.remove(deps.storage, &validator)?; let event = Event::new("steak/pause_validator").add_attribute("validator", validator); - Ok(Response::new() - .add_event(event) - .add_attribute("action", "steakhub/pause_validator")) + Ok(Response::new().add_event(event).add_attribute("action", "steakhub/pause_validator")) } pub fn unpause_validator( @@ -1047,9 +1011,7 @@ pub fn unpause_validator( let event = Event::new("steak/unpause_validator").add_attribute("validator", validator); - Ok(Response::new() - .add_event(event) - .add_attribute("action", "steakhub/unpause_validator")) + Ok(Response::new().add_event(event).add_attribute("action", "steakhub/unpause_validator")) } pub fn set_unbond_period( @@ -1065,18 +1027,14 @@ pub fn set_unbond_period( let event = Event::new("steak/set_unbond_period") .add_attribute("unbond_period", format!("{}", unbond_period)); - Ok(Response::new() - .add_event(event) - .add_attribute("action", "steakhub/set_unbond_period")) + Ok(Response::new().add_event(event).add_attribute("action", "steakhub/set_unbond_period")) } pub fn transfer_ownership(deps: DepsMut, sender: Addr, new_owner: String) -> StdResult { let state = State::default(); state.assert_owner(deps.storage, &sender)?; - state - .new_owner - .save(deps.storage, &deps.api.addr_validate(&new_owner)?)?; + state.new_owner.save(deps.storage, &deps.api.addr_validate(&new_owner)?)?; Ok(Response::new().add_attribute("action", "steakhub/transfer_ownership")) } @@ -1088,9 +1046,7 @@ pub fn accept_ownership(deps: DepsMut, sender: Addr) -> StdResult { let new_owner = state.new_owner.load(deps.storage)?; if sender != new_owner { - return Err(StdError::generic_err( - "unauthorized: sender is not new owner", - )); + return Err(StdError::generic_err("unauthorized: sender is not new owner")); } state.owner.save(deps.storage, &sender)?; @@ -1100,9 +1056,7 @@ pub fn accept_ownership(deps: DepsMut, sender: Addr) -> StdResult { .add_attribute("new_owner", new_owner) .add_attribute("previous_owner", previous_owner); - Ok(Response::new() - .add_event(event) - .add_attribute("action", "steakhub/transfer_ownership")) + Ok(Response::new().add_event(event).add_attribute("action", "steakhub/transfer_ownership")) } pub fn transfer_fee_account( @@ -1119,9 +1073,7 @@ pub fn transfer_fee_account( state.fee_account_type.save(deps.storage, &fee_type)?; - state - .fee_account - .save(deps.storage, &deps.api.addr_validate(&new_fee_account)?)?; + state.fee_account.save(deps.storage, &deps.api.addr_validate(&new_fee_account)?)?; Ok(Response::new().add_attribute("action", "steakhub/transfer_fee_account")) } @@ -1140,9 +1092,7 @@ pub fn update_fee(deps: DepsMut, sender: Addr, new_fee: Decimal) -> StdResult state.max_fee_rate.load(deps.storage)? { - return Err(StdError::generic_err( - "refusing to set fee above maximum set", - )); + return Err(StdError::generic_err("refusing to set fee above maximum set")); } state.fee_rate.save(deps.storage, &new_fee)?; @@ -1159,21 +1109,15 @@ pub fn set_dust_collector( state.assert_owner(deps.storage, &sender)?; if let Some(ref dust_addr) = dust_collector { - state - .dust_collector - .save(deps.storage, &Some(deps.api.addr_validate(dust_addr)?))?; + state.dust_collector.save(deps.storage, &Some(deps.api.addr_validate(dust_addr)?))?; } else { state.dust_collector.save(deps.storage, &None)?; }; - let event = Event::new("steak/set_dust_collector").add_attribute( - "dust_collector", - dust_collector.unwrap_or("-cleared-".into()), - ); + let event = Event::new("steak/set_dust_collector") + .add_attribute("dust_collector", dust_collector.unwrap_or("-cleared-".into())); - Ok(Response::new() - .add_event(event) - .add_attribute("action", "steakhub/set_dust_collector")) + Ok(Response::new().add_event(event).add_attribute("action", "steakhub/set_dust_collector")) } pub fn collect_dust(deps: DepsMut, env: Env, max_tokens: usize) -> StdResult { @@ -1216,12 +1160,8 @@ pub fn redelegate( state.assert_owner(deps.storage, &sender)?; let denom = state.denom.load(deps.storage)?; - let delegation = query_delegation( - &deps.querier, - &validator_from, - &env.contract.address, - &denom, - )?; + let delegation = + query_delegation(&deps.querier, &validator_from, &env.contract.address, &denom)?; let redelegation_msg = SubMsg { id: REPLY_REGISTER_RECEIVED_COINS, @@ -1231,10 +1171,9 @@ pub fn redelegate( reply_on: ReplyOn::Never, }; - state.prev_denom.save( - deps.storage, - &get_denom_balance(&deps.querier, env.contract.address, denom)?, - )?; + state + .prev_denom + .save(deps.storage, &get_denom_balance(&deps.querier, env.contract.address, denom)?)?; let event = Event::new("steak/redelegate") .add_attribute("validator_from", validator_from) @@ -1263,7 +1202,5 @@ pub fn change_token_factory( let event = Event::new("steak/change_token_factory") .add_attribute("token_factory_type", token_factory_type); - Ok(Response::new() - .add_event(event) - .add_attribute("action", "steakhub/change_token_factory")) + Ok(Response::new().add_event(event).add_attribute("action", "steakhub/change_token_factory")) } diff --git a/contracts/hub-tf/src/helpers.rs b/contracts/hub-tf/src/helpers.rs index b15b842..4c5e72e 100644 --- a/contracts/hub-tf/src/helpers.rs +++ b/contracts/hub-tf/src/helpers.rs @@ -1,6 +1,9 @@ use std::str::FromStr; -use cosmwasm_std::{Addr, BalanceResponse, BankQuery, Coin, QuerierWrapper, QueryRequest, StdError, StdResult, Uint128}; +use cosmwasm_std::{ + Addr, BalanceResponse, BankQuery, Coin, QuerierWrapper, QueryRequest, StdError, StdResult, + Uint128, +}; use crate::types::Delegation; /* @@ -10,7 +13,6 @@ pub(crate) fn unwrap_reply(reply: Reply) -> StdResult { } */ - /// Query the amounts of Luna a staker is delegating to a specific validator pub(crate) fn query_delegation( querier: &QuerierWrapper, @@ -30,15 +32,17 @@ pub(crate) fn query_delegation( pub(crate) fn query_all_delegations( querier: &QuerierWrapper, delegator_addr: &Addr, - // _denom: &str, + // _denom: &str, ) -> StdResult> { - Ok(querier.query_all_delegations(delegator_addr)?.into_iter().map( |std_delegation| {Delegation{ - validator: std_delegation.validator.to_string(), - amount: std_delegation.amount.amount.u128(), - denom: std_delegation.amount.denom, - }}).collect()) - - + Ok(querier + .query_all_delegations(delegator_addr)? + .into_iter() + .map(|std_delegation| Delegation { + validator: std_delegation.validator.to_string(), + amount: std_delegation.amount.amount.u128(), + denom: std_delegation.amount.denom, + }) + .collect()) } /// Query the amounts of Luna a staker is delegating to each of the validators specified @@ -73,10 +77,7 @@ pub(crate) fn parse_coin(s: &str) -> StdResult { } } - Err(StdError::generic_err(format!( - "failed to parse coin: {}", - s - ))) + Err(StdError::generic_err(format!("failed to parse coin: {}", s))) } /// Find the amount of a denom sent along a message, assert it is non-zero, and no other denom were @@ -104,7 +105,11 @@ pub(crate) fn parse_received_fund(funds: &[Coin], denom: &str) -> StdResult StdResult { +pub fn get_denom_balance( + querier: &QuerierWrapper, + account_addr: Addr, + denom: String, +) -> StdResult { let balance: BalanceResponse = querier.query(&QueryRequest::Bank(BankQuery::Balance { address: account_addr.to_string(), denom, diff --git a/contracts/hub-tf/src/injective/denom.rs b/contracts/hub-tf/src/injective/denom.rs index 9223df9..07ab8f3 100644 --- a/contracts/hub-tf/src/injective/denom.rs +++ b/contracts/hub-tf/src/injective/denom.rs @@ -1,7 +1,6 @@ // source: https://github.com/White-Whale-Defi-Platform/white-whale-core/blob/feat/tokenfactory-lp/packages/pool-network/src/denom.rs -use std::convert::TryFrom; -use std::convert::TryInto; +use std::convert::{TryFrom, TryInto}; use osmosis_std_derive::CosmwasmExt; @@ -12,14 +11,14 @@ use osmosis_std_derive::CosmwasmExt; /// NOTE: The amount field is an Int which implements the custom method /// signatures required by gogoproto. #[derive( -Clone, -PartialEq, -Eq, -::prost::Message, -serde::Serialize, -serde::Deserialize, -schemars::JsonSchema, -CosmwasmExt, + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, )] #[proto_message(type_url = "/cosmos.base.v1beta1.Coin")] pub struct Coin { @@ -39,14 +38,14 @@ pub struct Coin { /// originally set to be the creator, but this can be changed later. The token /// denom does not indicate the current admin. #[derive( -Clone, -PartialEq, -Eq, -::prost::Message, -serde::Serialize, -serde::Deserialize, -schemars::JsonSchema, -CosmwasmExt, + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, )] #[proto_message(type_url = "/injective.tokenfactory.v1beta1.MsgCreateDenom")] pub struct MsgCreateDenom { @@ -60,14 +59,14 @@ pub struct MsgCreateDenom { /// MsgCreateDenomResponse is the return value of MsgCreateDenom /// It returns the full string of the newly created denom #[derive( -Clone, -PartialEq, -Eq, -::prost::Message, -serde::Serialize, -serde::Deserialize, -schemars::JsonSchema, -CosmwasmExt, + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, )] #[proto_message(type_url = "/injective.tokenfactory.v1beta1.MsgCreateDenomResponse")] pub struct MsgCreateDenomResponse { @@ -78,14 +77,14 @@ pub struct MsgCreateDenomResponse { /// MsgMint is the sdk.Msg type for allowing an admin account to mint /// more of a token. For now, we only support minting to the sender account #[derive( -Clone, -PartialEq, -Eq, -::prost::Message, -serde::Serialize, -serde::Deserialize, -schemars::JsonSchema, -CosmwasmExt, + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, )] #[proto_message(type_url = "/injective.tokenfactory.v1beta1.MsgMint")] pub struct MsgMint { @@ -96,14 +95,14 @@ pub struct MsgMint { } #[derive( -Clone, -PartialEq, -Eq, -::prost::Message, -serde::Serialize, -serde::Deserialize, -schemars::JsonSchema, -CosmwasmExt, + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, )] #[proto_message(type_url = "/injective.tokenfactory.v1beta1.MsgMintResponse")] pub struct MsgMintResponse {} @@ -111,14 +110,14 @@ pub struct MsgMintResponse {} /// MsgBurn is the sdk.Msg type for allowing an admin account to burn /// a token. For now, we only support burning from the sender account. #[derive( -Clone, -PartialEq, -Eq, -::prost::Message, -serde::Serialize, -serde::Deserialize, -schemars::JsonSchema, -CosmwasmExt, + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, )] #[proto_message(type_url = "/injective.tokenfactory.v1beta1.MsgBurn")] pub struct MsgBurn { @@ -129,14 +128,14 @@ pub struct MsgBurn { } #[derive( -Clone, -PartialEq, -Eq, -::prost::Message, -serde::Serialize, -serde::Deserialize, -schemars::JsonSchema, -CosmwasmExt, + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, )] #[proto_message(type_url = "/injective.tokenfactory.v1beta1.MsgBurnResponse")] -pub struct MsgBurnResponse {} \ No newline at end of file +pub struct MsgBurnResponse {} diff --git a/contracts/hub-tf/src/injective/mod.rs b/contracts/hub-tf/src/injective/mod.rs index 396cc23..80617f6 100644 --- a/contracts/hub-tf/src/injective/mod.rs +++ b/contracts/hub-tf/src/injective/mod.rs @@ -1,2 +1,2 @@ // -pub mod denom; \ No newline at end of file +pub mod denom; diff --git a/contracts/hub-tf/src/kujira/denom.rs b/contracts/hub-tf/src/kujira/denom.rs index 231565c..cda290d 100644 --- a/contracts/hub-tf/src/kujira/denom.rs +++ b/contracts/hub-tf/src/kujira/denom.rs @@ -1,7 +1,6 @@ // source: https://github.com/White-Whale-Defi-Platform/white-whale-core/blob/feat/tokenfactory-lp/packages/pool-network/src/denom.rs -use std::convert::TryFrom; -use std::convert::TryInto; +use std::convert::{TryFrom, TryInto}; use osmosis_std_derive::CosmwasmExt; @@ -12,14 +11,14 @@ use osmosis_std_derive::CosmwasmExt; /// NOTE: The amount field is an Int which implements the custom method /// signatures required by gogoproto. #[derive( -Clone, -PartialEq, -Eq, -::prost::Message, -serde::Serialize, -serde::Deserialize, -schemars::JsonSchema, -CosmwasmExt, + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, )] #[proto_message(type_url = "/cosmos.base.v1beta1.Coin")] pub struct Coin { @@ -39,14 +38,14 @@ pub struct Coin { /// originally set to be the creator, but this can be changed later. The token /// denom does not indicate the current admin. #[derive( -Clone, -PartialEq, -Eq, -::prost::Message, -serde::Serialize, -serde::Deserialize, -schemars::JsonSchema, -CosmwasmExt, + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, )] #[proto_message(type_url = "/kujira.denom.MsgCreateDenom")] pub struct MsgCreateDenom { @@ -60,14 +59,14 @@ pub struct MsgCreateDenom { /// MsgCreateDenomResponse is the return value of MsgCreateDenom /// It returns the full string of the newly created denom #[derive( -Clone, -PartialEq, -Eq, -::prost::Message, -serde::Serialize, -serde::Deserialize, -schemars::JsonSchema, -CosmwasmExt, + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, )] #[proto_message(type_url = "/kujira.denom.MsgCreateDenomResponse")] pub struct MsgCreateDenomResponse { @@ -78,14 +77,14 @@ pub struct MsgCreateDenomResponse { /// MsgMint is the sdk.Msg type for allowing an admin account to mint /// more of a token. For now, we only support minting to the sender account #[derive( -Clone, -PartialEq, -Eq, -::prost::Message, -serde::Serialize, -serde::Deserialize, -schemars::JsonSchema, -CosmwasmExt, + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, )] #[proto_message(type_url = "/kujira.denom.MsgMint")] pub struct MsgMint { @@ -93,19 +92,19 @@ pub struct MsgMint { pub sender: ::prost::alloc::string::String, #[prost(message, optional, tag = "2")] pub amount: ::core::option::Option, - #[prost(string, tag = "3")] + #[prost(string, tag = "3")] pub recipient: ::prost::alloc::string::String, } #[derive( -Clone, -PartialEq, -Eq, -::prost::Message, -serde::Serialize, -serde::Deserialize, -schemars::JsonSchema, -CosmwasmExt, + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, )] #[proto_message(type_url = "/kujira.denom.MsgMintResponse")] pub struct MsgMintResponse {} @@ -113,14 +112,14 @@ pub struct MsgMintResponse {} /// MsgBurn is the sdk.Msg type for allowing an admin account to burn /// a token. For now, we only support burning from the sender account. #[derive( -Clone, -PartialEq, -Eq, -::prost::Message, -serde::Serialize, -serde::Deserialize, -schemars::JsonSchema, -CosmwasmExt, + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, )] #[proto_message(type_url = "/kujira.denom.MsgBurn")] pub struct MsgBurn { @@ -131,14 +130,14 @@ pub struct MsgBurn { } #[derive( -Clone, -PartialEq, -Eq, -::prost::Message, -serde::Serialize, -serde::Deserialize, -schemars::JsonSchema, -CosmwasmExt, + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, )] #[proto_message(type_url = "/kujira.denom.MsgBurnResponse")] -pub struct MsgBurnResponse {} \ No newline at end of file +pub struct MsgBurnResponse {} diff --git a/contracts/hub-tf/src/kujira/mod.rs b/contracts/hub-tf/src/kujira/mod.rs index 396cc23..80617f6 100644 --- a/contracts/hub-tf/src/kujira/mod.rs +++ b/contracts/hub-tf/src/kujira/mod.rs @@ -1,2 +1,2 @@ // -pub mod denom; \ No newline at end of file +pub mod denom; diff --git a/contracts/hub-tf/src/math.rs b/contracts/hub-tf/src/math.rs index d3517fe..4b752b9 100644 --- a/contracts/hub-tf/src/math.rs +++ b/contracts/hub-tf/src/math.rs @@ -1,8 +1,6 @@ -use std::{cmp, cmp::Ordering}; -use std::collections::HashMap; +use std::{cmp, cmp::Ordering, collections::HashMap}; use cosmwasm_std::Uint128; - use pfc_steak::hub::Batch; use crate::types::{Delegation, Redelegation, Undelegation}; @@ -32,8 +30,8 @@ pub(crate) fn compute_mint_amount( /// Compute the amount of `native` to unbond for a specific `usteak` burn amount /// -/// There is no way `usteak` total supply is zero when the user is senting a non-zero amount of `usteak` -/// to burn, so we don't need to handle division-by-zero here +/// There is no way `usteak` total supply is zero when the user is senting a non-zero amount of +/// `usteak` to burn, so we don't need to handle division-by-zero here pub(crate) fn compute_unbond_amount( usteak_supply: Uint128, usteak_to_burn: Uint128, @@ -145,10 +143,11 @@ pub(crate) fn compute_redelegations_for_removal( new_redelegations } -/// Compute redelegation moves that will make each validator's delegation the targeted amount (hopefully -/// this sentence makes sense) +/// Compute redelegation moves that will make each validator's delegation the targeted amount +/// (hopefully this sentence makes sense) /// -/// This algorithm does not guarantee the minimal number of moves, but is the best I can some up with... +/// This algorithm does not guarantee the minimal number of moves, but is the best I can some up +/// with... pub(crate) fn compute_redelegations_for_rebalancing( validators_active: Vec, current_delegations: &[Delegation], @@ -169,7 +168,8 @@ pub(crate) fn compute_redelegations_for_rebalancing( for (i, d) in current_delegations.iter().enumerate() { let remainder_for_validator: u128 = u128::from((i + 1) as u128 <= remainder); let native_for_validator = native_per_validator + remainder_for_validator; - // eprintln!("{} amount ={} native={} min={}", d.validator, d.amount, native_for_validator, min_difference); + // eprintln!("{} amount ={} native={} min={}", d.validator, d.amount, native_for_validator, + // min_difference); match d.amount.cmp(&native_for_validator) { Ordering::Greater => { if d.amount - native_for_validator > min_difference.u128() { @@ -179,17 +179,18 @@ pub(crate) fn compute_redelegations_for_rebalancing( &d.denom, )); } - } + }, Ordering::Less => { - if validators_active.contains(&d.validator) && - native_for_validator - d.amount > min_difference.u128() { + if validators_active.contains(&d.validator) + && native_for_validator - d.amount > min_difference.u128() + { dst_delegations.push(Delegation::new( &d.validator, native_for_validator - d.amount, &d.denom, )); } - } + }, Ordering::Equal => (), } } @@ -227,9 +228,9 @@ pub(crate) fn compute_redelegations_for_rebalancing( // Batch logics //-------------------------------------------------------------------------------------------------- -/// If the received native amount after the unbonding period is less than expected, e.g. due to rounding -/// error or the validator(s) being slashed, then deduct the difference in amount evenly from each -/// unreconciled batch. +/// If the received native amount after the unbonding period is less than expected, e.g. due to +/// rounding error or the validator(s) being slashed, then deduct the difference in amount evenly +/// from each unreconciled batch. /// /// The idea of "reconciling" is based on Stader's implementation: /// https://github.com/stader-labs/stader-liquid-token/blob/v0.2.1/contracts/staking/src/contract.rs#L968-L1048 diff --git a/contracts/hub-tf/src/migrations.rs b/contracts/hub-tf/src/migrations.rs index b7ead8f..a28ddad 100644 --- a/contracts/hub-tf/src/migrations.rs +++ b/contracts/hub-tf/src/migrations.rs @@ -1 +1 @@ -// no migrations yet \ No newline at end of file +// no migrations yet diff --git a/contracts/hub-tf/src/osmosis/denom.rs b/contracts/hub-tf/src/osmosis/denom.rs index 4a4ddf4..304d3d8 100644 --- a/contracts/hub-tf/src/osmosis/denom.rs +++ b/contracts/hub-tf/src/osmosis/denom.rs @@ -1,7 +1,6 @@ // source: https://github.com/White-Whale-Defi-Platform/white-whale-core/blob/feat/tokenfactory-lp/packages/pool-network/src/denom.rs -use std::convert::TryFrom; -use std::convert::TryInto; +use std::convert::{TryFrom, TryInto}; use osmosis_std_derive::CosmwasmExt; diff --git a/contracts/hub-tf/src/osmosis/mod.rs b/contracts/hub-tf/src/osmosis/mod.rs index 396cc23..80617f6 100644 --- a/contracts/hub-tf/src/osmosis/mod.rs +++ b/contracts/hub-tf/src/osmosis/mod.rs @@ -1,2 +1,2 @@ // -pub mod denom; \ No newline at end of file +pub mod denom; diff --git a/contracts/hub-tf/src/queries.rs b/contracts/hub-tf/src/queries.rs index 3db704d..55b7212 100644 --- a/contracts/hub-tf/src/queries.rs +++ b/contracts/hub-tf/src/queries.rs @@ -1,17 +1,20 @@ -use std::collections::{BTreeSet, HashSet}; -use std::iter::FromIterator; +use std::{ + collections::{BTreeSet, HashSet}, + iter::FromIterator, +}; -use cosmwasm_std::{ Decimal, Deps, Env, Order, StdResult, Uint128}; +use cosmwasm_std::{Decimal, Deps, Env, Order, StdResult, Uint128}; use cw_storage_plus::Bound; - use pfc_steak::hub::{ Batch, ConfigResponse, PendingBatch, StateResponse, UnbondRequestsByBatchResponseItem, UnbondRequestsByUserResponseItem, }; -use crate::helpers::query_delegations; -use crate::state; -use crate::state::{State, VALIDATORS, VALIDATORS_ACTIVE}; +use crate::{ + helpers::query_delegations, + state, + state::{State, VALIDATORS, VALIDATORS_ACTIVE}, +}; const MAX_LIMIT: u32 = 30; const DEFAULT_LIMIT: u32 = 10; @@ -30,16 +33,12 @@ pub fn config(deps: Deps) -> StdResult { validators_active.insert(validator); } - let validator_active_vec: Vec = Vec::from_iter(validators_active); let paused_validators: Vec = Vec::from_iter(validators); Ok(ConfigResponse { owner: state.owner.load(deps.storage)?.into(), - new_owner: state - .new_owner - .may_load(deps.storage)? - .map(|addr| addr.into()), + new_owner: state.new_owner.may_load(deps.storage)?.map(|addr| addr.into()), steak_token: state.steak_denom.load(deps.storage)?, epoch_period: state.epoch_period.load(deps.storage)?, unbond_period: state.unbond_period.load(deps.storage)?, @@ -50,15 +49,15 @@ pub fn config(deps: Deps) -> StdResult { max_fee_rate: state.max_fee_rate.load(deps.storage)?, validators: validator_active_vec, paused_validators, - dust_collector: state.dust_collector.load(deps.storage)?.map( |a| a.to_string()), - token_factory: Some(state.token_factory_type.load(deps.storage)?.to_string()) + dust_collector: state.dust_collector.load(deps.storage)?.map(|a| a.to_string()), + token_factory: Some(state.token_factory_type.load(deps.storage)?.to_string()), }) } pub fn state(deps: Deps, env: Env) -> StdResult { let state = State::default(); let denom = state.denom.load(deps.storage)?; - let total_usteak = state.steak_minted.load(deps.storage)?;// query_cw20_total_supply(&deps.querier, &steak_token)?; + let total_usteak = state.steak_minted.load(deps.storage)?; // query_cw20_total_supply(&deps.querier, &steak_token)?; let mut validators: HashSet = Default::default(); for res in VALIDATORS.items(deps.storage, None, None, Order::Ascending) { validators.insert(res?); @@ -69,7 +68,8 @@ pub fn state(deps: Deps, env: Env) -> StdResult { } validators.extend(validators_active); let validator_vec: Vec = Vec::from_iter(validators); - let delegations = query_delegations(&deps.querier, &validator_vec, &env.contract.address, &denom)?; + let delegations = + query_delegations(&deps.querier, &validator_vec, &env.contract.address, &denom)?; let total_native: u128 = delegations.iter().map(|d| d.amount).sum(); let exchange_rate = if total_usteak.is_zero() { @@ -128,7 +128,7 @@ pub fn unbond_requests_by_batch( deps.api.addr_validate(&addr_str)?; addr_clone = addr_str; Some(Bound::exclusive(addr_clone.as_str())) - } + }, }; let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize; diff --git a/contracts/hub-tf/src/state.rs b/contracts/hub-tf/src/state.rs index bfaa7ec..5fe6bb4 100644 --- a/contracts/hub-tf/src/state.rs +++ b/contracts/hub-tf/src/state.rs @@ -1,19 +1,22 @@ use cosmwasm_std::{Addr, Coin, Decimal, StdError, StdResult, Storage, Uint128}; use cw_item_set::Set; -use cw_storage_plus::{Index, IndexedMap, IndexList, Item, MultiIndex}; - -use pfc_steak::hub::{Batch, FeeType, PendingBatch, UnbondRequest}; -use pfc_steak::hub_tf::{ TokenFactoryType}; +use cw_storage_plus::{Index, IndexList, IndexedMap, Item, MultiIndex}; +use pfc_steak::{ + hub::{Batch, FeeType, PendingBatch, UnbondRequest}, + hub_tf::TokenFactoryType, +}; pub(crate) const BATCH_KEY_V101: &str = "previous_batches_101"; pub(crate) const BATCH_KEY_OWNER_V101: &str = "previous_batches_owner_101"; pub(crate) const UNBOND_KEY_V101: &str = "unbond_101"; pub(crate) const UNBOND_KEY_USER_V101: &str = "unbond_owner_101"; -/// Validators who currently have delegations. This can be different due to being paused/re-delegations +/// Validators who currently have delegations. This can be different due to being +/// paused/re-delegations pub(crate) const VALIDATORS: Set<&str> = Set::new("validators_001", "validator_counter_001"); /// Validators where we send delegations to. -pub(crate) const VALIDATORS_ACTIVE: Set<&str> = Set::new("validators_active_001", "validator_active_counter_001"); +pub(crate) const VALIDATORS_ACTIVE: Set<&str> = + Set::new("validators_active_001", "validator_active_counter_001"); // pub(crate) const BATCH_KEY_RECONCILED_V101: &str = "previous_batches__reconciled_101"; @@ -50,8 +53,8 @@ pub(crate) struct State<'a> { /// Previous batches that have started unbonding but not yet finished // pub previous_batches: IndexedMap<'a, u64, Batch, PreviousBatchesIndexes<'a>>, /// Users' shares in un-bonding batches - //pub unbond_requests: IndexedMap<'a, (u64, &'a Addr), UnbondRequest, UnbondRequestsIndexes<'a>>, - // pub validators_active: Item<'a, Vec>, + //pub unbond_requests: IndexedMap<'a, (u64, &'a Addr), UnbondRequest, + // UnbondRequestsIndexes<'a>>, pub validators_active: Item<'a, Vec>, /// coins in 'denom' held before reinvest was called. pub prev_denom: Item<'a, Uint128>, @@ -64,7 +67,6 @@ pub(crate) struct State<'a> { pub token_factory_type: Item<'a, TokenFactoryType>, } - impl Default for State<'static> { fn default() -> Self { Self { @@ -89,7 +91,7 @@ impl Default for State<'static> { fee_account_type: Item::new("fee_account_type"), kuji_token_factory: Item::new("kuji_token_factory"), dust_collector: Item::new("dust_collector"), - token_factory_type: Item::new("token_factory_type") + token_factory_type: Item::new("token_factory_type"), } } } @@ -117,7 +119,11 @@ pub fn previous_batches<'a>() -> IndexedMap<'a, u64, Batch, PreviousBatchesIndex IndexedMap::new( BATCH_KEY_V101, PreviousBatchesIndexes { - reconciled: MultiIndex::new(previous_batches_reconciled_idx, BATCH_KEY_V101, BATCH_KEY_OWNER_V101), + reconciled: MultiIndex::new( + previous_batches_reconciled_idx, + BATCH_KEY_V101, + BATCH_KEY_OWNER_V101, + ), }, ) } @@ -126,7 +132,8 @@ pub fn unbond_requests_user_idx(_pk: &[u8], d: &UnbondRequest) -> String { d.user.to_string() } -pub fn unbond_requests<'a>() -> IndexedMap<'a, (u64, &'a str), UnbondRequest, UnbondRequestsIndexes<'a>> { +pub fn unbond_requests<'a>() +-> IndexedMap<'a, (u64, &'a str), UnbondRequest, UnbondRequestsIndexes<'a>> { IndexedMap::new( UNBOND_KEY_V101, UnbondRequestsIndexes { @@ -135,14 +142,13 @@ pub fn unbond_requests<'a>() -> IndexedMap<'a, (u64, &'a str), UnbondRequest, Un ) } - pub struct PreviousBatchesIndexes<'a> { // pk goes to second tuple element pub reconciled: MultiIndex<'a, String, Batch, String>, } impl<'a> IndexList for PreviousBatchesIndexes<'a> { - fn get_indexes(&'_ self) -> Box> + '_> { + fn get_indexes(&'_ self) -> Box> + '_> { let v: Vec<&dyn Index> = vec![&self.reconciled]; Box::new(v.into_iter()) } @@ -154,7 +160,7 @@ pub struct UnbondRequestsIndexes<'a> { } impl<'a> IndexList for UnbondRequestsIndexes<'a> { - fn get_indexes(&'_ self) -> Box> + '_> { + fn get_indexes(&'_ self) -> Box> + '_> { let v: Vec<&dyn Index> = vec![&self.user]; Box::new(v.into_iter()) } diff --git a/contracts/hub-tf/src/testing/custom_querier.rs b/contracts/hub-tf/src/testing/custom_querier.rs index cdb8fb5..222ebd6 100644 --- a/contracts/hub-tf/src/testing/custom_querier.rs +++ b/contracts/hub-tf/src/testing/custom_querier.rs @@ -1,12 +1,12 @@ use cosmwasm_std::{ - Addr, Coin, Empty, from_slice, FullDelegation, Querier, QuerierResult, - QueryRequest, SystemError, WasmQuery, + from_slice, + testing::{BankQuerier, StakingQuerier, MOCK_CONTRACT_ADDR}, + Addr, Coin, Empty, FullDelegation, Querier, QuerierResult, QueryRequest, SystemError, + WasmQuery, }; -use cosmwasm_std::testing::{BankQuerier, MOCK_CONTRACT_ADDR, StakingQuerier}; - -use crate::types::Delegation; use super::helpers::err_unsupported_query; +use crate::types::Delegation; #[derive(Default)] pub(super) struct CustomQuerier { @@ -23,8 +23,8 @@ impl Querier for CustomQuerier { error: format!("Parsing query request: {}", e), request: bin_request.into(), }) - .into(); - } + .into(); + }, }; self.handle_query(&request) } @@ -53,11 +53,9 @@ impl CustomQuerier { pub fn handle_query(&self, request: &QueryRequest) -> QuerierResult { match request { QueryRequest::Wasm(WasmQuery::Smart { - contract_addr: _, - msg, - }) => { - err_unsupported_query(msg) - } + contract_addr: _, + msg, + }) => err_unsupported_query(msg), QueryRequest::Bank(query) => self.bank_querier.query(query), diff --git a/contracts/hub-tf/src/testing/helpers.rs b/contracts/hub-tf/src/testing/helpers.rs index ecfd4da..9a5c6fd 100644 --- a/contracts/hub-tf/src/testing/helpers.rs +++ b/contracts/hub-tf/src/testing/helpers.rs @@ -1,15 +1,14 @@ -use cosmwasm_std::testing::{mock_env, MockApi, MockStorage, MOCK_CONTRACT_ADDR}; use cosmwasm_std::{ - from_binary, Addr, BlockInfo, ContractInfo, Deps, Env, OwnedDeps, QuerierResult, SystemError, - SystemResult, Timestamp, + from_binary, + testing::{mock_env, MockApi, MockStorage, MOCK_CONTRACT_ADDR}, + Addr, BlockInfo, ContractInfo, Deps, Env, OwnedDeps, QuerierResult, SystemError, SystemResult, + Timestamp, }; -use serde::de::DeserializeOwned; - use pfc_steak::hub::QueryMsg; - -use crate::contract::query; +use serde::de::DeserializeOwned; use super::custom_querier::CustomQuerier; +use crate::contract::query; pub(super) fn err_unsupported_query(request: T) -> QuerierResult { SystemResult::Err(SystemError::InvalidRequest { diff --git a/contracts/hub-tf/src/testing/tests.rs b/contracts/hub-tf/src/testing/tests.rs index 9ebc0fd..46e150a 100644 --- a/contracts/hub-tf/src/testing/tests.rs +++ b/contracts/hub-tf/src/testing/tests.rs @@ -1,29 +1,36 @@ use std::str::FromStr; -use cosmwasm_std::testing::{mock_env, mock_info, MockApi, MockStorage, MOCK_CONTRACT_ADDR}; use cosmwasm_std::{ + testing::{mock_env, mock_info, MockApi, MockStorage, MOCK_CONTRACT_ADDR}, to_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, DistributionMsg, Order, OwnedDeps, ReplyOn, StdError, SubMsg, Uint128, WasmMsg, }; - -use pfc_steak::hub::{ - Batch, CallbackMsg, ConfigResponse, PendingBatch, QueryMsg, StateResponse, UnbondRequest, - UnbondRequestsByBatchResponseItem, UnbondRequestsByUserResponseItem, +use pfc_steak::{ + hub::{ + Batch, CallbackMsg, ConfigResponse, PendingBatch, QueryMsg, StateResponse, UnbondRequest, + UnbondRequestsByBatchResponseItem, UnbondRequestsByUserResponseItem, + }, + hub_tf::{ExecuteMsg, InstantiateMsg}, }; -use pfc_steak::hub_tf::{ExecuteMsg, InstantiateMsg}; -use crate::contract::{execute, instantiate, REPLY_REGISTER_RECEIVED_COINS}; -use crate::helpers::{parse_coin, parse_received_fund}; -use crate::math::{ - compute_redelegations_for_rebalancing, compute_redelegations_for_removal, compute_undelegations, +use super::{ + custom_querier::CustomQuerier, + helpers::{mock_dependencies, mock_env_at_timestamp, query_helper}, +}; +use crate::{ + contract::{execute, instantiate, REPLY_REGISTER_RECEIVED_COINS}, + helpers::{parse_coin, parse_received_fund}, + math::{ + compute_redelegations_for_rebalancing, compute_redelegations_for_removal, + compute_undelegations, + }, + state::{previous_batches, unbond_requests, State, VALIDATORS}, + token_factory::{ + denom, + denom::{MsgBurn, MsgCreateDenom, MsgMint}, + }, + types::{Coins, Delegation, Redelegation, Undelegation}, }; -use crate::state::{previous_batches, unbond_requests, State, VALIDATORS}; -use crate::token_factory::denom; -use crate::token_factory::denom::{MsgBurn, MsgCreateDenom, MsgMint}; -use crate::types::{Coins, Delegation, Redelegation, Undelegation}; - -use super::custom_querier::CustomQuerier; -use super::helpers::{mock_dependencies, mock_env_at_timestamp, query_helper}; //-------------------------------------------------------------------------------------------------- // Test setup @@ -47,11 +54,7 @@ fn setup_test() -> OwnedDeps { epoch_period: 259200, // 3 * 24 * 60 * 60 = 3 days unbond_period: 1814400, // 21 * 24 * 60 * 60 = 21 days - validators: vec![ - "alice".to_string(), - "bob".to_string(), - "charlie".to_string(), - ], + validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string()], token_factory: "CosmWasm".to_string(), dust_collector: Some("dusty_1".to_string()), }, @@ -85,11 +88,7 @@ fn setup_test_fee_split() -> OwnedDeps { max_fee_amount: Decimal::from_ratio(20_u128, 100_u128), //20% epoch_period: 259200, // 3 * 24 * 60 * 60 = 3 days unbond_period: 1814400, // 21 * 24 * 60 * 60 = 21 days - validators: vec![ - "alice".to_string(), - "bob".to_string(), - "charlie".to_string(), - ], + validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string()], token_factory: "CosmWasm".to_string(), dust_collector: Some("dusty_2".to_string()), @@ -129,11 +128,7 @@ fn proper_instantiation() { fee_account: "the_fee_man".to_string(), fee_rate: Decimal::from_ratio(10_u128, 100_u128), max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), - validators: vec![ - "alice".to_string(), - "bob".to_string(), - "charlie".to_string(), - ], + validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string(),], paused_validators: vec![], dust_collector: Some("dusty_1".to_string()), token_factory: Some("CosmWasm".to_string()) @@ -176,11 +171,7 @@ fn proper_instantiation() { fee_account: "fee_split_contract".to_string(), fee_rate: Decimal::from_ratio(10_u128, 100_u128), max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), - validators: vec![ - "alice".to_string(), - "bob".to_string(), - "charlie".to_string(), - ], + validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string(),], paused_validators: vec![], dust_collector: Some("dusty_2".to_string()), token_factory: Some("CosmWasm".to_string()) @@ -205,8 +196,8 @@ fn bonding() { ) .unwrap(); - // 3 messages. (switched to 3 so we can 'send' instead of 'transfer' minted tokens, so contract will know about it - // 1 - delegate + // 3 messages. (switched to 3 so we can 'send' instead of 'transfer' minted tokens, so contract + // will know about it 1 - delegate // 2 - mint token (to ourselves) // 3 - send/transfer it assert_eq!(res.messages.len(), 3); @@ -253,7 +244,8 @@ fn bonding() { ); // Bond when there are existing delegations, and Luna:Steak exchange rate is >1 - // Previously user 1 delegated 1,000,000 uluna. We assume we have accumulated 2.5% yield at 1025000 staked + // Previously user 1 delegated 1,000,000 uluna. We assume we have accumulated 2.5% yield at + // 1025000 staked deps.querier.set_staking_delegations(&[ Delegation::new("alice", 341667, "uxyz"), Delegation::new("bob", 341667, "uxyz"), @@ -346,13 +338,8 @@ fn harvesting() { Delegation::new("charlie", 341666, "uxyz"), ]); - let res = execute( - deps.as_mut(), - mock_env(), - mock_info("worker", &[]), - ExecuteMsg::Harvest {}, - ) - .unwrap(); + let res = execute(deps.as_mut(), mock_env(), mock_info("worker", &[]), ExecuteMsg::Harvest {}) + .unwrap(); assert_eq!(res.messages.len(), 4); assert_eq!( @@ -413,12 +400,8 @@ fn reinvesting() { Delegation::new("bob", 333333, "uxyz"), Delegation::new("charlie", 333333, "uxyz"), ]); - state - .prev_denom - .save(deps.as_mut().storage, &Uint128::from(0 as u32)) - .unwrap(); - deps.querier - .set_bank_balances(&[Coin::new(234u128, "uxyz")]); + state.prev_denom.save(deps.as_mut().storage, &Uint128::from(0 as u32)).unwrap(); + deps.querier.set_bank_balances(&[Coin::new(234u128, "uxyz")]); // After the swaps, `unlocked_coins` should contain only uxyz and unknown denoms state @@ -489,12 +472,8 @@ fn reinvesting_fee_split() { Delegation::new("bob", 333333, "uxyz"), Delegation::new("charlie", 333333, "uxyz"), ]); - state - .prev_denom - .save(deps.as_mut().storage, &Uint128::from(0 as u32)) - .unwrap(); - deps.querier - .set_bank_balances(&[Coin::new(234u128, "uxyz")]); + state.prev_denom.save(deps.as_mut().storage, &Uint128::from(0 as u32)).unwrap(); + deps.querier.set_bank_balances(&[Coin::new(234u128, "uxyz")]); // After the swaps, `unlocked_coins` should contain only uxyz and unknown denoms state @@ -530,7 +509,9 @@ fn reinvesting_fee_split() { reply_on: ReplyOn::Never, } ); - let send_msg = pfc_fee_split::fee_split_msg::ExecuteMsg::Deposit { flush: false }; + let send_msg = pfc_fee_split::fee_split_msg::ExecuteMsg::Deposit { + flush: false, + }; assert_eq!( res.messages[1], @@ -571,7 +552,9 @@ fn queuing_unbond() { amount: Uint128::new(69_420), }], ), - ExecuteMsg::Unbond { receiver: None }, + ExecuteMsg::Unbond { + receiver: None, + }, ) .unwrap_err(); @@ -612,7 +595,9 @@ fn queuing_unbond() { amount: Uint128::new(69_420), }], ), - ExecuteMsg::Unbond { receiver: None }, + ExecuteMsg::Unbond { + receiver: None, + }, ) .unwrap(); @@ -633,16 +618,10 @@ fn queuing_unbond() { // The users' unbonding requests should have been saved let ubr1 = unbond_requests() - .load( - deps.as_ref().storage, - (1u64.into(), &Addr::unchecked("user_1").to_string()), - ) + .load(deps.as_ref().storage, (1u64.into(), &Addr::unchecked("user_1").to_string())) .unwrap(); let ubr2 = unbond_requests() - .load( - deps.as_ref().storage, - (1u64.into(), &Addr::unchecked("user_3").to_string()), - ) + .load(deps.as_ref().storage, (1u64.into(), &Addr::unchecked("user_3").to_string())) .unwrap(); assert_eq!( @@ -687,10 +666,7 @@ fn submitting_batch() { Delegation::new("bob", 345_782, "uxyz"), Delegation::new("charlie", 345_781, "uxyz"), ]); - state - .steak_minted - .save(deps.as_mut().storage, &Uint128::new(1_012_043)) - .unwrap(); + state.steak_minted.save(deps.as_mut().storage, &Uint128::new(1_012_043)).unwrap(); // We continue from the contract state at the end of the last test let unbond_reqs = vec![ @@ -710,10 +686,7 @@ fn submitting_batch() { unbond_requests() .save( deps.as_mut().storage, - ( - unbond_request.id.into(), - &Addr::unchecked(unbond_request.user.clone()).as_str(), - ), + (unbond_request.id.into(), &Addr::unchecked(unbond_request.user.clone()).as_str()), unbond_request, ) .unwrap(); @@ -808,9 +781,7 @@ fn submitting_batch() { ); // Previous batch should have been updated - let previous_batch = previous_batches() - .load(deps.as_ref().storage, 1u64.into()) - .unwrap(); + let previous_batch = previous_batches().load(deps.as_ref().storage, 1u64.into()).unwrap(); assert_eq!( previous_batch, Batch { @@ -861,11 +832,7 @@ fn reconciling() { for previous_batch in &previous_batch_list { previous_batches() - .save( - deps.as_mut().storage, - previous_batch.id.into(), - previous_batch, - ) + .save(deps.as_mut().storage, previous_batch.id.into(), previous_batch) .unwrap(); } @@ -889,10 +856,7 @@ fn reconciling() { Coin::new(12345, "uxyz"), Coin::new(234, "ukrw"), Coin::new(345, "uusd"), - Coin::new( - 69420, - "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", - ), + Coin::new(69420, "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B"), ]); execute( @@ -913,9 +877,7 @@ fn reconciling() { // remainder: 0 // batch 2: 1385 - 273 = 1112 // batch 3: 1506 - 273 = 1233 - let batch = previous_batches() - .load(deps.as_ref().storage, 2u64.into()) - .unwrap(); + let batch = previous_batches().load(deps.as_ref().storage, 2u64.into()).unwrap(); assert_eq!( batch, Batch { @@ -927,9 +889,7 @@ fn reconciling() { } ); - let batch = previous_batches() - .load(deps.as_ref().storage, 3u64.into()) - .unwrap(); + let batch = previous_batches().load(deps.as_ref().storage, 3u64.into()).unwrap(); assert_eq!( batch, Batch { @@ -942,14 +902,10 @@ fn reconciling() { ); // Batches 1 and 4 should not have changed - let batch = previous_batches() - .load(deps.as_ref().storage, 1u64.into()) - .unwrap(); + let batch = previous_batches().load(deps.as_ref().storage, 1u64.into()).unwrap(); assert_eq!(batch, previous_batch_list[0]); - let batch = previous_batches() - .load(deps.as_ref().storage, 4u64.into()) - .unwrap(); + let batch = previous_batches().load(deps.as_ref().storage, 4u64.into()).unwrap(); assert_eq!(batch, previous_batch_list[3]); } @@ -994,10 +950,7 @@ fn withdrawing_unbonded() { unbond_requests() .save( deps.as_mut().storage, - ( - unbond_request.id.into(), - &Addr::unchecked(unbond_request.user.clone()).as_str(), - ), + (unbond_request.id.into(), &Addr::unchecked(unbond_request.user.clone()).as_str()), unbond_request, ) .unwrap(); @@ -1030,17 +983,14 @@ fn withdrawing_unbonded() { reconciled: true, total_shares: Uint128::new(56789), amount_unclaimed: Uint128::new(59060), // 1.040 Luna per Steak - est_unbond_end_time: 30000, // reconciled, but not yet finished unbonding; ignored + est_unbond_end_time: 30000, /* reconciled, but not yet finished unbonding; + * ignored */ }, ]; for previous_batch in &previous_batch_list { previous_batches() - .save( - deps.as_mut().storage, - previous_batch.id.into(), - previous_batch, - ) + .save(deps.as_mut().storage, previous_batch.id.into(), previous_batch) .unwrap(); } @@ -1061,7 +1011,9 @@ fn withdrawing_unbonded() { deps.as_mut(), mock_env_at_timestamp(5000), mock_info("user_1", &[]), - ExecuteMsg::WithdrawUnbonded { receiver: None }, + ExecuteMsg::WithdrawUnbonded { + receiver: None, + }, ) .unwrap_err(); @@ -1082,7 +1034,9 @@ fn withdrawing_unbonded() { deps.as_mut(), mock_env_at_timestamp(25000), mock_info("user_1", &[]), - ExecuteMsg::WithdrawUnbonded { receiver: None }, + ExecuteMsg::WithdrawUnbonded { + receiver: None, + }, ) .unwrap(); @@ -1101,9 +1055,7 @@ fn withdrawing_unbonded() { ); // Previous batches should have been updated - let batch = previous_batches() - .load(deps.as_ref().storage, 1u64.into()) - .unwrap(); + let batch = previous_batches().load(deps.as_ref().storage, 1u64.into()).unwrap(); assert_eq!( batch, Batch { @@ -1115,9 +1067,7 @@ fn withdrawing_unbonded() { } ); - let err = previous_batches() - .load(deps.as_ref().storage, 2u64.into()) - .unwrap_err(); + let err = previous_batches().load(deps.as_ref().storage, 2u64.into()).unwrap_err(); assert_eq!( err, StdError::NotFound { @@ -1127,16 +1077,10 @@ fn withdrawing_unbonded() { // User 1's unbond requests in batches 1 and 2 should have been deleted let err1 = unbond_requests() - .load( - deps.as_ref().storage, - (1u64.into(), &Addr::unchecked("user_1").as_str()), - ) + .load(deps.as_ref().storage, (1u64.into(), &Addr::unchecked("user_1").as_str())) .unwrap_err(); let err2 = unbond_requests() - .load( - deps.as_ref().storage, - (1u64.into(), &Addr::unchecked("user_1").as_str()), - ) + .load(deps.as_ref().storage, (1u64.into(), &Addr::unchecked("user_1").as_str())) .unwrap_err(); assert_eq!( @@ -1178,9 +1122,7 @@ fn withdrawing_unbonded() { ); // Batch 1 and user 2's unbonding request should have been purged from storage - let err = previous_batches() - .load(deps.as_ref().storage, 1u64.into()) - .unwrap_err(); + let err = previous_batches().load(deps.as_ref().storage, 1u64.into()).unwrap_err(); assert_eq!( err, StdError::NotFound { @@ -1189,10 +1131,7 @@ fn withdrawing_unbonded() { ); let err = unbond_requests() - .load( - deps.as_ref().storage, - (1u64.into(), &Addr::unchecked("user_3").as_str()), - ) + .load(deps.as_ref().storage, (1u64.into(), &Addr::unchecked("user_3").as_str())) .unwrap_err(); assert_eq!( @@ -1218,10 +1157,7 @@ fn adding_validator() { ) .unwrap_err(); - assert_eq!( - err, - StdError::generic_err("unauthorized: sender is not owner") - ); + assert_eq!(err, StdError::generic_err("unauthorized: sender is not owner")); let err = execute( deps.as_mut(), @@ -1233,10 +1169,7 @@ fn adding_validator() { ) .unwrap_err(); - assert_eq!( - err, - StdError::generic_err("validator is already whitelisted") - ); + assert_eq!(err, StdError::generic_err("validator is already whitelisted")); let res = execute( deps.as_mut(), @@ -1278,10 +1211,7 @@ fn removing_validator() { ) .unwrap_err(); - assert_eq!( - err, - StdError::generic_err("unauthorized: sender is not owner") - ); + assert_eq!(err, StdError::generic_err("unauthorized: sender is not owner")); let err = execute( deps.as_mut(), @@ -1293,10 +1223,7 @@ fn removing_validator() { ) .unwrap_err(); - assert_eq!( - err, - StdError::generic_err("validator is not already whitelisted") - ); + assert_eq!(err, StdError::generic_err("validator is not already whitelisted")); // Target: (341667 + 341667 + 341666) / 2 = 512500 // Remainder: 0 @@ -1352,10 +1279,7 @@ fn transferring_ownership() { ) .unwrap_err(); - assert_eq!( - err, - StdError::generic_err("unauthorized: sender is not owner") - ); + assert_eq!(err, StdError::generic_err("unauthorized: sender is not owner")); let res = execute( deps.as_mut(), @@ -1380,18 +1304,11 @@ fn transferring_ownership() { ) .unwrap_err(); - assert_eq!( - err, - StdError::generic_err("unauthorized: sender is not new owner") - ); + assert_eq!(err, StdError::generic_err("unauthorized: sender is not new owner")); - let res = execute( - deps.as_mut(), - mock_env(), - mock_info("jake", &[]), - ExecuteMsg::AcceptOwnership {}, - ) - .unwrap(); + let res = + execute(deps.as_mut(), mock_env(), mock_info("jake", &[]), ExecuteMsg::AcceptOwnership {}) + .unwrap(); assert_eq!(res.messages.len(), 0); @@ -1414,10 +1331,7 @@ fn splitting_fees() { ) .unwrap_err(); - assert_eq!( - err, - StdError::generic_err("unauthorized: sender is not owner") - ); + assert_eq!(err, StdError::generic_err("unauthorized: sender is not owner")); let err = execute( deps.as_mut(), @@ -1430,10 +1344,7 @@ fn splitting_fees() { ) .unwrap_err(); - assert_eq!( - err, - StdError::generic_err("Invalid Fee type: Wallet or FeeSplit only") - ); + assert_eq!(err, StdError::generic_err("Invalid Fee type: Wallet or FeeSplit only")); execute( deps.as_mut(), @@ -1459,11 +1370,7 @@ fn splitting_fees() { fee_account: "charlie".to_string(), fee_rate: Decimal::from_ratio(10_u128, 100_u128), max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), - validators: vec![ - "alice".to_string(), - "bob".to_string(), - "charlie".to_string(), - ], + validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string(),], paused_validators: vec![], dust_collector: Some("dusty_1".to_string()), token_factory: Some("CosmWasm".to_string()) @@ -1494,11 +1401,7 @@ fn splitting_fees() { fee_account: "contract".to_string(), fee_rate: Decimal::from_ratio(10_u128, 100_u128), max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), - validators: vec![ - "alice".to_string(), - "bob".to_string(), - "charlie".to_string(), - ], + validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string(),], paused_validators: vec![], dust_collector: Some("dusty_1".to_string()), token_factory: Some("CosmWasm".to_string()) @@ -1546,9 +1449,7 @@ fn querying_previous_batches() { //let state = State::default(); for batch in &batches { - previous_batches() - .save(deps.as_mut().storage, batch.id.into(), batch) - .unwrap(); + previous_batches().save(deps.as_mut().storage, batch.id.into(), batch).unwrap(); } // Querying a single batch @@ -1575,10 +1476,7 @@ fn querying_previous_batches() { limit: None, }, ); - assert_eq!( - res, - vec![batches[1].clone(), batches[2].clone(), batches[3].clone()] - ); + assert_eq!(res, vec![batches[1].clone(), batches[2].clone(), batches[3].clone()]); let res: Vec = query_helper( deps.as_ref(), @@ -1649,10 +1547,7 @@ fn querying_unbond_requests() { unbond_requests() .save( deps.as_mut().storage, - ( - unbond_request.id.into(), - &Addr::unchecked(unbond_request.user.clone()).as_str(), - ), + (unbond_request.id.into(), &Addr::unchecked(unbond_request.user.clone()).as_str()), unbond_request, ) .unwrap(); @@ -1693,10 +1588,7 @@ fn querying_unbond_requests() { limit: None, }, ); - assert_eq!( - res, - vec![unbond_reqs[0].clone().into(), unbond_reqs[3].clone().into(),] - ); + assert_eq!(res, vec![unbond_reqs[0].clone().into(), unbond_reqs[3].clone().into(),]); /* for x in unbond_requests().range(deps.as_ref().storage, None, None, Order::Ascending) { let rec = x.unwrap(); @@ -1826,12 +1718,8 @@ fn computing_redelegations_for_rebalancing() { expected, ); - let partially_active = vec![ - "alice".to_string(), - "charlie".to_string(), - "dave".to_string(), - "evan".to_string(), - ]; + let partially_active = + vec!["alice".to_string(), "charlie".to_string(), "dave".to_string(), "evan".to_string()]; let partially_expected = vec![ Redelegation::new("alice", "dave", 10118, "uxyz"), @@ -1875,20 +1763,14 @@ fn parsing_coin() { .unwrap(); assert_eq!( coin, - Coin::new( - 23456, - "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", - ) + Coin::new(23456, "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B",) ); let err = parse_coin("69420").unwrap_err(); assert_eq!(err, StdError::generic_err("failed to parse coin: 69420")); let err = parse_coin("ngmi").unwrap_err(); - assert_eq!( - err, - StdError::generic_err("Parsing u128: cannot parse integer from empty string") - ); + assert_eq!(err, StdError::generic_err("Parsing u128: cannot parse integer from empty string")); } #[test] @@ -1900,10 +1782,7 @@ fn parsing_coins() { assert_eq!(coins.0, vec![Coin::new(12345, "uatom")]); let coins = Coins::from_str("12345uatom,23456uxyz").unwrap(); - assert_eq!( - coins.0, - vec![Coin::new(12345, "uatom"), Coin::new(23456, "uxyz")] - ); + assert_eq!(coins.0, vec![Coin::new(12345, "uatom"), Coin::new(23456, "uxyz")]); } #[test] @@ -1914,53 +1793,29 @@ fn adding_coins() { assert_eq!(coins.0, vec![Coin::new(12345, "uatom")]); coins.add(&Coin::new(23456, "uxyz")).unwrap(); - assert_eq!( - coins.0, - vec![Coin::new(12345, "uatom"), Coin::new(23456, "uxyz")] - ); + assert_eq!(coins.0, vec![Coin::new(12345, "uatom"), Coin::new(23456, "uxyz")]); - coins - .add_many(&Coins::from_str("76543uatom,69420uusd").unwrap()) - .unwrap(); + coins.add_many(&Coins::from_str("76543uatom,69420uusd").unwrap()).unwrap(); assert_eq!( coins.0, - vec![ - Coin::new(88888, "uatom"), - Coin::new(23456, "uxyz"), - Coin::new(69420, "uusd"), - ] + vec![Coin::new(88888, "uatom"), Coin::new(23456, "uxyz"), Coin::new(69420, "uusd"),] ); } #[test] fn receiving_funds() { let err = parse_received_fund(&[], "uxyz").unwrap_err(); - assert_eq!( - err, - StdError::generic_err("must deposit exactly one coin; received 0") - ); + assert_eq!(err, StdError::generic_err("must deposit exactly one coin; received 0")); - let err = parse_received_fund( - &[Coin::new(12345, "uatom"), Coin::new(23456, "uxyz")], - "uxyz", - ) - .unwrap_err(); - assert_eq!( - err, - StdError::generic_err("must deposit exactly one coin; received 2") - ); + let err = parse_received_fund(&[Coin::new(12345, "uatom"), Coin::new(23456, "uxyz")], "uxyz") + .unwrap_err(); + assert_eq!(err, StdError::generic_err("must deposit exactly one coin; received 2")); let err = parse_received_fund(&[Coin::new(12345, "uatom")], "uxyz").unwrap_err(); - assert_eq!( - err, - StdError::generic_err("expected uxyz deposit, received uatom") - ); + assert_eq!(err, StdError::generic_err("expected uxyz deposit, received uatom")); let err = parse_received_fund(&[Coin::new(0, "uxyz")], "uxyz").unwrap_err(); - assert_eq!( - err, - StdError::generic_err("deposit amount must be non-zero") - ); + assert_eq!(err, StdError::generic_err("deposit amount must be non-zero")); let amount = parse_received_fund(&[Coin::new(69420, "uxyz")], "uxyz").unwrap(); assert_eq!(amount, Uint128::new(69420)); @@ -2001,9 +1856,7 @@ fn reconciling_underflow() { }, ]; for previous_batch in &previous_batch_list { - previous_batches() - .save(deps.as_mut().storage, previous_batch.id, previous_batch) - .unwrap(); + previous_batches().save(deps.as_mut().storage, previous_batch.id, previous_batch).unwrap(); } state .unlocked_coins @@ -2024,10 +1877,7 @@ fn reconciling_underflow() { Coin::new(12345, "uatom"), Coin::new(234, "ukrw"), Coin::new(345, "uusd"), - Coin::new( - 69420, - "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", - ), + Coin::new(69420, "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B"), ]); execute( deps.as_mut(), @@ -2073,9 +1923,7 @@ fn reconciling_underflow_second() { }, ]; for previous_batch in &previous_batch_list { - previous_batches() - .save(deps.as_mut().storage, previous_batch.id, previous_batch) - .unwrap(); + previous_batches().save(deps.as_mut().storage, previous_batch.id, previous_batch).unwrap(); } state .unlocked_coins @@ -2096,10 +1944,7 @@ fn reconciling_underflow_second() { Coin::new(12345 - 1323, "uatom"), Coin::new(234, "ukrw"), Coin::new(345, "uusd"), - Coin::new( - 69420, - "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", - ), + Coin::new(69420, "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B"), ]); execute( deps.as_mut(), @@ -2126,8 +1971,8 @@ fn dust_return_denom() { ) .unwrap(); - // 3 messages. (switched to 3 so we can 'send' instead of 'transfer' minted tokens, so contract will know about it - // 1 - delegate + // 3 messages. (switched to 3 so we can 'send' instead of 'transfer' minted tokens, so contract + // will know about it 1 - delegate // 2 - mint token (to ourselves) // 3 - send/transfer it assert_eq!(res.messages.len(), 3); @@ -2174,7 +2019,8 @@ fn dust_return_denom() { ); // Bond when there are existing delegations, and Luna:Steak exchange rate is >1 - // Previously user 1 delegated 1,000,000 uluna. We assume we have accumulated 2.5% yield at 1025000 staked + // Previously user 1 delegated 1,000,000 uluna. We assume we have accumulated 2.5% yield at + // 1025000 staked deps.querier.set_staking_delegations(&[ Delegation::new("alice", 341667, "uxyz"), Delegation::new("bob", 341667, "uxyz"), diff --git a/contracts/hub-tf/src/token_factory/denom.rs b/contracts/hub-tf/src/token_factory/denom.rs index e43d2a9..a7804d5 100644 --- a/contracts/hub-tf/src/token_factory/denom.rs +++ b/contracts/hub-tf/src/token_factory/denom.rs @@ -1,7 +1,6 @@ // source: https://github.com/White-Whale-Defi-Platform/white-whale-core/blob/feat/tokenfactory-lp/packages/pool-network/src/denom.rs -use std::convert::TryFrom; -use std::convert::TryInto; +use std::convert::{TryFrom, TryInto}; use osmosis_std_derive::CosmwasmExt; @@ -12,14 +11,14 @@ use osmosis_std_derive::CosmwasmExt; /// NOTE: The amount field is an Int which implements the custom method /// signatures required by gogoproto. #[derive( -Clone, -PartialEq, -Eq, -::prost::Message, -serde::Serialize, -serde::Deserialize, -schemars::JsonSchema, -CosmwasmExt, + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, )] #[proto_message(type_url = "/cosmos.base.v1beta1.Coin")] pub struct Coin { @@ -39,14 +38,14 @@ pub struct Coin { /// originally set to be the creator, but this can be changed later. The token /// denom does not indicate the current admin. #[derive( -Clone, -PartialEq, -Eq, -::prost::Message, -serde::Serialize, -serde::Deserialize, -schemars::JsonSchema, -CosmwasmExt, + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, )] #[proto_message(type_url = "/cosmwasm.tokenfactory.v1beta1.MsgCreateDenom")] pub struct MsgCreateDenom { @@ -60,14 +59,14 @@ pub struct MsgCreateDenom { /// MsgCreateDenomResponse is the return value of MsgCreateDenom /// It returns the full string of the newly created denom #[derive( -Clone, -PartialEq, -Eq, -::prost::Message, -serde::Serialize, -serde::Deserialize, -schemars::JsonSchema, -CosmwasmExt, + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, )] #[proto_message(type_url = "/cosmwasm.tokenfactory.v1beta1.MsgCreateDenomResponse")] pub struct MsgCreateDenomResponse { @@ -78,14 +77,14 @@ pub struct MsgCreateDenomResponse { /// MsgMint is the sdk.Msg type for allowing an admin account to mint /// more of a token. For now, we only support minting to the sender account #[derive( -Clone, -PartialEq, -Eq, -::prost::Message, -serde::Serialize, -serde::Deserialize, -schemars::JsonSchema, -CosmwasmExt, + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, )] #[proto_message(type_url = "/cosmwasm.tokenfactory.v1beta1.MsgMint")] pub struct MsgMint { @@ -96,14 +95,14 @@ pub struct MsgMint { } #[derive( -Clone, -PartialEq, -Eq, -::prost::Message, -serde::Serialize, -serde::Deserialize, -schemars::JsonSchema, -CosmwasmExt, + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, )] #[proto_message(type_url = "/cosmwasm.tokenfactory.v1beta1.MsgMintResponse")] pub struct MsgMintResponse {} @@ -111,14 +110,14 @@ pub struct MsgMintResponse {} /// MsgBurn is the sdk.Msg type for allowing an admin account to burn /// a token. For now, we only support burning from the sender account. #[derive( -Clone, -PartialEq, -Eq, -::prost::Message, -serde::Serialize, -serde::Deserialize, -schemars::JsonSchema, -CosmwasmExt, + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, )] #[proto_message(type_url = "/cosmwasm.tokenfactory.v1beta1.MsgBurn")] pub struct MsgBurn { @@ -129,14 +128,14 @@ pub struct MsgBurn { } #[derive( -Clone, -PartialEq, -Eq, -::prost::Message, -serde::Serialize, -serde::Deserialize, -schemars::JsonSchema, -CosmwasmExt, + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, )] #[proto_message(type_url = "/cosmwasm.tokenfactory.v1beta1.MsgBurnResponse")] -pub struct MsgBurnResponse {} \ No newline at end of file +pub struct MsgBurnResponse {} diff --git a/contracts/hub-tf/src/token_factory/mod.rs b/contracts/hub-tf/src/token_factory/mod.rs index 396cc23..80617f6 100644 --- a/contracts/hub-tf/src/token_factory/mod.rs +++ b/contracts/hub-tf/src/token_factory/mod.rs @@ -1,2 +1,2 @@ // -pub mod denom; \ No newline at end of file +pub mod denom; diff --git a/contracts/hub/src/contract.rs b/contracts/hub/src/contract.rs index 8842049..a5847b0 100644 --- a/contracts/hub/src/contract.rs +++ b/contracts/hub/src/contract.rs @@ -1,19 +1,22 @@ +use std::convert::TryInto; + use cosmwasm_std::{ entry_point, from_binary, to_binary, Binary, Decimal, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdError, StdResult, }; use cw2::{get_contract_version, set_contract_version, ContractVersion}; use cw20::Cw20ReceiveMsg; -use std::convert::TryInto; - use pfc_steak::hub::{ CallbackMsg, ExecuteMsg, FeeType, InstantiateMsg, MigrateMsg, QueryMsg, ReceiveMsg, }; -use crate::helpers::{get_denom_balance, unwrap_reply}; -use crate::migrations::ConfigV100; -use crate::state::State; -use crate::{execute, queries}; +use crate::{ + execute, + helpers::{get_denom_balance, unwrap_reply}, + migrations::ConfigV100, + queries, + state::State, +}; /// Contract name that is used for migration. pub const CONTRACT_NAME: &str = "steak-hub"; @@ -39,87 +42,91 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S let api = deps.api; match msg { ExecuteMsg::Receive(cw20_msg) => receive(deps, env, info, cw20_msg), - ExecuteMsg::Bond { receiver, exec_msg } => execute::bond( + ExecuteMsg::Bond { + receiver, + exec_msg, + } => execute::bond( deps, env, - receiver - .map(|s| api.addr_validate(&s)) - .transpose()? - .unwrap_or(info.sender), + receiver.map(|s| api.addr_validate(&s)).transpose()?.unwrap_or(info.sender), info.funds, exec_msg, false, ), - ExecuteMsg::BondEx { receiver } => execute::bond( + ExecuteMsg::BondEx { + receiver, + } => execute::bond( deps, env, - receiver - .map(|s| api.addr_validate(&s)) - .transpose()? - .unwrap_or(info.sender), + receiver.map(|s| api.addr_validate(&s)).transpose()?.unwrap_or(info.sender), info.funds, None, true, ), - ExecuteMsg::WithdrawUnbonded { receiver } => execute::withdraw_unbonded( + ExecuteMsg::WithdrawUnbonded { + receiver, + } => execute::withdraw_unbonded( deps, env, info.sender.clone(), - receiver - .map(|s| api.addr_validate(&s)) - .transpose()? - .unwrap_or(info.sender), + receiver.map(|s| api.addr_validate(&s)).transpose()?.unwrap_or(info.sender), ), - ExecuteMsg::WithdrawUnbondedAdmin { address } => { - execute::withdraw_unbonded_admin(deps, env, info.sender, api.addr_validate(&address)?) - } - ExecuteMsg::AddValidator { validator } => { - execute::add_validator(deps, info.sender, validator) - } - ExecuteMsg::RemoveValidator { validator } => { - execute::remove_validator(deps, env, info.sender, validator) - } - ExecuteMsg::RemoveValidatorEx { validator } => { - execute::remove_validator_ex(deps, env, info.sender, validator) - } + ExecuteMsg::WithdrawUnbondedAdmin { + address, + } => execute::withdraw_unbonded_admin(deps, env, info.sender, api.addr_validate(&address)?), + ExecuteMsg::AddValidator { + validator, + } => execute::add_validator(deps, info.sender, validator), + ExecuteMsg::RemoveValidator { + validator, + } => execute::remove_validator(deps, env, info.sender, validator), + ExecuteMsg::RemoveValidatorEx { + validator, + } => execute::remove_validator_ex(deps, env, info.sender, validator), ExecuteMsg::Redelegate { validator_from, validator_to, } => execute::redelegate(deps, env, info.sender, validator_from, validator_to), - ExecuteMsg::TransferOwnership { new_owner } => { - execute::transfer_ownership(deps, info.sender, new_owner) - } + ExecuteMsg::TransferOwnership { + new_owner, + } => execute::transfer_ownership(deps, info.sender, new_owner), ExecuteMsg::AcceptOwnership {} => execute::accept_ownership(deps, info.sender), ExecuteMsg::Harvest {} => execute::harvest(deps, env), - ExecuteMsg::Rebalance { minimum } => execute::rebalance(deps, env, minimum), + ExecuteMsg::Rebalance { + minimum, + } => execute::rebalance(deps, env, minimum), ExecuteMsg::Reconcile {} => execute::reconcile(deps, env), ExecuteMsg::SubmitBatch {} => execute::submit_batch(deps, env), ExecuteMsg::TransferFeeAccount { fee_account_type, new_fee_account, } => execute::transfer_fee_account(deps, info.sender, fee_account_type, new_fee_account), - ExecuteMsg::UpdateFee { new_fee } => execute::update_fee(deps, info.sender, new_fee), + ExecuteMsg::UpdateFee { + new_fee, + } => execute::update_fee(deps, info.sender, new_fee), ExecuteMsg::Callback(callback_msg) => callback(deps, env, info, callback_msg), - ExecuteMsg::PauseValidator { validator } => { - execute::pause_validator(deps, env, info.sender, validator) - } - ExecuteMsg::UnPauseValidator { validator } => { - execute::unpause_validator(deps, env, info.sender, validator) - } - ExecuteMsg::SetUnbondPeriod { unbond_period } => { - execute::set_unbond_period(deps, env, info.sender, unbond_period) - } - ExecuteMsg::SetDustCollector { dust_collector } => { - execute::set_dust_collector(deps, env, info.sender, dust_collector) - } - ExecuteMsg::CollectDust { max_tokens } => { + ExecuteMsg::PauseValidator { + validator, + } => execute::pause_validator(deps, env, info.sender, validator), + ExecuteMsg::UnPauseValidator { + validator, + } => execute::unpause_validator(deps, env, info.sender, validator), + ExecuteMsg::SetUnbondPeriod { + unbond_period, + } => execute::set_unbond_period(deps, env, info.sender, unbond_period), + ExecuteMsg::SetDustCollector { + dust_collector, + } => execute::set_dust_collector(deps, env, info.sender, dust_collector), + ExecuteMsg::CollectDust { + max_tokens, + } => { let max_tokens_usize_r = max_tokens.try_into(); if let Ok(max_tokens_usize) = max_tokens_usize_r { execute::collect_dust(deps, env, max_tokens_usize) } else { Err(StdError::generic_err("max_tokens too large")) } - } + }, ExecuteMsg::ReturnDenom {} => execute::return_denom(deps, env, info.funds), } } @@ -132,7 +139,9 @@ fn receive( ) -> StdResult { let api = deps.api; match from_binary(&cw20_msg.msg)? { - ReceiveMsg::QueueUnbond { receiver } => { + ReceiveMsg::QueueUnbond { + receiver, + } => { let state = State::default(); let steak_token = state.steak_token.load(deps.storage)?; @@ -149,7 +158,7 @@ fn receive( api.addr_validate(&receiver.unwrap_or(cw20_msg.sender))?, cw20_msg.amount, ) - } + }, } } @@ -160,9 +169,7 @@ fn callback( callback_msg: CallbackMsg, ) -> StdResult { if env.contract.address != info.sender { - return Err(StdError::generic_err( - "callbacks can only be invoked by the contract itself", - )); + return Err(StdError::generic_err("callbacks can only be invoked by the contract itself")); } match callback_msg { @@ -176,11 +183,8 @@ pub fn reply(deps: DepsMut, env: Env, reply: Reply) -> StdResult { 1 => execute::register_steak_token(deps, unwrap_reply(reply)?), REPLY_REGISTER_RECEIVED_COINS => { execute::register_received_coins(deps, env, unwrap_reply(reply)?.events) - } - id => Err(StdError::generic_err(format!( - "invalid reply id: {}; must be 1-2", - id - ))), + }, + id => Err(StdError::generic_err(format!("invalid reply id: {}; must be 1-2", id))), } } @@ -191,29 +195,20 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { QueryMsg::State {} => to_binary(&queries::state(deps, env)?), QueryMsg::PendingBatch {} => to_binary(&queries::pending_batch(deps)?), QueryMsg::PreviousBatch(id) => to_binary(&queries::previous_batch(deps, id)?), - QueryMsg::PreviousBatches { start_after, limit } => { - to_binary(&queries::previous_batches(deps, start_after, limit)?) - } - QueryMsg::UnbondRequestsByBatch { - id, + QueryMsg::PreviousBatches { start_after, limit, - } => to_binary(&queries::unbond_requests_by_batch( - deps, + } => to_binary(&queries::previous_batches(deps, start_after, limit)?), + QueryMsg::UnbondRequestsByBatch { id, start_after, limit, - )?), + } => to_binary(&queries::unbond_requests_by_batch(deps, id, start_after, limit)?), QueryMsg::UnbondRequestsByUser { user, start_after, limit, - } => to_binary(&queries::unbond_requests_by_user( - deps, - user, - start_after, - limit, - )?), + } => to_binary(&queries::unbond_requests_by_user(deps, user, start_after, limit)?), } } @@ -237,28 +232,22 @@ pub fn migrate(deps: DepsMut, env: Env, _msg: MigrateMsg) -> StdResult state.fee_account.save(deps.storage, &owner)?; state.max_fee_rate.save(deps.storage, &Decimal::zero())?; state.fee_rate.save(deps.storage, &Decimal::zero())?; - state - .fee_account_type - .save(deps.storage, &FeeType::Wallet)?; + state.fee_account_type.save(deps.storage, &FeeType::Wallet)?; ConfigV100::upgrade_stores(deps.storage, &deps.querier, env.contract.address)?; state.dust_collector.save(deps.storage, &None)?; - } + }, "2.1.4" => { let state = State::default(); ConfigV100::upgrade_stores(deps.storage, &deps.querier, env.contract.address)?; - state - .fee_account_type - .save(deps.storage, &FeeType::Wallet)?; + state.fee_account_type.save(deps.storage, &FeeType::Wallet)?; state.dust_collector.save(deps.storage, &None)?; - } + }, "2.1.5" => { ConfigV100::upgrade_stores(deps.storage, &deps.querier, env.contract.address)?; let state = State::default(); - state - .fee_account_type - .save(deps.storage, &FeeType::Wallet)?; + state.fee_account_type.save(deps.storage, &FeeType::Wallet)?; state.dust_collector.save(deps.storage, &None)?; - } + }, "2.1.6" | "2.1.7" => { let state = State::default(); // note: this is also done in ConfigV100::upgrade @@ -268,29 +257,23 @@ pub fn migrate(deps: DepsMut, env: Env, _msg: MigrateMsg) -> StdResult &get_denom_balance(&deps.querier, env.contract.address, denom)?, )?; - state - .fee_account_type - .save(deps.storage, &FeeType::Wallet)?; + state.fee_account_type.save(deps.storage, &FeeType::Wallet)?; state.dust_collector.save(deps.storage, &None)?; - } + }, "2.1.8" | "2.1.16" => { let state = State::default(); - state - .fee_account_type - .save(deps.storage, &FeeType::Wallet)?; + state.fee_account_type.save(deps.storage, &FeeType::Wallet)?; state.dust_collector.save(deps.storage, &None)?; - } + }, "3.0.1" => { let state = State::default(); state.dust_collector.save(deps.storage, &None)?; - } - _ => {} + }, + _ => {}, }, _ => { - return Err(StdError::generic_err( - "contract name is not the same. aborting {}", - )); - } + return Err(StdError::generic_err("contract name is not the same. aborting {}")); + }, } /* let state = State::default(); diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index 7c16814..f7ca472 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -1,6 +1,4 @@ -use std::collections::HashSet; -use std::iter::FromIterator; -use std::str::FromStr; +use std::{collections::HashSet, iter::FromIterator, str::FromStr}; use cosmwasm_std::{ from_binary, to_binary, Addr, BankMsg, Binary, Coin, CosmosMsg, Decimal, DepsMut, @@ -9,24 +7,28 @@ use cosmwasm_std::{ }; use cw20::{Cw20ExecuteMsg, MinterResponse}; use cw20_base::msg::InstantiateMsg as Cw20InstantiateMsg; - -use pfc_steak::hub::{ - Batch, CallbackMsg, Cw20HookMsg, ExecuteMsg, FeeType, InstantiateMsg, PendingBatch, - UnbondRequest, +use pfc_steak::{ + hub::{ + Batch, CallbackMsg, Cw20HookMsg, ExecuteMsg, FeeType, InstantiateMsg, PendingBatch, + UnbondRequest, + }, + DecimalCheckedOps, }; -use pfc_steak::DecimalCheckedOps; -use crate::contract::{REPLY_INSTANTIATE_TOKEN, REPLY_REGISTER_RECEIVED_COINS}; -use crate::helpers::{ - get_denom_balance, parse_received_fund, query_cw20_total_supply, query_delegation, - query_delegations, -}; -use crate::math::{ - compute_mint_amount, compute_redelegations_for_rebalancing, compute_redelegations_for_removal, - compute_unbond_amount, compute_undelegations, reconcile_batches, +use crate::{ + contract::{REPLY_INSTANTIATE_TOKEN, REPLY_REGISTER_RECEIVED_COINS}, + helpers::{ + get_denom_balance, parse_received_fund, query_cw20_total_supply, query_delegation, + query_delegations, + }, + math::{ + compute_mint_amount, compute_redelegations_for_rebalancing, + compute_redelegations_for_removal, compute_unbond_amount, compute_undelegations, + reconcile_batches, + }, + state::State, + types::{Coins, Delegation, Redelegation}, }; -use crate::state::State; -use crate::types::{Coins, Delegation, Redelegation}; //-------------------------------------------------------------------------------------------------- // Instantiation @@ -45,9 +47,7 @@ pub fn instantiate(deps: DepsMut, env: Env, msg: InstantiateMsg) -> StdResult StdResult StdResult = if mint_it { vec![CosmosMsg::Wasm(WasmMsg::Execute { @@ -241,7 +233,7 @@ pub fn bond( funds: vec![], }) } - } + }, Err(_) => { CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: steak_token.to_string(), @@ -252,7 +244,7 @@ pub fn bond( })?, funds: vec![], }) - } + }, }; vec![ CosmosMsg::Wasm(WasmMsg::Execute { @@ -314,7 +306,8 @@ pub fn harvest(deps: DepsMut, env: Env) -> StdResult { } /// NOTE: -/// 1. When delegation Native denom here, we don't need to use a `SubMsg` to handle the received coins, +/// 1. When delegation Native denom here, we don't need to use a `SubMsg` to handle the received +/// coins, /// because we have already withdrawn all claimable staking rewards previously in the same atomic /// execution. /// 2. Same as with `bond`, in the latest implementation we only delegate staking rewards with the @@ -384,10 +377,12 @@ pub fn reinvest(deps: DepsMut, env: Env) -> StdResult { amount: vec![Coin::new(fee_amount.into(), &denom)], })], FeeType::FeeSplit => { - let msg = pfc_fee_split::fee_split_msg::ExecuteMsg::Deposit { flush: false }; + let msg = pfc_fee_split::fee_split_msg::ExecuteMsg::Deposit { + flush: false, + }; vec![msg.into_cosmos_msg(fee_account, vec![Coin::new(fee_amount.into(), &denom)])?] - } + }, }; Ok(Response::new() .add_message(new_delegation.to_cosmos_msg()) @@ -402,7 +397,8 @@ pub fn reinvest(deps: DepsMut, env: Env) -> StdResult { } } -/// NOTE: a `SubMsgResponse` may contain multiple coin-receiving events, must handle them individually +/// NOTE: a `SubMsgResponse` may contain multiple coin-receiving events, must handle them +/// individually pub fn register_received_coins( deps: DepsMut, env: Env, @@ -419,13 +415,11 @@ pub fn register_received_coins( } let state = State::default(); - state - .unlocked_coins - .update(deps.storage, |coins| -> StdResult<_> { - let mut coins = Coins(coins); - coins.add_many(&received_coins)?; - Ok(coins.0) - })?; + state.unlocked_coins.update(deps.storage, |coins| -> StdResult<_> { + let mut coins = Coins(coins); + coins.add_many(&received_coins)?; + Ok(coins.0) + })?; Ok(Response::new().add_attribute("action", "steakhub/register_received_coins")) } @@ -531,12 +525,8 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { // for unbonding we still need to look at let delegations = query_delegations(&deps.querier, &validators, &env.contract.address, &denom)?; - let delegations_active = query_delegations( - &deps.querier, - &active_validator_list, - &env.contract.address, - &denom, - )?; + let delegations_active = + query_delegations(&deps.querier, &active_validator_list, &env.contract.address, &denom)?; let usteak_supply = query_cw20_total_supply(&deps.querier, &steak_token)?; let amount_to_bond = compute_unbond_amount( @@ -552,10 +542,11 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { // If validators misbehave and get slashed during the unbonding period, the contract can receive // LESS Luna than `amount_to_unbond` when unbonding finishes! // - // In this case, users who invokes `withdraw_unbonded` will have their txs failed as the contract - // does not have enough Luna balance. + // In this case, users who invokes `withdraw_unbonded` will have their txs failed as the + // contract does not have enough Luna balance. // - // I don't have a solution for this... other than to manually fund contract with the slashed amount. + // I don't have a solution for this... other than to manually fund contract with the slashed + // amount. state.previous_batches.save( deps.storage, pending_batch.id, @@ -577,16 +568,15 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { est_unbond_start_time: current_time + epoch_period, }, )?; - state.prev_denom.save( - deps.storage, - &get_denom_balance(&deps.querier, env.contract.address, denom)?, - )?; + state + .prev_denom + .save(deps.storage, &get_denom_balance(&deps.querier, env.contract.address, denom)?)?; let undelegate_submsgs = new_undelegations .iter() .map(|d| SubMsg::reply_on_success(d.to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS)) .collect::>(); - + let event = Event::new("steakhub/unbond_submitted") .add_attribute("time", env.block.time.seconds().to_string()) .add_attribute("height", env.block.height.to_string()) @@ -600,7 +590,7 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { // .add_message(burn_msg) .add_event(event)); } - + let burn_msg = CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: steak_token.into(), msg: to_binary(&Cw20ExecuteMsg::Burn { @@ -645,14 +635,10 @@ pub fn reconcile(deps: DepsMut, env: Env) -> StdResult { let native_expected_unlocked = Coins(unlocked_coins).find(&denom).amount; let native_expected = native_expected_received + native_expected_unlocked; - let native_actual = deps - .querier - .query_balance(&env.contract.address, &denom)? - .amount; + let native_actual = deps.querier.query_balance(&env.contract.address, &denom)?.amount; - let native_to_deduct = native_expected - .checked_sub(native_actual) - .unwrap_or_else(|_| Uint128::zero()); + let native_to_deduct = + native_expected.checked_sub(native_actual).unwrap_or_else(|_| Uint128::zero()); if !native_to_deduct.is_zero() { reconcile_batches(&mut batches, native_expected - native_actual); } @@ -662,19 +648,13 @@ pub fn reconcile(deps: DepsMut, env: Env) -> StdResult { state.previous_batches.save(deps.storage, batch.id, batch)?; } - let ids = batches - .iter() - .map(|b| b.id.to_string()) - .collect::>() - .join(","); + let ids = batches.iter().map(|b| b.id.to_string()).collect::>().join(","); let event = Event::new("steakhub/reconciled") .add_attribute("ids", ids) .add_attribute("native_deducted", native_to_deduct.to_string()); - Ok(Response::new() - .add_event(event) - .add_attribute("action", "steakhub/reconcile")) + Ok(Response::new().add_event(event).add_attribute("action", "steakhub/reconcile")) } pub fn withdraw_unbonded_admin( @@ -719,16 +699,15 @@ pub fn withdraw_unbonded( // - is a _previous_ batch, not a _pending_ batch // - is reconciled // - has finished unbonding - // If not sure whether the batches have been reconciled, the user should first invoke `ExecuteMsg::Reconcile` - // before withdrawing. + // If not sure whether the batches have been reconciled, the user should first invoke + // `ExecuteMsg::Reconcile` before withdrawing. let mut total_native_to_refund = Uint128::zero(); let mut ids: Vec = vec![]; for request in &requests { if let Ok(mut batch) = state.previous_batches.load(deps.storage, request.id) { if batch.reconciled && batch.est_unbond_end_time < current_time { - let native_to_refund = batch - .amount_unclaimed - .multiply_ratio(request.shares, batch.total_shares); + let native_to_refund = + batch.amount_unclaimed.multiply_ratio(request.shares, batch.total_shares); ids.push(request.id.to_string()); @@ -739,14 +718,10 @@ pub fn withdraw_unbonded( if batch.total_shares.is_zero() { state.previous_batches.remove(deps.storage, request.id)?; } else { - state - .previous_batches - .save(deps.storage, batch.id, &batch)?; + state.previous_batches.save(deps.storage, batch.id, &batch)?; } - state - .unbond_requests - .remove(deps.storage, (request.id, &user))?; + state.unbond_requests.remove(deps.storage, (request.id, &user))?; } } } @@ -789,10 +764,9 @@ pub fn rebalance(deps: DepsMut, env: Env, minimum: Uint128) -> StdResult StdResul if !validators_active.contains(&validator) { validators_active.push(validator.clone()); } - state - .validators_active - .save(deps.storage, &validators_active)?; + state.validators_active.save(deps.storage, &validators_active)?; let event = Event::new("steakhub/validator_added").add_attribute("validator", validator); - Ok(Response::new() - .add_event(event) - .add_attribute("action", "steakhub/add_validator")) + Ok(Response::new().add_event(event).add_attribute("action", "steakhub/add_validator")) } pub fn remove_validator( @@ -849,9 +819,7 @@ pub fn remove_validator( let validators = state.validators.update(deps.storage, |mut validators| { if !validators.contains(&validator) { - return Err(StdError::generic_err( - "validator is not already whitelisted", - )); + return Err(StdError::generic_err("validator is not already whitelisted")); } validators.retain(|v| *v != validator); Ok(validators) @@ -860,9 +828,7 @@ pub fn remove_validator( if !validators_active.contains(&validator) { validators_active.push(validator.clone()); } - state - .validators_active - .save(deps.storage, &validators_active)?; + state.validators_active.save(deps.storage, &validators_active)?; let delegations = query_delegations(&deps.querier, &validators, &env.contract.address, &denom)?; let delegation_to_remove = @@ -870,10 +836,9 @@ pub fn remove_validator( let new_redelegations = compute_redelegations_for_removal(&delegation_to_remove, &delegations, &denom); - state.prev_denom.save( - deps.storage, - &get_denom_balance(&deps.querier, env.contract.address, denom)?, - )?; + state + .prev_denom + .save(deps.storage, &get_denom_balance(&deps.querier, env.contract.address, denom)?)?; let redelegate_submsgs = new_redelegations .iter() @@ -900,9 +865,7 @@ pub fn remove_validator_ex( state.validators.update(deps.storage, |mut validators| { if !validators.contains(&validator) { - return Err(StdError::generic_err( - "validator is not already whitelisted", - )); + return Err(StdError::generic_err("validator is not already whitelisted")); } validators.retain(|v| *v != validator); Ok(validators) @@ -910,9 +873,7 @@ pub fn remove_validator_ex( let event = Event::new("steak/validator_removed_ex").add_attribute("validator", validator); - Ok(Response::new() - .add_event(event) - .add_attribute("action", "steakhub/remove_validator_ex")) + Ok(Response::new().add_event(event).add_attribute("action", "steakhub/remove_validator_ex")) } pub fn pause_validator( @@ -925,23 +886,17 @@ pub fn pause_validator( state.assert_owner(deps.storage, &sender)?; - state - .validators_active - .update(deps.storage, |mut validators| { - if !validators.contains(&validator) { - return Err(StdError::generic_err( - "validator is not already whitelisted", - )); - } - validators.retain(|v| *v != validator); - Ok(validators) - })?; + state.validators_active.update(deps.storage, |mut validators| { + if !validators.contains(&validator) { + return Err(StdError::generic_err("validator is not already whitelisted")); + } + validators.retain(|v| *v != validator); + Ok(validators) + })?; let event = Event::new("steak/pause_validator").add_attribute("validator", validator); - Ok(Response::new() - .add_event(event) - .add_attribute("action", "steakhub/pause_validator")) + Ok(Response::new().add_event(event).add_attribute("action", "steakhub/pause_validator")) } pub fn unpause_validator( @@ -957,15 +912,11 @@ pub fn unpause_validator( if !validators_active.contains(&validator) { validators_active.push(validator.clone()); } - state - .validators_active - .save(deps.storage, &validators_active)?; + state.validators_active.save(deps.storage, &validators_active)?; let event = Event::new("steak/unpause_validator").add_attribute("validator", validator); - Ok(Response::new() - .add_event(event) - .add_attribute("action", "steakhub/unpause_validator")) + Ok(Response::new().add_event(event).add_attribute("action", "steakhub/unpause_validator")) } pub fn set_unbond_period( @@ -981,18 +932,14 @@ pub fn set_unbond_period( let event = Event::new("steak/set_unbond_period") .add_attribute("unbond_period", format!("{}", unbond_period)); - Ok(Response::new() - .add_event(event) - .add_attribute("action", "steakhub/set_unbond_period")) + Ok(Response::new().add_event(event).add_attribute("action", "steakhub/set_unbond_period")) } pub fn transfer_ownership(deps: DepsMut, sender: Addr, new_owner: String) -> StdResult { let state = State::default(); state.assert_owner(deps.storage, &sender)?; - state - .new_owner - .save(deps.storage, &deps.api.addr_validate(&new_owner)?)?; + state.new_owner.save(deps.storage, &deps.api.addr_validate(&new_owner)?)?; Ok(Response::new().add_attribute("action", "steakhub/transfer_ownership")) } @@ -1004,9 +951,7 @@ pub fn accept_ownership(deps: DepsMut, sender: Addr) -> StdResult { let new_owner = state.new_owner.load(deps.storage)?; if sender != new_owner { - return Err(StdError::generic_err( - "unauthorized: sender is not new owner", - )); + return Err(StdError::generic_err("unauthorized: sender is not new owner")); } state.owner.save(deps.storage, &sender)?; @@ -1016,9 +961,7 @@ pub fn accept_ownership(deps: DepsMut, sender: Addr) -> StdResult { .add_attribute("new_owner", new_owner) .add_attribute("previous_owner", previous_owner); - Ok(Response::new() - .add_event(event) - .add_attribute("action", "steakhub/transfer_ownership")) + Ok(Response::new().add_event(event).add_attribute("action", "steakhub/transfer_ownership")) } pub fn transfer_fee_account( @@ -1035,9 +978,7 @@ pub fn transfer_fee_account( state.fee_account_type.save(deps.storage, &fee_type)?; - state - .fee_account - .save(deps.storage, &deps.api.addr_validate(&new_fee_account)?)?; + state.fee_account.save(deps.storage, &deps.api.addr_validate(&new_fee_account)?)?; Ok(Response::new().add_attribute("action", "steakhub/transfer_fee_account")) } @@ -1056,9 +997,7 @@ pub fn update_fee(deps: DepsMut, sender: Addr, new_fee: Decimal) -> StdResult state.max_fee_rate.load(deps.storage)? { - return Err(StdError::generic_err( - "refusing to set fee above maximum set", - )); + return Err(StdError::generic_err("refusing to set fee above maximum set")); } state.fee_rate.save(deps.storage, &new_fee)?; @@ -1075,20 +1014,14 @@ pub fn set_dust_collector( state.assert_owner(deps.storage, &sender)?; if let Some(ref dust_addr) = dust_collector { - state - .dust_collector - .save(deps.storage, &Some(deps.api.addr_validate(dust_addr)?))?; + state.dust_collector.save(deps.storage, &Some(deps.api.addr_validate(dust_addr)?))?; } else { state.dust_collector.save(deps.storage, &None)?; }; - let event = Event::new("steak/set_dust_collector").add_attribute( - "dust_collector", - dust_collector.unwrap_or("-cleared-".into()), - ); + let event = Event::new("steak/set_dust_collector") + .add_attribute("dust_collector", dust_collector.unwrap_or("-cleared-".into())); - Ok(Response::new() - .add_event(event) - .add_attribute("action", "steakhub/set_dust_collector")) + Ok(Response::new().add_event(event).add_attribute("action", "steakhub/set_dust_collector")) } pub fn collect_dust(deps: DepsMut, _env: Env, _max_tokens: usize) -> StdResult { @@ -1123,12 +1056,8 @@ pub fn redelegate( state.assert_owner(deps.storage, &sender)?; let denom = state.denom.load(deps.storage)?; - let delegation = query_delegation( - &deps.querier, - &validator_from, - &env.contract.address, - &denom, - )?; + let delegation = + query_delegation(&deps.querier, &validator_from, &env.contract.address, &denom)?; let redelegation_msg = SubMsg::reply_on_success( Redelegation::new(&validator_from, &validator_to, delegation.amount, &denom) @@ -1136,10 +1065,9 @@ pub fn redelegate( REPLY_REGISTER_RECEIVED_COINS, ); - state.prev_denom.save( - deps.storage, - &get_denom_balance(&deps.querier, env.contract.address, denom)?, - )?; + state + .prev_denom + .save(deps.storage, &get_denom_balance(&deps.querier, env.contract.address, denom)?)?; let event = Event::new("steak/redelegate") .add_attribute("validator_from", validator_from) diff --git a/contracts/hub/src/helpers.rs b/contracts/hub/src/helpers.rs index 7e83105..3a1de78 100644 --- a/contracts/hub/src/helpers.rs +++ b/contracts/hub/src/helpers.rs @@ -1,7 +1,10 @@ use std::str::FromStr; -use cosmwasm_std::{Addr, BalanceResponse, BankQuery, Coin, QuerierWrapper, QueryRequest, Reply, StdError, StdResult, SubMsgResponse, Uint128}; -use cw20::{ Cw20QueryMsg, TokenInfoResponse}; +use cosmwasm_std::{ + Addr, BalanceResponse, BankQuery, Coin, QuerierWrapper, QueryRequest, Reply, StdError, + StdResult, SubMsgResponse, Uint128, +}; +use cw20::{Cw20QueryMsg, TokenInfoResponse}; use crate::types::Delegation; @@ -69,10 +72,7 @@ pub(crate) fn parse_coin(s: &str) -> StdResult { } } - Err(StdError::generic_err(format!( - "failed to parse coin: {}", - s - ))) + Err(StdError::generic_err(format!("failed to parse coin: {}", s))) } /// Find the amount of a denom sent along a message, assert it is non-zero, and no other denom were @@ -100,7 +100,11 @@ pub(crate) fn parse_received_fund(funds: &[Coin], denom: &str) -> StdResult StdResult { +pub fn get_denom_balance( + querier: &QuerierWrapper, + account_addr: Addr, + denom: String, +) -> StdResult { let balance: BalanceResponse = querier.query(&QueryRequest::Bank(BankQuery::Balance { address: account_addr.to_string(), denom, diff --git a/contracts/hub/src/lib.rs b/contracts/hub/src/lib.rs index f2c0ace..c05dd01 100644 --- a/contracts/hub/src/lib.rs +++ b/contracts/hub/src/lib.rs @@ -8,6 +8,6 @@ pub mod queries; pub mod state; pub mod types; +mod migrations; #[cfg(test)] mod testing; -mod migrations; diff --git a/contracts/hub/src/math.rs b/contracts/hub/src/math.rs index d3517fe..4b752b9 100644 --- a/contracts/hub/src/math.rs +++ b/contracts/hub/src/math.rs @@ -1,8 +1,6 @@ -use std::{cmp, cmp::Ordering}; -use std::collections::HashMap; +use std::{cmp, cmp::Ordering, collections::HashMap}; use cosmwasm_std::Uint128; - use pfc_steak::hub::Batch; use crate::types::{Delegation, Redelegation, Undelegation}; @@ -32,8 +30,8 @@ pub(crate) fn compute_mint_amount( /// Compute the amount of `native` to unbond for a specific `usteak` burn amount /// -/// There is no way `usteak` total supply is zero when the user is senting a non-zero amount of `usteak` -/// to burn, so we don't need to handle division-by-zero here +/// There is no way `usteak` total supply is zero when the user is senting a non-zero amount of +/// `usteak` to burn, so we don't need to handle division-by-zero here pub(crate) fn compute_unbond_amount( usteak_supply: Uint128, usteak_to_burn: Uint128, @@ -145,10 +143,11 @@ pub(crate) fn compute_redelegations_for_removal( new_redelegations } -/// Compute redelegation moves that will make each validator's delegation the targeted amount (hopefully -/// this sentence makes sense) +/// Compute redelegation moves that will make each validator's delegation the targeted amount +/// (hopefully this sentence makes sense) /// -/// This algorithm does not guarantee the minimal number of moves, but is the best I can some up with... +/// This algorithm does not guarantee the minimal number of moves, but is the best I can some up +/// with... pub(crate) fn compute_redelegations_for_rebalancing( validators_active: Vec, current_delegations: &[Delegation], @@ -169,7 +168,8 @@ pub(crate) fn compute_redelegations_for_rebalancing( for (i, d) in current_delegations.iter().enumerate() { let remainder_for_validator: u128 = u128::from((i + 1) as u128 <= remainder); let native_for_validator = native_per_validator + remainder_for_validator; - // eprintln!("{} amount ={} native={} min={}", d.validator, d.amount, native_for_validator, min_difference); + // eprintln!("{} amount ={} native={} min={}", d.validator, d.amount, native_for_validator, + // min_difference); match d.amount.cmp(&native_for_validator) { Ordering::Greater => { if d.amount - native_for_validator > min_difference.u128() { @@ -179,17 +179,18 @@ pub(crate) fn compute_redelegations_for_rebalancing( &d.denom, )); } - } + }, Ordering::Less => { - if validators_active.contains(&d.validator) && - native_for_validator - d.amount > min_difference.u128() { + if validators_active.contains(&d.validator) + && native_for_validator - d.amount > min_difference.u128() + { dst_delegations.push(Delegation::new( &d.validator, native_for_validator - d.amount, &d.denom, )); } - } + }, Ordering::Equal => (), } } @@ -227,9 +228,9 @@ pub(crate) fn compute_redelegations_for_rebalancing( // Batch logics //-------------------------------------------------------------------------------------------------- -/// If the received native amount after the unbonding period is less than expected, e.g. due to rounding -/// error or the validator(s) being slashed, then deduct the difference in amount evenly from each -/// unreconciled batch. +/// If the received native amount after the unbonding period is less than expected, e.g. due to +/// rounding error or the validator(s) being slashed, then deduct the difference in amount evenly +/// from each unreconciled batch. /// /// The idea of "reconciling" is based on Stader's implementation: /// https://github.com/stader-labs/stader-liquid-token/blob/v0.2.1/contracts/staking/src/contract.rs#L968-L1048 diff --git a/contracts/hub/src/migrations.rs b/contracts/hub/src/migrations.rs index c327a5d..6b86e7b 100644 --- a/contracts/hub/src/migrations.rs +++ b/contracts/hub/src/migrations.rs @@ -1,11 +1,13 @@ -use crate::state::{State, BATCH_KEY_V101}; -use crate::types::BooleanKey; use cosmwasm_std::{Addr, Order, QuerierWrapper, StdError, StdResult, Storage, Uint128}; use cw_storage_plus::{Index, IndexList, IndexedMap, MultiIndex}; use pfc_steak::hub::Batch; - use serde::{Deserialize, Serialize}; -use crate::helpers::get_denom_balance; + +use crate::{ + helpers::get_denom_balance, + state::{State, BATCH_KEY_V101}, + types::BooleanKey, +}; const BATCH_KEY_V100: &str = "previous_batches"; const BATCH_KEY_RECONCILED_V100: &str = "previous_batches__reconciled"; @@ -28,11 +30,13 @@ pub struct BatchV100 { pub struct ConfigV100 {} impl ConfigV100 { - pub fn upgrade_stores(storage: &mut dyn Storage, querier: &QuerierWrapper, contract_addr: Addr,) -> StdResult { + pub fn upgrade_stores( + storage: &mut dyn Storage, + querier: &QuerierWrapper, + contract_addr: Addr, + ) -> StdResult { if BATCH_KEY_V101 == BATCH_KEY_V100 { - Err(StdError::generic_err( - "STEAK: Migration Failed. Config keys are the same", - )) + Err(StdError::generic_err("STEAK: Migration Failed. Config keys are the same")) } else { let pb_indexes_v100 = PreviousBatchesIndexesV100 { reconciled: MultiIndex::new( @@ -46,9 +50,7 @@ impl ConfigV100 { IndexedMap::new(BATCH_KEY_V100, pb_indexes_v100); let state = State::default(); let denom = state.denom.load(storage)?; - state.prev_denom.save(storage, &get_denom_balance(querier,contract_addr, denom)?)?; - - + state.prev_denom.save(storage, &get_denom_balance(querier, contract_addr, denom)?)?; let old_batches = old .range(storage, None, None, Order::Ascending) @@ -59,7 +61,7 @@ impl ConfigV100 { }) .collect::>>()?; - { + { old_batches.into_iter().for_each(|v| { { let batch: Batch = Batch { diff --git a/contracts/hub/src/queries.rs b/contracts/hub/src/queries.rs index 09ac999..d308d76 100644 --- a/contracts/hub/src/queries.rs +++ b/contracts/hub/src/queries.rs @@ -1,24 +1,29 @@ -use std::collections::{BTreeSet, HashSet}; -use std::iter::FromIterator; +use std::{ + collections::{BTreeSet, HashSet}, + iter::FromIterator, +}; use cosmwasm_std::{Addr, Decimal, Deps, Env, Order, StdResult, Uint128}; use cw_storage_plus::{Bound, CwIntKey}; - use pfc_steak::hub::{ Batch, ConfigResponse, PendingBatch, StateResponse, UnbondRequestsByBatchResponseItem, UnbondRequestsByUserResponseItem, }; -use crate::helpers::{query_cw20_total_supply, query_delegations}; -use crate::state::State; +use crate::{ + helpers::{query_cw20_total_supply, query_delegations}, + state::State, +}; const MAX_LIMIT: u32 = 30; const DEFAULT_LIMIT: u32 = 10; pub fn config(deps: Deps) -> StdResult { let state = State::default(); - let mut validators: BTreeSet = BTreeSet::from_iter(state.validators.load(deps.storage)?); - let validators_active: BTreeSet = BTreeSet::from_iter(state.validators_active.load(deps.storage)?); + let mut validators: BTreeSet = + BTreeSet::from_iter(state.validators.load(deps.storage)?); + let validators_active: BTreeSet = + BTreeSet::from_iter(state.validators_active.load(deps.storage)?); for v in validators_active.iter() { validators.remove(v); @@ -28,10 +33,7 @@ pub fn config(deps: Deps) -> StdResult { Ok(ConfigResponse { owner: state.owner.load(deps.storage)?.into(), - new_owner: state - .new_owner - .may_load(deps.storage)? - .map(|addr| addr.into()), + new_owner: state.new_owner.may_load(deps.storage)?.map(|addr| addr.into()), steak_token: state.steak_token.load(deps.storage)?.into(), epoch_period: state.epoch_period.load(deps.storage)?, unbond_period: state.unbond_period.load(deps.storage)?, @@ -42,8 +44,8 @@ pub fn config(deps: Deps) -> StdResult { max_fee_rate: state.max_fee_rate.load(deps.storage)?, validators: validator_active_vec, paused_validators, - dust_collector:None, - token_factory:None + dust_collector: None, + token_factory: None, }) } @@ -55,11 +57,13 @@ pub fn state(deps: Deps, env: Env) -> StdResult { let total_usteak = query_cw20_total_supply(&deps.querier, &steak_token)?; let mut validators: HashSet = HashSet::from_iter(state.validators.load(deps.storage)?); - let validators_active: HashSet = HashSet::from_iter(state.validators_active.load(deps.storage)?); + let validators_active: HashSet = + HashSet::from_iter(state.validators_active.load(deps.storage)?); validators.extend(validators_active); let validator_vec: Vec = Vec::from_iter(validators); - let delegations = query_delegations(&deps.querier, &validator_vec, &env.contract.address, &denom)?; + let delegations = + query_delegations(&deps.querier, &validator_vec, &env.contract.address, &denom)?; let total_native: u128 = delegations.iter().map(|d| d.amount).sum(); let exchange_rate = if total_usteak.is_zero() { @@ -121,7 +125,7 @@ pub fn unbond_requests_by_batch( Some(addr_str) => { addr = deps.api.addr_validate(&addr_str)?; Some(Bound::exclusive(&addr)) - } + }, }; let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize; diff --git a/contracts/hub/src/state.rs b/contracts/hub/src/state.rs index 2a0cadd..597d56d 100644 --- a/contracts/hub/src/state.rs +++ b/contracts/hub/src/state.rs @@ -1,6 +1,5 @@ use cosmwasm_std::{Addr, Coin, Decimal, StdError, StdResult, Storage, Uint128}; use cw_storage_plus::{Index, IndexList, IndexedMap, Item, MultiIndex}; - use pfc_steak::hub::{Batch, FeeType, PendingBatch, UnbondRequest}; use crate::types::BooleanKey; @@ -12,7 +11,7 @@ pub(crate) struct State<'a> { pub owner: Item<'a, Addr>, /// Pending ownership transfer, awaiting acceptance by the new owner pub new_owner: Item<'a, Addr>, - pub fee_account_type: Item<'a,FeeType>, + pub fee_account_type: Item<'a, FeeType>, /// Account to send fees to pub fee_account: Item<'a, Addr>, /// Current fee rate @@ -80,7 +79,7 @@ impl Default for State<'static> { validators_active: Item::new("validators_active"), prev_denom: Item::new("prev_denom"), fee_account_type: Item::new("fee_account_type"), - dust_collector: Item::new("dust_collector") + dust_collector: Item::new("dust_collector"), } } } diff --git a/contracts/hub/src/testing/custom_querier.rs b/contracts/hub/src/testing/custom_querier.rs index b120b65..18f13c7 100644 --- a/contracts/hub/src/testing/custom_querier.rs +++ b/contracts/hub/src/testing/custom_querier.rs @@ -1,17 +1,16 @@ use std::collections::HashMap; -use cosmwasm_std::testing::{BankQuerier, StakingQuerier, MOCK_CONTRACT_ADDR}; use cosmwasm_std::{ - from_binary, from_slice, Addr, Coin, Empty, FullDelegation, Querier, QuerierResult, - QueryRequest, SystemError, WasmQuery, + from_binary, from_slice, + testing::{BankQuerier, StakingQuerier, MOCK_CONTRACT_ADDR}, + Addr, Coin, Empty, FullDelegation, Querier, QuerierResult, QueryRequest, SystemError, + WasmQuery, }; use cw20::Cw20QueryMsg; +use super::{cw20_querier::Cw20Querier, helpers::err_unsupported_query}; use crate::types::Delegation; -use super::cw20_querier::Cw20Querier; -use super::helpers::err_unsupported_query; - #[derive(Default)] pub(super) struct CustomQuerier { pub cw20_querier: Cw20Querier, @@ -28,7 +27,7 @@ impl Querier for CustomQuerier { error: format!("Parsing query request: {}", e), request: bin_request.into(), }) - .into() + .into(); }, }; self.handle_query(&request) @@ -51,9 +50,7 @@ impl CustomQuerier { } pub fn set_cw20_total_supply(&mut self, token: &str, total_supply: u128) { - self.cw20_querier - .total_supplies - .insert(token.to_string(), total_supply); + self.cw20_querier.total_supplies.insert(token.to_string(), total_supply); } pub fn set_bank_balances(&mut self, balances: &[Coin]) { diff --git a/contracts/hub/src/testing/cw20_querier.rs b/contracts/hub/src/testing/cw20_querier.rs index b80115a..f9de6c7 100644 --- a/contracts/hub/src/testing/cw20_querier.rs +++ b/contracts/hub/src/testing/cw20_querier.rs @@ -51,7 +51,10 @@ impl Cw20Querier { let balance = contract_balances .get(address) .ok_or_else(|| SystemError::InvalidRequest { - error: format!("[mock] balance not set for cw20 `{}` and user `{}`", contract_addr, address), + error: format!( + "[mock] balance not set for cw20 `{}` and user `{}`", + contract_addr, address + ), request: Default::default(), }) .unwrap(); diff --git a/contracts/hub/src/testing/helpers.rs b/contracts/hub/src/testing/helpers.rs index 172070f..d48a360 100644 --- a/contracts/hub/src/testing/helpers.rs +++ b/contracts/hub/src/testing/helpers.rs @@ -1,15 +1,14 @@ -use cosmwasm_std::testing::{mock_env, MockApi, MockStorage, MOCK_CONTRACT_ADDR}; use cosmwasm_std::{ - from_binary, Addr, BlockInfo, ContractInfo, Deps, Env, OwnedDeps, QuerierResult, SystemError, - SystemResult, Timestamp, + from_binary, + testing::{mock_env, MockApi, MockStorage, MOCK_CONTRACT_ADDR}, + Addr, BlockInfo, ContractInfo, Deps, Env, OwnedDeps, QuerierResult, SystemError, SystemResult, + Timestamp, }; -use serde::de::DeserializeOwned; - use pfc_steak::hub::QueryMsg; - -use crate::contract::query; +use serde::de::DeserializeOwned; use super::custom_querier::CustomQuerier; +use crate::contract::query; pub(super) fn err_unsupported_query(request: T) -> QuerierResult { SystemResult::Err(SystemError::InvalidRequest { diff --git a/contracts/hub/src/testing/tests.rs b/contracts/hub/src/testing/tests.rs index 4ea9d87..1e3e243 100644 --- a/contracts/hub/src/testing/tests.rs +++ b/contracts/hub/src/testing/tests.rs @@ -1,29 +1,34 @@ use std::str::FromStr; use cosmwasm_std::{ - Addr, BankMsg, Coin, CosmosMsg, Decimal, DistributionMsg, Event, Order, OwnedDeps, Reply, - ReplyOn, StdError, SubMsg, SubMsgResponse, to_binary, Uint128, WasmMsg, + testing::{mock_env, mock_info, MockApi, MockStorage, MOCK_CONTRACT_ADDR}, + to_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, DistributionMsg, Event, Order, OwnedDeps, + Reply, ReplyOn, StdError, SubMsg, SubMsgResponse, Uint128, WasmMsg, }; -use cosmwasm_std::testing::{MOCK_CONTRACT_ADDR, mock_env, mock_info, MockApi, MockStorage}; use cw20::{Cw20ExecuteMsg, MinterResponse}; use cw20_base::msg::InstantiateMsg as Cw20InstantiateMsg; - use pfc_steak::hub::{ Batch, CallbackMsg, ConfigResponse, ExecuteMsg, InstantiateMsg, PendingBatch, QueryMsg, ReceiveMsg, StateResponse, UnbondRequest, UnbondRequestsByBatchResponseItem, UnbondRequestsByUserResponseItem, }; -use crate::contract::{execute, instantiate, reply, REPLY_INSTANTIATE_TOKEN, REPLY_REGISTER_RECEIVED_COINS}; -use crate::helpers::{parse_coin, parse_received_fund}; -use crate::math::{ - compute_redelegations_for_rebalancing, compute_redelegations_for_removal, compute_undelegations, +use super::{ + custom_querier::CustomQuerier, + helpers::{mock_dependencies, mock_env_at_timestamp, query_helper}, +}; +use crate::{ + contract::{ + execute, instantiate, reply, REPLY_INSTANTIATE_TOKEN, REPLY_REGISTER_RECEIVED_COINS, + }, + helpers::{parse_coin, parse_received_fund}, + math::{ + compute_redelegations_for_rebalancing, compute_redelegations_for_removal, + compute_undelegations, + }, + state::State, + types::{Coins, Delegation, Redelegation, Undelegation}, }; -use crate::state::State; -use crate::types::{Coins, Delegation, Redelegation, Undelegation}; - -use super::custom_querier::CustomQuerier; -use super::helpers::{mock_dependencies, mock_env_at_timestamp, query_helper}; //-------------------------------------------------------------------------------------------------- // Test setup @@ -49,17 +54,13 @@ fn setup_test() -> OwnedDeps { decimals: 6, epoch_period: 259200, // 3 * 24 * 60 * 60 = 3 days unbond_period: 1814400, // 21 * 24 * 60 * 60 = 21 days - validators: vec![ - "alice".to_string(), - "bob".to_string(), - "charlie".to_string(), - ], + validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string()], label: None, marketing: None, - dust_collector:None + dust_collector: None, }, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 1); assert_eq!( @@ -79,7 +80,7 @@ fn setup_test() -> OwnedDeps { }), marketing: None, }) - .unwrap(), + .unwrap(), funds: vec![], label: "steak_token".to_string(), }), @@ -102,7 +103,7 @@ fn setup_test() -> OwnedDeps { }), }, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 0); @@ -130,17 +131,13 @@ fn setup_test_fee_split() -> OwnedDeps { decimals: 6, epoch_period: 259200, // 3 * 24 * 60 * 60 = 3 days unbond_period: 1814400, // 21 * 24 * 60 * 60 = 21 days - validators: vec![ - "alice".to_string(), - "bob".to_string(), - "charlie".to_string(), - ], + validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string()], label: None, marketing: None, - dust_collector:None + dust_collector: None, }, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 1); assert_eq!( @@ -160,7 +157,7 @@ fn setup_test_fee_split() -> OwnedDeps { }), marketing: None, }) - .unwrap(), + .unwrap(), funds: vec![], label: "steak_token".to_string(), }), @@ -183,7 +180,7 @@ fn setup_test_fee_split() -> OwnedDeps { }), }, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 0); @@ -213,14 +210,10 @@ fn proper_instantiation() { fee_account: "the_fee_man".to_string(), fee_rate: Decimal::from_ratio(10_u128, 100_u128), max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), - validators: vec![ - "alice".to_string(), - "bob".to_string(), - "charlie".to_string(), - ], + validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string(),], paused_validators: vec![], dust_collector: None, - token_factory:None + token_factory: None } ); @@ -260,14 +253,10 @@ fn proper_instantiation() { fee_account: "fee_split_contract".to_string(), fee_rate: Decimal::from_ratio(10_u128, 100_u128), max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), - validators: vec![ - "alice".to_string(), - "bob".to_string(), - "charlie".to_string(), - ], + validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string(),], paused_validators: vec![], dust_collector: None, - token_factory:None + token_factory: None } ); } @@ -282,18 +271,24 @@ fn bonding() { deps.as_mut(), mock_env(), mock_info("user_1", &[Coin::new(1_000_000, "uxyz")]), - ExecuteMsg::Bond { receiver: None,exec_msg:None }, + ExecuteMsg::Bond { + receiver: None, + exec_msg: None, + }, ) - .unwrap(); + .unwrap(); - // 3 messages. (switched to 3 so we can 'send' instead of 'transfer' minted tokens, so contract will know about it - // 1 - delegate + // 3 messages. (switched to 3 so we can 'send' instead of 'transfer' minted tokens, so contract + // will know about it 1 - delegate // 2 - mint token (to ourselves) // 3 - send/transfer it assert_eq!(res.messages.len(), 3); assert_eq!( res.messages[0], - SubMsg::reply_on_success(Delegation::new("alice", 1_000_000, "uxyz").to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS) + SubMsg::reply_on_success( + Delegation::new("alice", 1_000_000, "uxyz").to_cosmos_msg(), + REPLY_REGISTER_RECEIVED_COINS + ) ); assert_eq!( res.messages[1], @@ -305,7 +300,7 @@ fn bonding() { recipient: "cosmos2contract".to_string(), amount: Uint128::new(1_000_000), }) - .unwrap(), + .unwrap(), funds: vec![], }), gas_limit: None, @@ -323,7 +318,7 @@ fn bonding() { recipient: "user_1".to_string(), amount: Uint128::new(1000000), }) - .unwrap(), + .unwrap(), funds: vec![], }), gas_limit: None, @@ -331,9 +326,9 @@ fn bonding() { } ); - // Bond when there are existing delegations, and Luna:Steak exchange rate is >1 - // Previously user 1 delegated 1,000,000 uluna. We assume we have accumulated 2.5% yield at 1025000 staked + // Previously user 1 delegated 1,000,000 uluna. We assume we have accumulated 2.5% yield at + // 1025000 staked deps.querier.set_staking_delegations(&[ Delegation::new("alice", 341667, "uxyz"), Delegation::new("bob", 341667, "uxyz"), @@ -348,15 +343,18 @@ fn bonding() { mock_info("user_2", &[Coin::new(12345, "uxyz")]), ExecuteMsg::Bond { receiver: Some("user_3".to_string()), - exec_msg:None + exec_msg: None, }, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 3); assert_eq!( res.messages[0], - SubMsg::reply_on_success(Delegation::new("charlie", 12345, "uxyz").to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS) + SubMsg::reply_on_success( + Delegation::new("charlie", 12345, "uxyz").to_cosmos_msg(), + REPLY_REGISTER_RECEIVED_COINS + ) ); assert_eq!( res.messages[1], @@ -368,7 +366,7 @@ fn bonding() { recipient: "cosmos2contract".to_string(), amount: Uint128::new(12043), }) - .unwrap(), + .unwrap(), funds: vec![], }), gas_limit: None, @@ -386,7 +384,7 @@ fn bonding() { recipient: "user_3".to_string(), amount: Uint128::new(12043), }) - .unwrap(), + .unwrap(), funds: vec![], }), gas_limit: None, @@ -423,18 +421,23 @@ fn bond_ex() { deps.as_mut(), mock_env(), mock_info("user_1", &[Coin::new(1_000_000, "uxyz")]), - ExecuteMsg::BondEx { receiver: None}, + ExecuteMsg::BondEx { + receiver: None, + }, ) - .unwrap(); + .unwrap(); - // 3 messages. (switched to 3 so we can 'send' instead of 'transfer' minted tokens, so contract will know about it - // 1 - delegate + // 3 messages. (switched to 3 so we can 'send' instead of 'transfer' minted tokens, so contract + // will know about it 1 - delegate // 2 - mint token (to ourselves) // 3 - send/transfer it assert_eq!(res.messages.len(), 2); assert_eq!( res.messages[0], - SubMsg::reply_on_success(Delegation::new("alice", 1_000_000, "uxyz").to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS) + SubMsg::reply_on_success( + Delegation::new("alice", 1_000_000, "uxyz").to_cosmos_msg(), + REPLY_REGISTER_RECEIVED_COINS + ) ); assert_eq!( res.messages[1], @@ -446,14 +449,13 @@ fn bond_ex() { recipient: "user_1".to_string(), amount: Uint128::new(1_000_000), }) - .unwrap(), + .unwrap(), funds: vec![], }), gas_limit: None, reply_on: ReplyOn::Never, } ); - } #[test] @@ -468,12 +470,7 @@ fn harvesting() { ]); deps.querier.set_cw20_total_supply("steak_token", 1000000); - let res = execute( - deps.as_mut(), - mock_env(), - mock_info("worker", &[]), - ExecuteMsg::Harvest {}, - ) + let res = execute(deps.as_mut(), mock_env(), mock_info("worker", &[]), ExecuteMsg::Harvest {}) .unwrap(); assert_eq!(res.messages.len(), 4); @@ -524,10 +521,15 @@ fn registering_unlocked_coins() { let mut deps = setup_test(); let state = State::default(); - // After withdrawing staking rewards, we parse the `coin_received` event to find the received amounts + // After withdrawing staking rewards, we parse the `coin_received` event to find the received + // amounts let event = Event::new("coin_received") .add_attribute("receiver", MOCK_CONTRACT_ADDR.to_string()) - .add_attribute("amount", "123ukrw,234uxyz,345uusd,69420ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B"); + .add_attribute( + "amount", + "123ukrw,234uxyz,345uusd,69420ibc/\ + 0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + ); reply( deps.as_mut(), @@ -540,7 +542,7 @@ fn registering_unlocked_coins() { }), }, ) - .unwrap(); + .unwrap(); // Unlocked coins in contract state should have been updated let unlocked_coins = state.unlocked_coins.load(deps.as_ref().storage).unwrap(); @@ -593,7 +595,7 @@ fn reinvesting() { mock_info(MOCK_CONTRACT_ADDR, &[]), ExecuteMsg::Callback(CallbackMsg::Reinvest {}), ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 2); assert_eq!( @@ -665,7 +667,7 @@ fn reinvesting_fee_split() { mock_info(MOCK_CONTRACT_ADDR, &[]), ExecuteMsg::Callback(CallbackMsg::Reinvest {}), ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 2); assert_eq!( @@ -677,13 +679,17 @@ fn reinvesting_fee_split() { reply_on: ReplyOn::Never, } ); - let send_msg = pfc_fee_split::fee_split_msg::ExecuteMsg::Deposit { flush: false }; + let send_msg = pfc_fee_split::fee_split_msg::ExecuteMsg::Deposit { + flush: false, + }; assert_eq!( res.messages[1], SubMsg { id: 0, - msg: send_msg.into_cosmos_msg("fee_split_contract", vec![Coin::new(23u128, "uxyz")]).unwrap(), + msg: send_msg + .into_cosmos_msg("fee_split_contract", vec![Coin::new(23u128, "uxyz")]) + .unwrap(), gas_limit: None, reply_on: ReplyOn::Never, } @@ -713,15 +719,15 @@ fn queuing_unbond() { ExecuteMsg::Receive(cw20::Cw20ReceiveMsg { sender: "hacker".to_string(), amount: Uint128::new(69420), - msg: to_binary(&ReceiveMsg::QueueUnbond { receiver: None }).unwrap(), + msg: to_binary(&ReceiveMsg::QueueUnbond { + receiver: None, + }) + .unwrap(), }), ) - .unwrap_err(); + .unwrap_err(); - assert_eq!( - err, - StdError::generic_err("expecting Steak token, received random_token") - ); + assert_eq!(err, StdError::generic_err("expecting Steak token, received random_token")); // User 1 creates an unbonding request before `est_unbond_start_time` is reached. The unbond // request is saved, but not the pending batch is not submitted for unbonding @@ -732,10 +738,13 @@ fn queuing_unbond() { ExecuteMsg::Receive(cw20::Cw20ReceiveMsg { sender: "user_1".to_string(), amount: Uint128::new(23456), - msg: to_binary(&ReceiveMsg::QueueUnbond { receiver: None }).unwrap(), + msg: to_binary(&ReceiveMsg::QueueUnbond { + receiver: None, + }) + .unwrap(), }), ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 0); @@ -751,10 +760,10 @@ fn queuing_unbond() { msg: to_binary(&ReceiveMsg::QueueUnbond { receiver: Some("user_3".to_string()), }) - .unwrap(), + .unwrap(), }), ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 1); assert_eq!( @@ -774,17 +783,11 @@ fn queuing_unbond() { // The users' unbonding requests should have been saved let ubr1 = state .unbond_requests - .load( - deps.as_ref().storage, - (1u64.into(), &Addr::unchecked("user_1")), - ) + .load(deps.as_ref().storage, (1u64.into(), &Addr::unchecked("user_1"))) .unwrap(); let ubr2 = state .unbond_requests - .load( - deps.as_ref().storage, - (1u64.into(), &Addr::unchecked("user_3")), - ) + .load(deps.as_ref().storage, (1u64.into(), &Addr::unchecked("user_3"))) .unwrap(); assert_eq!( @@ -850,10 +853,7 @@ fn submitting_batch() { .unbond_requests .save( deps.as_mut().storage, - ( - unbond_request.id.into(), - &Addr::unchecked(unbond_request.user.clone()), - ), + (unbond_request.id.into(), &Addr::unchecked(unbond_request.user.clone())), unbond_request, ) .unwrap(); @@ -888,16 +888,22 @@ fn submitting_batch() { mock_info(MOCK_CONTRACT_ADDR, &[]), ExecuteMsg::SubmitBatch {}, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 4); assert_eq!( res.messages[0], - SubMsg::reply_on_success(Undelegation::new("alice", 31732, "uxyz").to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS) + SubMsg::reply_on_success( + Undelegation::new("alice", 31732, "uxyz").to_cosmos_msg(), + REPLY_REGISTER_RECEIVED_COINS + ) ); assert_eq!( res.messages[1], - SubMsg::reply_on_success(Undelegation::new("bob", 31733, "uxyz").to_cosmos_msg(), REPLY_REGISTER_RECEIVED_COINS) + SubMsg::reply_on_success( + Undelegation::new("bob", 31733, "uxyz").to_cosmos_msg(), + REPLY_REGISTER_RECEIVED_COINS + ) ); assert_eq!( res.messages[2], @@ -915,7 +921,7 @@ fn submitting_batch() { msg: to_binary(&Cw20ExecuteMsg::Burn { amount: Uint128::new(92876) }) - .unwrap(), + .unwrap(), funds: vec![], }), gas_limit: None, @@ -935,10 +941,7 @@ fn submitting_batch() { ); // Previous batch should have been updated - let previous_batch = state - .previous_batches - .load(deps.as_ref().storage, 1u64.into()) - .unwrap(); + let previous_batch = state.previous_batches.load(deps.as_ref().storage, 1u64.into()).unwrap(); assert_eq!( previous_batch, Batch { @@ -990,11 +993,7 @@ fn reconciling() { for previous_batch in &previous_batches { state .previous_batches - .save( - deps.as_mut().storage, - previous_batch.id.into(), - previous_batch, - ) + .save(deps.as_mut().storage, previous_batch.id.into(), previous_batch) .unwrap(); } @@ -1018,10 +1017,7 @@ fn reconciling() { Coin::new(12345, "uxyz"), Coin::new(234, "ukrw"), Coin::new(345, "uusd"), - Coin::new( - 69420, - "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", - ), + Coin::new(69420, "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B"), ]); execute( @@ -1030,7 +1026,7 @@ fn reconciling() { mock_info("worker", &[]), ExecuteMsg::Reconcile {}, ) - .unwrap(); + .unwrap(); // Expected received: batch 2 + batch 3 = 1385 + 1506 = 2891 // Expected unlocked: 10000 @@ -1042,10 +1038,7 @@ fn reconciling() { // remainder: 0 // batch 2: 1385 - 273 = 1112 // batch 3: 1506 - 273 = 1233 - let batch = state - .previous_batches - .load(deps.as_ref().storage, 2u64.into()) - .unwrap(); + let batch = state.previous_batches.load(deps.as_ref().storage, 2u64.into()).unwrap(); assert_eq!( batch, Batch { @@ -1057,10 +1050,7 @@ fn reconciling() { } ); - let batch = state - .previous_batches - .load(deps.as_ref().storage, 3u64.into()) - .unwrap(); + let batch = state.previous_batches.load(deps.as_ref().storage, 3u64.into()).unwrap(); assert_eq!( batch, Batch { @@ -1073,16 +1063,10 @@ fn reconciling() { ); // Batches 1 and 4 should not have changed - let batch = state - .previous_batches - .load(deps.as_ref().storage, 1u64.into()) - .unwrap(); + let batch = state.previous_batches.load(deps.as_ref().storage, 1u64.into()).unwrap(); assert_eq!(batch, previous_batches[0]); - let batch = state - .previous_batches - .load(deps.as_ref().storage, 4u64.into()) - .unwrap(); + let batch = state.previous_batches.load(deps.as_ref().storage, 4u64.into()).unwrap(); assert_eq!(batch, previous_batches[3]); } @@ -1128,10 +1112,7 @@ fn withdrawing_unbonded() { .unbond_requests .save( deps.as_mut().storage, - ( - unbond_request.id.into(), - &Addr::unchecked(unbond_request.user.clone()), - ), + (unbond_request.id.into(), &Addr::unchecked(unbond_request.user.clone())), unbond_request, ) .unwrap(); @@ -1164,18 +1145,15 @@ fn withdrawing_unbonded() { reconciled: true, total_shares: Uint128::new(56789), amount_unclaimed: Uint128::new(59060), // 1.040 Luna per Steak - est_unbond_end_time: 30000, // reconciled, but not yet finished unbonding; ignored + est_unbond_end_time: 30000, /* reconciled, but not yet finished unbonding; + * ignored */ }, ]; for previous_batch in &previous_batches { state .previous_batches - .save( - deps.as_mut().storage, - previous_batch.id.into(), - previous_batch, - ) + .save(deps.as_mut().storage, previous_batch.id.into(), previous_batch) .unwrap(); } @@ -1196,9 +1174,11 @@ fn withdrawing_unbonded() { deps.as_mut(), mock_env_at_timestamp(5000), mock_info("user_1", &[]), - ExecuteMsg::WithdrawUnbonded { receiver: None }, + ExecuteMsg::WithdrawUnbonded { + receiver: None, + }, ) - .unwrap_err(); + .unwrap_err(); assert_eq!(err, StdError::generic_err("withdrawable amount is zero")); @@ -1217,9 +1197,11 @@ fn withdrawing_unbonded() { deps.as_mut(), mock_env_at_timestamp(25000), mock_info("user_1", &[]), - ExecuteMsg::WithdrawUnbonded { receiver: None }, + ExecuteMsg::WithdrawUnbonded { + receiver: None, + }, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 1); assert_eq!( @@ -1236,10 +1218,7 @@ fn withdrawing_unbonded() { ); // Previous batches should have been updated - let batch = state - .previous_batches - .load(deps.as_ref().storage, 1u64.into()) - .unwrap(); + let batch = state.previous_batches.load(deps.as_ref().storage, 1u64.into()).unwrap(); assert_eq!( batch, Batch { @@ -1251,10 +1230,7 @@ fn withdrawing_unbonded() { } ); - let err = state - .previous_batches - .load(deps.as_ref().storage, 2u64.into()) - .unwrap_err(); + let err = state.previous_batches.load(deps.as_ref().storage, 2u64.into()).unwrap_err(); assert_eq!( err, StdError::NotFound { @@ -1265,17 +1241,11 @@ fn withdrawing_unbonded() { // User 1's unbond requests in batches 1 and 2 should have been deleted let err1 = state .unbond_requests - .load( - deps.as_ref().storage, - (1u64.into(), &Addr::unchecked("user_1")), - ) + .load(deps.as_ref().storage, (1u64.into(), &Addr::unchecked("user_1"))) .unwrap_err(); let err2 = state .unbond_requests - .load( - deps.as_ref().storage, - (1u64.into(), &Addr::unchecked("user_1")), - ) + .load(deps.as_ref().storage, (1u64.into(), &Addr::unchecked("user_1"))) .unwrap_err(); assert_eq!( @@ -1300,7 +1270,7 @@ fn withdrawing_unbonded() { receiver: Some("user_2".to_string()), }, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 1); assert_eq!( @@ -1317,10 +1287,7 @@ fn withdrawing_unbonded() { ); // Batch 1 and user 2's unbonding request should have been purged from storage - let err = state - .previous_batches - .load(deps.as_ref().storage, 1u64.into()) - .unwrap_err(); + let err = state.previous_batches.load(deps.as_ref().storage, 1u64.into()).unwrap_err(); assert_eq!( err, StdError::NotFound { @@ -1330,10 +1297,7 @@ fn withdrawing_unbonded() { let err = state .unbond_requests - .load( - deps.as_ref().storage, - (1u64.into(), &Addr::unchecked("user_3")), - ) + .load(deps.as_ref().storage, (1u64.into(), &Addr::unchecked("user_3"))) .unwrap_err(); assert_eq!( @@ -1357,12 +1321,9 @@ fn adding_validator() { validator: "dave".to_string(), }, ) - .unwrap_err(); + .unwrap_err(); - assert_eq!( - err, - StdError::generic_err("unauthorized: sender is not owner") - ); + assert_eq!(err, StdError::generic_err("unauthorized: sender is not owner")); let err = execute( deps.as_mut(), @@ -1372,12 +1333,9 @@ fn adding_validator() { validator: "alice".to_string(), }, ) - .unwrap_err(); + .unwrap_err(); - assert_eq!( - err, - StdError::generic_err("validator is already whitelisted") - ); + assert_eq!(err, StdError::generic_err("validator is already whitelisted")); let res = execute( deps.as_mut(), @@ -1387,7 +1345,7 @@ fn adding_validator() { validator: "dave".to_string(), }, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 0); @@ -1422,12 +1380,9 @@ fn removing_validator() { validator: "charlie".to_string(), }, ) - .unwrap_err(); + .unwrap_err(); - assert_eq!( - err, - StdError::generic_err("unauthorized: sender is not owner") - ); + assert_eq!(err, StdError::generic_err("unauthorized: sender is not owner")); let err = execute( deps.as_mut(), @@ -1437,12 +1392,9 @@ fn removing_validator() { validator: "dave".to_string(), }, ) - .unwrap_err(); + .unwrap_err(); - assert_eq!( - err, - StdError::generic_err("validator is not already whitelisted") - ); + assert_eq!(err, StdError::generic_err("validator is not already whitelisted")); // Target: (341667 + 341667 + 341666) / 2 = 512500 // Remainder: 0 @@ -1456,7 +1408,7 @@ fn removing_validator() { validator: "charlie".to_string(), }, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 2); assert_eq!( @@ -1475,7 +1427,7 @@ fn removing_validator() { ); let validators = state.validators.load(deps.as_ref().storage).unwrap(); - assert_eq!(validators, vec![String::from("alice"), String::from("bob")], ); + assert_eq!(validators, vec![String::from("alice"), String::from("bob")],); } #[test] @@ -1491,12 +1443,9 @@ fn transferring_ownership() { new_owner: "jake".to_string(), }, ) - .unwrap_err(); + .unwrap_err(); - assert_eq!( - err, - StdError::generic_err("unauthorized: sender is not owner") - ); + assert_eq!(err, StdError::generic_err("unauthorized: sender is not owner")); let res = execute( deps.as_mut(), @@ -1506,7 +1455,7 @@ fn transferring_ownership() { new_owner: "jake".to_string(), }, ) - .unwrap(); + .unwrap(); assert_eq!(res.messages.len(), 0); @@ -1519,20 +1468,13 @@ fn transferring_ownership() { mock_info("pumpkin", &[]), ExecuteMsg::AcceptOwnership {}, ) - .unwrap_err(); + .unwrap_err(); - assert_eq!( - err, - StdError::generic_err("unauthorized: sender is not new owner") - ); + assert_eq!(err, StdError::generic_err("unauthorized: sender is not new owner")); - let res = execute( - deps.as_mut(), - mock_env(), - mock_info("jake", &[]), - ExecuteMsg::AcceptOwnership {}, - ) - .unwrap(); + let res = + execute(deps.as_mut(), mock_env(), mock_info("jake", &[]), ExecuteMsg::AcceptOwnership {}) + .unwrap(); assert_eq!(res.messages.len(), 0); @@ -1544,7 +1486,6 @@ fn transferring_ownership() { fn splitting_fees() { let mut deps = setup_test(); - let err = execute( deps.as_mut(), mock_env(), @@ -1554,12 +1495,9 @@ fn splitting_fees() { new_fee_account: "charlie".to_string(), }, ) - .unwrap_err(); + .unwrap_err(); - assert_eq!( - err, - StdError::generic_err("unauthorized: sender is not owner") - ); + assert_eq!(err, StdError::generic_err("unauthorized: sender is not owner")); let err = execute( deps.as_mut(), @@ -1570,12 +1508,9 @@ fn splitting_fees() { new_fee_account: "charlie".to_string(), }, ) - .unwrap_err(); + .unwrap_err(); - assert_eq!( - err, - StdError::generic_err("Invalid Fee type: Wallet or FeeSplit only") - ); + assert_eq!(err, StdError::generic_err("Invalid Fee type: Wallet or FeeSplit only")); execute( deps.as_mut(), @@ -1586,7 +1521,7 @@ fn splitting_fees() { new_fee_account: "charlie".to_string(), }, ) - .unwrap(); + .unwrap(); let res: ConfigResponse = query_helper(deps.as_ref(), QueryMsg::Config {}); assert_eq!( res, @@ -1601,18 +1536,13 @@ fn splitting_fees() { fee_account: "charlie".to_string(), fee_rate: Decimal::from_ratio(10_u128, 100_u128), max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), - validators: vec![ - "alice".to_string(), - "bob".to_string(), - "charlie".to_string(), - ], + validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string(),], paused_validators: vec![], dust_collector: None, - token_factory:None + token_factory: None } ); - execute( deps.as_mut(), mock_env(), @@ -1622,7 +1552,7 @@ fn splitting_fees() { new_fee_account: "contract".to_string(), }, ) - .unwrap(); + .unwrap(); let res: ConfigResponse = query_helper(deps.as_ref(), QueryMsg::Config {}); assert_eq!( res, @@ -1637,14 +1567,10 @@ fn splitting_fees() { fee_account: "contract".to_string(), fee_rate: Decimal::from_ratio(10_u128, 100_u128), max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), - validators: vec![ - "alice".to_string(), - "bob".to_string(), - "charlie".to_string(), - ], + validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string(),], paused_validators: vec![], - dust_collector:None, - token_factory:None + dust_collector: None, + token_factory: None } ); } @@ -1689,10 +1615,7 @@ fn querying_previous_batches() { let state = State::default(); for batch in &batches { - state - .previous_batches - .save(deps.as_mut().storage, batch.id.into(), batch) - .unwrap(); + state.previous_batches.save(deps.as_mut().storage, batch.id.into(), batch).unwrap(); } // Querying a single batch @@ -1719,10 +1642,7 @@ fn querying_previous_batches() { limit: None, }, ); - assert_eq!( - res, - vec![batches[1].clone(), batches[2].clone(), batches[3].clone()] - ); + assert_eq!(res, vec![batches[1].clone(), batches[2].clone(), batches[3].clone()]); let res: Vec = query_helper( deps.as_ref(), @@ -1796,10 +1716,7 @@ fn querying_unbond_requests() { .unbond_requests .save( deps.as_mut().storage, - ( - unbond_request.id.into(), - &Addr::unchecked(unbond_request.user.clone()), - ), + (unbond_request.id.into(), &Addr::unchecked(unbond_request.user.clone())), unbond_request, ) .unwrap(); @@ -1840,13 +1757,7 @@ fn querying_unbond_requests() { limit: None, }, ); - assert_eq!( - res, - vec![ - unbond_requests[0].clone().into(), - unbond_requests[3].clone().into(), - ] - ); + assert_eq!(res, vec![unbond_requests[0].clone().into(), unbond_requests[3].clone().into(),]); let res: Vec = query_helper( deps.as_ref(), @@ -1925,9 +1836,13 @@ fn computing_redelegations_for_rebalancing() { Delegation::new("dave", 40471, "uxyz"), Delegation::new("evan", 2345, "uxyz"), ]; - let active_validators: Vec = vec!["alice".to_string(), "bob".to_string(), - "charlie".to_string(), "dave".to_string(), - "evan".to_string()]; + let active_validators: Vec = vec![ + "alice".to_string(), + "bob".to_string(), + "charlie".to_string(), + "dave".to_string(), + "evan".to_string(), + ]; // uluna_per_validator = (69420 + 88888 + 1234 + 40471 + 2345) / 4 = 40471 // remainer = 3 // src_delegations: @@ -1959,15 +1874,16 @@ fn computing_redelegations_for_rebalancing() { ]; assert_eq!( - compute_redelegations_for_rebalancing(active_validators, ¤t_delegations, Uint128::from(10 as u64)), + compute_redelegations_for_rebalancing( + active_validators, + ¤t_delegations, + Uint128::from(10 as u64) + ), expected, ); - - let partially_active = vec!["alice".to_string(), - "charlie".to_string(), - "dave".to_string(), - "evan".to_string()]; + let partially_active = + vec!["alice".to_string(), "charlie".to_string(), "dave".to_string(), "evan".to_string()]; let partially_expected = vec![ Redelegation::new("alice", "dave", 10118, "uxyz"), @@ -1975,7 +1891,11 @@ fn computing_redelegations_for_rebalancing() { Redelegation::new("charlie", "evan", 38299, "uxyz"), ]; assert_eq!( - compute_redelegations_for_rebalancing(partially_active.clone(), ¤t_delegations, Uint128::from(10 as u64)), + compute_redelegations_for_rebalancing( + partially_active.clone(), + ¤t_delegations, + Uint128::from(10 as u64) + ), partially_expected, ); @@ -1984,7 +1904,11 @@ fn computing_redelegations_for_rebalancing() { Redelegation::new("charlie", "evan", 29414, "uxyz"), ]; assert_eq!( - compute_redelegations_for_rebalancing(partially_active, ¤t_delegations, Uint128::from(15_000 as u64)), + compute_redelegations_for_rebalancing( + partially_active, + ¤t_delegations, + Uint128::from(15_000 as u64) + ), partially_expected_minimums, ); } @@ -2003,20 +1927,14 @@ fn parsing_coin() { .unwrap(); assert_eq!( coin, - Coin::new( - 23456, - "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", - ) + Coin::new(23456, "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B",) ); let err = parse_coin("69420").unwrap_err(); assert_eq!(err, StdError::generic_err("failed to parse coin: 69420")); let err = parse_coin("ngmi").unwrap_err(); - assert_eq!( - err, - StdError::generic_err("Parsing u128: cannot parse integer from empty string") - ); + assert_eq!(err, StdError::generic_err("Parsing u128: cannot parse integer from empty string")); } #[test] @@ -2028,10 +1946,7 @@ fn parsing_coins() { assert_eq!(coins.0, vec![Coin::new(12345, "uatom")]); let coins = Coins::from_str("12345uatom,23456uxyz").unwrap(); - assert_eq!( - coins.0, - vec![Coin::new(12345, "uatom"), Coin::new(23456, "uxyz")] - ); + assert_eq!(coins.0, vec![Coin::new(12345, "uatom"), Coin::new(23456, "uxyz")]); } #[test] @@ -2042,59 +1957,34 @@ fn adding_coins() { assert_eq!(coins.0, vec![Coin::new(12345, "uatom")]); coins.add(&Coin::new(23456, "uxyz")).unwrap(); - assert_eq!( - coins.0, - vec![Coin::new(12345, "uatom"), Coin::new(23456, "uxyz")] - ); + assert_eq!(coins.0, vec![Coin::new(12345, "uatom"), Coin::new(23456, "uxyz")]); - coins - .add_many(&Coins::from_str("76543uatom,69420uusd").unwrap()) - .unwrap(); + coins.add_many(&Coins::from_str("76543uatom,69420uusd").unwrap()).unwrap(); assert_eq!( coins.0, - vec![ - Coin::new(88888, "uatom"), - Coin::new(23456, "uxyz"), - Coin::new(69420, "uusd"), - ] + vec![Coin::new(88888, "uatom"), Coin::new(23456, "uxyz"), Coin::new(69420, "uusd"),] ); } #[test] fn receiving_funds() { let err = parse_received_fund(&[], "uxyz").unwrap_err(); - assert_eq!( - err, - StdError::generic_err("must deposit exactly one coin; received 0") - ); + assert_eq!(err, StdError::generic_err("must deposit exactly one coin; received 0")); - let err = parse_received_fund( - &[Coin::new(12345, "uatom"), Coin::new(23456, "uxyz")], - "uxyz", - ) + let err = parse_received_fund(&[Coin::new(12345, "uatom"), Coin::new(23456, "uxyz")], "uxyz") .unwrap_err(); - assert_eq!( - err, - StdError::generic_err("must deposit exactly one coin; received 2") - ); + assert_eq!(err, StdError::generic_err("must deposit exactly one coin; received 2")); let err = parse_received_fund(&[Coin::new(12345, "uatom")], "uxyz").unwrap_err(); - assert_eq!( - err, - StdError::generic_err("expected uxyz deposit, received uatom") - ); + assert_eq!(err, StdError::generic_err("expected uxyz deposit, received uatom")); let err = parse_received_fund(&[Coin::new(0, "uxyz")], "uxyz").unwrap_err(); - assert_eq!( - err, - StdError::generic_err("deposit amount must be non-zero") - ); + assert_eq!(err, StdError::generic_err("deposit amount must be non-zero")); let amount = parse_received_fund(&[Coin::new(69420, "uxyz")], "uxyz").unwrap(); assert_eq!(amount, Uint128::new(69420)); } - #[test] fn reconciling_underflow() { let mut deps = setup_test(); @@ -2162,7 +2052,7 @@ fn reconciling_underflow() { mock_info("worker", &[]), ExecuteMsg::Reconcile {}, ) - .unwrap(); + .unwrap(); } #[test] @@ -2232,5 +2122,5 @@ fn reconciling_underflow_second() { mock_info("worker", &[]), ExecuteMsg::Reconcile {}, ) - .unwrap(); -} \ No newline at end of file + .unwrap(); +} diff --git a/contracts/token/src/lib.rs b/contracts/token/src/lib.rs index 45956ab..9fb5b36 100644 --- a/contracts/token/src/lib.rs +++ b/contracts/token/src/lib.rs @@ -2,12 +2,12 @@ use cosmwasm_std::{ entry_point, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdError, StdResult, Storage, }; -use cw20_base::contract::{ - execute as cw20_execute, instantiate as cw20_instantiate, query as cw20_query, +use cw20_base::{ + contract::{execute as cw20_execute, instantiate as cw20_instantiate, query as cw20_query}, + msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, + state::{MinterData, TOKEN_INFO}, + ContractError, }; -use cw20_base::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; -use cw20_base::state::{MinterData, TOKEN_INFO}; -use cw20_base::ContractError; #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( @@ -29,8 +29,12 @@ pub fn execute( // For `burn`, we assert that the caller is the minter // For `burn_from`, we simply disable it match msg { - ExecuteMsg::Burn { .. } => assert_minter(deps.storage, &info.sender)?, - ExecuteMsg::BurnFrom { .. } => return Err(StdError::generic_err("`burn_from` command is disabled").into()), + ExecuteMsg::Burn { + .. + } => assert_minter(deps.storage, &info.sender)?, + ExecuteMsg::BurnFrom { + .. + } => return Err(StdError::generic_err("`burn_from` command is disabled").into()), _ => (), } @@ -40,7 +44,11 @@ pub fn execute( fn assert_minter(storage: &dyn Storage, sender: &Addr) -> Result<(), ContractError> { let token_info = TOKEN_INFO.load(storage)?; - if let Some(MinterData { minter, .. }) = &token_info.mint { + if let Some(MinterData { + minter, + .. + }) = &token_info.mint + { if sender != minter { return Err(StdError::generic_err("only minter can execute token burn").into()); } @@ -56,10 +64,10 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { #[cfg(test)] mod tests { - use cosmwasm_std::testing::{ - mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage, + use cosmwasm_std::{ + testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, + OwnedDeps, Uint128, }; - use cosmwasm_std::{OwnedDeps, Uint128}; use cw20_base::state::{TokenInfo, BALANCES}; use super::*; @@ -84,19 +92,11 @@ mod tests { .unwrap(); BALANCES - .save( - deps.as_mut().storage, - &Addr::unchecked("steak_hub"), - &Uint128::new(100) - ) + .save(deps.as_mut().storage, &Addr::unchecked("steak_hub"), &Uint128::new(100)) .unwrap(); BALANCES - .save( - deps.as_mut().storage, - &Addr::unchecked("alice"), - &Uint128::new(100) - ) + .save(deps.as_mut().storage, &Addr::unchecked("alice"), &Uint128::new(100)) .unwrap(); deps diff --git a/init/osmo-tf_init.json b/init/osmo-tf_init.json new file mode 100644 index 0000000..472c46f --- /dev/null +++ b/init/osmo-tf_init.json @@ -0,0 +1,16 @@ +{ + "owner": "osmo1lu92zj8q6cmrptu09rp3343x9969r9qr4w9r7h", + "epoch_period": 259200, + "unbond_period": 1209600, + "validators": [ + "osmovaloper14ts0j42qkpr43a3tgxr7zz6l6zdf7hdes5jpma", + "osmovaloper135p8cjwa3hnnnksjxchda0a6339z9zm7cudyxn" + ], + "denom": "uosmo", + "steak_denom": "boneOsmo", + "fee_account": "osmo1ctrfxgxdjqgd0usaepzdan2razpd3sge7jvckakh036yzm24ymgsnktjf8", + "fee_amount": "0.10", + "max_fee_amount": "0.10", + "fee_account_type": "Wallet", + "token_factory": "Osmosis" +} \ No newline at end of file diff --git a/justfile b/justfile new file mode 100644 index 0000000..6753226 --- /dev/null +++ b/justfile @@ -0,0 +1,30 @@ +check: + cargo check --target wasm32-unknown-unknown --lib + +clippy: + cargo clippy --tests + +format: + cargo +nightly fmt + +test: + cargo test + +optimize: + if [[ $(uname -m) =~ "arm64" ]]; then \ + just optimize-arm; else \ + just optimize-x86; fi + +optimize-arm: + docker run --rm -v "$(pwd)":/code \ + --mount type=volume,source="$(basename "$(pwd)")_cache",target=/target \ + --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ + --platform linux/arm64 \ + cosmwasm/workspace-optimizer-arm64:0.15.1 + +optimize-x86: + docker run --rm -v "$(pwd)":/code \ + --mount type=volume,source="$(basename "$(pwd)")_cache",target=/target \ + --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ + --platform linux/amd64 \ + cosmwasm/workspace-optimizer:0.15.1 diff --git a/packages/steak/src/hub.rs b/packages/steak/src/hub.rs index 637b8de..4b76042 100644 --- a/packages/steak/src/hub.rs +++ b/packages/steak/src/hub.rs @@ -84,7 +84,8 @@ pub enum ExecuteMsg { RemoveValidator { validator: String, }, - /// Remove a validator from the whitelist; callable by the owner. Does not undelegate. use for typos + /// Remove a validator from the whitelist; callable by the owner. Does not undelegate. use for + /// typos RemoveValidatorEx { validator: String, }, @@ -152,7 +153,9 @@ pub enum ExecuteMsg { pub enum ReceiveMsg { /// Submit an unbonding request to the current unbonding queue; automatically invokes `unbond` /// if `epoch_time` has elapsed since when the last unbonding queue was executed. - QueueUnbond { receiver: Option }, + QueueUnbond { + receiver: Option, + }, } #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, JsonSchema)] @@ -184,19 +187,21 @@ pub enum QueryMsg { /// Query an individual batch that has previously been submitted for unbonding but have not yet /// fully withdrawn. Response: `Batch` PreviousBatch(u64), - /// Enumerate all previous batches that have previously been submitted for unbonding but have not - /// yet fully withdrawn. Response: `Vec` + /// Enumerate all previous batches that have previously been submitted for unbonding but have + /// not yet fully withdrawn. Response: `Vec` PreviousBatches { start_after: Option, limit: Option, }, - /// Enumerate all outstanding unbonding requests in a given batch. Response: `Vec` + /// Enumerate all outstanding unbonding requests in a given batch. Response: + /// `Vec` UnbondRequestsByBatch { id: u64, start_after: Option, limit: Option, }, - /// Enumerate all outstanding unbonding requests from given a user. Response: `Vec` + /// Enumerate all outstanding unbonding requests from given a user. Response: + /// `Vec` UnbondRequestsByUser { user: String, start_after: Option, @@ -323,6 +328,7 @@ pub enum FeeType { impl FromStr for FeeType { type Err = (); + fn from_str(s: &str) -> Result { match s { "Wallet" => Ok(FeeType::Wallet), diff --git a/packages/steak/src/hub_tf.rs b/packages/steak/src/hub_tf.rs index 65df430..1622b4c 100644 --- a/packages/steak/src/hub_tf.rs +++ b/packages/steak/src/hub_tf.rs @@ -1,5 +1,6 @@ -use cosmwasm_schema::cw_serde; use std::str::FromStr; + +use cosmwasm_schema::cw_serde; // use cosmwasm_std::{Decimal, Uint128}; @@ -24,6 +25,7 @@ impl ToString for TokenFactoryType { } impl FromStr for TokenFactoryType { type Err = (); + fn from_str(s: &str) -> Result { match s { "CosmWasm" => Ok(TokenFactoryType::CosmWasm), @@ -71,32 +73,53 @@ pub enum ExecuteMsg { exec_msg: Option, }, /// Bond specified amount of Luna - Unbond { receiver: Option }, + Unbond { + receiver: Option, + }, /// Withdraw Luna that have finished un-bonding in previous batches - WithdrawUnbonded { receiver: Option }, + WithdrawUnbonded { + receiver: Option, + }, /// Withdraw Luna that has finished unbonding in previous batches, for given address - WithdrawUnbondedAdmin { address: String }, + WithdrawUnbondedAdmin { + address: String, + }, /// Add a validator to the whitelist; callable by the owner - AddValidator { validator: String }, + AddValidator { + validator: String, + }, /// Remove a validator from the whitelist; callable by the owner - RemoveValidator { validator: String }, - /// Remove a validator from the whitelist; callable by the owner. Does not undelegate. use for typos - RemoveValidatorEx { validator: String }, + RemoveValidator { + validator: String, + }, + /// Remove a validator from the whitelist; callable by the owner. Does not undelegate. use for + /// typos + RemoveValidatorEx { + validator: String, + }, /// Pause a validator from accepting new delegations - PauseValidator { validator: String }, + PauseValidator { + validator: String, + }, /// Unpause a validator from accepting new delegations - UnPauseValidator { validator: String }, + UnPauseValidator { + validator: String, + }, /// Transfer ownership to another account; will not take effect unless the new owner accepts - TransferOwnership { new_owner: String }, + TransferOwnership { + new_owner: String, + }, /// Accept an ownership transfer AcceptOwnership {}, /// Claim staking rewards, swap all for Luna, and restake Harvest {}, /// Use redelegations to balance the amounts of Luna delegated to validators - Rebalance { minimum: Uint128 }, + Rebalance { + minimum: Uint128, + }, /// redelegate stake from one validator to another Redelegate { validator_from: String, @@ -107,7 +130,9 @@ pub enum ExecuteMsg { /// Submit the current pending batch of unbonding requests to be unbonded SubmitBatch {}, /// Set unbond period - SetUnbondPeriod { unbond_period: u64 }, + SetUnbondPeriod { + unbond_period: u64, + }, /// Transfer Fee collection account to another account TransferFeeAccount { @@ -115,15 +140,23 @@ pub enum ExecuteMsg { new_fee_account: String, }, /// Update fee collection amount - UpdateFee { new_fee: Decimal }, + UpdateFee { + new_fee: Decimal, + }, /// Callbacks; can only be invoked by the contract itself Callback(CallbackMsg), /// Set Dust Collector Contract - SetDustCollector { dust_collector: Option }, + SetDustCollector { + dust_collector: Option, + }, /// Collect the Dust - CollectDust { max_tokens: u32 }, + CollectDust { + max_tokens: u32, + }, /// Return the Dust in shiny 'base denom' ReturnDenom {}, /// change tokenfactory type (ADMIN only) - ChangeTokenFactory { token_factory_type: String }, + ChangeTokenFactory { + token_factory_type: String, + }, } diff --git a/packages/steak/src/lib.rs b/packages/steak/src/lib.rs index 1690352..6f466d3 100644 --- a/packages/steak/src/lib.rs +++ b/packages/steak/src/lib.rs @@ -4,9 +4,10 @@ pub mod hub_tf; // this was copied from eris-staking's branch of STEAK. // mod decimal_checked_ops { - use cosmwasm_std::{Decimal, Decimal256, Fraction, OverflowError, StdError, Uint128, Uint256}; use std::{convert::TryInto, str::FromStr}; + use cosmwasm_std::{Decimal, Decimal256, Fraction, OverflowError, StdError, Uint128, Uint256}; + // pub trait Decimal256CheckedOps { // fn to_decimal(self) -> Result; // } diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..ce337f2 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,24 @@ +# https://rust-lang.github.io/rustfmt +format_code_in_doc_comments = true # nightly +group_imports = "StdExternalCrate" # nightly +imports_granularity = "Crate" # nightly +match_block_trailing_comma = true +max_width = 100 +use_small_heuristics = "Off" + +edition = "2021" +version = "Two" + +comment_width = 100 + +error_on_line_overflow = true +format_macro_bodies = true +format_macro_matchers = true +format_strings = true +newline_style = "Unix" +reorder_impl_items = true +use_field_init_shorthand = true + +wrap_comments = true + + From de10dd96899e92359b53979b26445bd86b764416 Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Tue, 26 Mar 2024 17:29:01 -0500 Subject: [PATCH 58/77] chore: minor tweaks to cosmwasm-std 1.5.x --- Cargo.lock | 345 ++++++++---------- contracts/hub-tf/Cargo.toml | 18 +- contracts/hub-tf/src/contract.rs | 16 +- contracts/hub-tf/src/execute.rs | 8 +- contracts/hub-tf/src/math.rs | 2 +- .../hub-tf/src/testing/custom_querier.rs | 4 +- contracts/hub-tf/src/testing/helpers.rs | 9 +- contracts/hub-tf/src/testing/tests.rs | 61 ++-- contracts/hub-tf/src/types/coins.rs | 2 +- contracts/hub/Cargo.toml | 2 +- contracts/hub/src/contract.rs | 18 +- contracts/hub/src/execute.rs | 24 +- contracts/hub/src/math.rs | 2 +- contracts/hub/src/testing/custom_querier.rs | 6 +- contracts/hub/src/testing/cw20_querier.rs | 6 +- contracts/hub/src/testing/helpers.rs | 4 +- contracts/hub/src/testing/tests.rs | 28 +- contracts/hub/src/types/coins.rs | 3 +- packages/steak/Cargo.toml | 10 +- packages/steak/src/hub.rs | 4 +- 20 files changed, 265 insertions(+), 307 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index db4035d..91c968c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,15 +10,15 @@ checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" [[package]] name = "base16ct" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] name = "base64" -version = "0.13.0" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64ct" @@ -26,6 +26,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dea908e7347a8c64e378c17e30ef880ad73e3b4498346b055c2c00ea342f3179" +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + [[package]] name = "block-buffer" version = "0.9.0" @@ -44,6 +50,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56953345e39537a3e18bdaeba4cb0c58a78c1f61f361dc0fa7c5c7340ae87c5f" + [[package]] name = "byteorder" version = "1.4.3" @@ -64,37 +76,38 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "const-oid" -version = "0.9.0" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "722e23542a15cea1f65d4a1419c4cfd7a26706c70871a13a04238ca3f40f1661" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "cosmwasm-crypto" -version = "1.2.4" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b76d2207945b8aa3ce0735da53ab9a74f75fe3e7794754c216a9edfa04e1e627" +checksum = "9934c79e58d9676edfd592557dee765d2a6ef54c09d5aa2edb06156b00148966" dependencies = [ - "digest 0.10.5", + "digest 0.10.7", + "ecdsa", "ed25519-zebra", "k256", - "rand_core 0.6.3", + "rand_core 0.6.4", "thiserror", ] [[package]] name = "cosmwasm-derive" -version = "1.2.4" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd07af7736164d2d8126dc67fdb33b1b5c54fb5a3190395c47f46d24fc6d592" +checksum = "bc5e72e330bd3bdab11c52b5ecbdeb6a8697a004c57964caeb5d876f0b088b3c" dependencies = [ "syn 1.0.109", ] [[package]] name = "cosmwasm-schema" -version = "1.2.4" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e9e92cdce475a91659d0dc4d17836a484fc534e80e787281aa4adde4cb1798b" +checksum = "ac3e3a2136e2a60e8b6582f5dffca5d1a683ed77bf38537d330bc1dfccd69010" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -105,9 +118,9 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.4" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69681bac3dbeb0b00990279e3ed39e3d4406b29f538b16b712f6771322a45048" +checksum = "f5d803bea6bd9ed61bd1ee0b4a2eb09ee20dbb539cc6e0b8795614d20952ebb1" dependencies = [ "proc-macro2", "quote", @@ -116,11 +129,13 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.4" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d39f20967baeb94709123f7bba13a25ae2fa166bc5e7813f734914df3f8f6a1" +checksum = "ef8666e572a3a2519010dde88c04d16e9339ae751b56b2bb35081fe3f7d6be74" dependencies = [ "base64", + "bech32", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -130,8 +145,8 @@ dependencies = [ "serde", "serde-json-wasm", "sha2 0.10.6", + "static_assertions", "thiserror", - "uint", ] [[package]] @@ -143,20 +158,14 @@ dependencies = [ "libc", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" -version = "0.4.9" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", - "rand_core 0.6.3", + "rand_core 0.6.4", "subtle", "zeroize", ] @@ -200,7 +209,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dea5a233bd67babedbe96a514178a64b0c597f1f38bc474fa8d63e3f26bdceb2" dependencies = [ "cosmwasm-std", - "cw-storage-plus 1.0.1", + "cw-storage-plus 1.2.0", ] [[package]] @@ -213,7 +222,7 @@ dependencies = [ "cosmwasm-std", "cw-address-like", "cw-ownable-derive", - "cw-storage-plus 1.0.1", + "cw-storage-plus 1.2.0", "cw-utils 1.0.1", "thiserror", ] @@ -242,20 +251,9 @@ dependencies = [ [[package]] name = "cw-storage-plus" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b6f91c0b94481a3e9ef1ceb183c37d00764f8751e39b45fc09f4d9b970d469" -dependencies = [ - "cosmwasm-std", - "schemars", - "serde", -] - -[[package]] -name = "cw-storage-plus" -version = "1.0.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053a5083c258acd68386734f428a5a171b29f7d733151ae83090c6fcc9417ffa" +checksum = "d5ff29294ee99373e2cd5fd21786a3c0ced99a52fec2ca347d565489c61b723c" dependencies = [ "cosmwasm-std", "schemars", @@ -274,21 +272,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "cw-utils" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6a84c6c1c0acc3616398eba50783934bd6c964bad6974241eaee3460c8f5b26" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw2 0.16.0", - "schemars", - "semver", - "serde", - "thiserror", -] - [[package]] name = "cw-utils" version = "1.0.1" @@ -297,7 +280,7 @@ checksum = "c80e93d1deccb8588db03945016a292c3c631e6325d349ebb35d2db6f4f946f7" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw2 1.0.1", + "cw2 1.1.2", "schemars", "semver", "serde", @@ -318,28 +301,17 @@ dependencies = [ [[package]] name = "cw2" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91398113b806f4d2a8d5f8d05684704a20ffd5968bf87e3473e1973710b884ad" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 0.16.0", - "schemars", - "serde", -] - -[[package]] -name = "cw2" -version = "1.0.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb70cee2cf0b4a8ff7253e6bc6647107905e8eb37208f87d54f67810faa62f8" +checksum = "c6c120b24fbbf5c3bedebb97f2cc85fbfa1c3287e09223428e7e597b5293c1fa" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw-storage-plus 1.0.1", + "cw-storage-plus 1.2.0", "schemars", + "semver", "serde", + "thiserror", ] [[package]] @@ -356,9 +328,9 @@ dependencies = [ [[package]] name = "cw20" -version = "1.0.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91666da6c7b40c8dd5ff94df655a28114efc10c79b70b4d06f13c31e37d60609" +checksum = "526e39bb20534e25a1cd0386727f0038f4da294e5e535729ba3ef54055246abd" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -385,16 +357,15 @@ dependencies = [ [[package]] name = "cw20-base" -version = "1.0.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79314264ffc46b7658ee30caccc1540f14b9119568264bc02817f79c6f989a9" +checksum = "17ad79e86ea3707229bf78df94e08732e8f713207b4a77b2699755596725e7d9" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw-storage-plus 0.16.0", - "cw-utils 0.16.0", - "cw2 1.0.1", - "cw20 1.0.1", + "cw-storage-plus 1.2.0", + "cw2 1.1.2", + "cw20 1.1.2", "schemars", "semver", "serde", @@ -403,9 +374,9 @@ dependencies = [ [[package]] name = "der" -version = "0.6.0" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dd2ae565c0a381dde7fade45fce95984c568bdcb4700a4fdbe3175e0380b2f" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" dependencies = [ "const-oid", "zeroize", @@ -433,11 +404,12 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.5" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.3", + "const-oid", "crypto-common", "subtle", ] @@ -450,14 +422,16 @@ checksum = "21e50f3adc76d6a43f5ed73b698a87d0760ca74617f60f7c3b879003536fdd28" [[package]] name = "ecdsa" -version = "0.14.8" +version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", + "digest 0.10.7", "elliptic-curve", "rfc6979", "signature", + "spki", ] [[package]] @@ -468,7 +442,7 @@ checksum = "403ef3e961ab98f0ba902771d29f842058578bb1ce7e3c59dad5a6a93e784c69" dependencies = [ "curve25519-dalek", "hex", - "rand_core 0.6.3", + "rand_core 0.6.4", "serde", "sha2 0.9.9", "thiserror", @@ -483,19 +457,18 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "elliptic-curve" -version = "0.12.3" +version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", - "der", - "digest 0.10.5", + "digest 0.10.7", "ff", "generic-array", "group", "pkcs8", - "rand_core 0.6.3", + "rand_core 0.6.4", "sec1", "subtle", "zeroize", @@ -503,11 +476,11 @@ dependencies = [ [[package]] name = "ff" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df689201f395c6b90dfe87127685f8dbfc083a5e779e613575d8bd7314300c3e" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ - "rand_core 0.6.3", + "rand_core 0.6.4", "subtle", ] @@ -524,18 +497,19 @@ source = "git+https://github.com/terra-money/enterprise-contracts.git?branch=mai dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw20 1.0.1", + "cw20 1.1.2", "thiserror", ] [[package]] name = "generic-array" -version = "0.14.5" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -562,12 +536,12 @@ dependencies = [ [[package]] name = "group" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", - "rand_core 0.6.3", + "rand_core 0.6.4", "subtle", ] @@ -583,7 +557,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.5", + "digest 0.10.7", ] [[package]] @@ -603,14 +577,16 @@ checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" [[package]] name = "k256" -version = "0.11.6" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" dependencies = [ "cfg-if", "ecdsa", "elliptic-curve", + "once_cell", "sha2 0.10.6", + "signature", ] [[package]] @@ -633,9 +609,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "osmosis-std-derive" -version = "0.13.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a455e262a6fdfd3914f3a4e11e6bc0ce491901cb9d507d7856d7ef6e129e90c6" +checksum = "f4d482a16be198ee04e0f94e10dd9b8d02332dcf33bc5ea4b255e7e25eedc5df" dependencies = [ "itertools", "proc-macro2", @@ -678,50 +654,62 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "659bc95805fb3ab45d6db2eda7ee71bfe5fa952e4705146e0ce3d8113daaab57" dependencies = [ "cosmwasm-std", - "cw20 1.0.1", + "cw20 1.1.2", + "schemars", + "serde", +] + +[[package]] +name = "pfc-fee-split" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d362330ac0f385ff8d58ae80075dabaaeddc148a5156e041c36ee743fe96a3a8" +dependencies = [ + "cosmwasm-std", + "cw20 1.1.2", "schemars", "serde", ] [[package]] name = "pfc-steak" -version = "3.0.12" +version = "3.0.14" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw20 1.0.1", - "cw20-base 1.0.0", + "cw20 1.1.2", + "cw20-base 1.1.2", "schemars", "serde", ] [[package]] name = "pfc-steak-hub" -version = "3.0.11" +version = "3.0.14" dependencies = [ "cosmwasm-std", "cw-storage-plus 0.13.4", - "cw2 1.0.1", - "cw20 1.0.1", - "cw20-base 1.0.0", + "cw2 1.1.2", + "cw20 1.1.2", + "cw20-base 1.1.2", "funds-distributor-api", - "pfc-fee-split", + "pfc-fee-split 0.2.5", "pfc-steak", "serde", ] [[package]] name = "pfc-steak-hub-tf" -version = "3.0.12" +version = "3.0.14" dependencies = [ "cosmwasm-std", "cw-item-set", "cw-ownable", - "cw-storage-plus 1.0.1", - "cw2 1.0.1", + "cw-storage-plus 1.2.0", + "cw2 1.1.2", "osmosis-std-derive", "pfc-dust-collector", - "pfc-fee-split", + "pfc-fee-split 1.5.0", "pfc-steak", "prost", "prost-types", @@ -747,7 +735,7 @@ checksum = "217870c932a72a63c52c9126792a5bd29773b1318b65635d900091c8f54d3a5c" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw-storage-plus 1.0.1", + "cw-storage-plus 1.2.0", "pfc-whitelist-derive", "schemars", "serde", @@ -767,9 +755,9 @@ dependencies = [ [[package]] name = "pkcs8" -version = "0.9.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ "der", "spki", @@ -777,18 +765,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.54" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e472a104799c74b514a57226160104aa483546de37e839ec50e3c2e41dd87534" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] [[package]] name = "prost" -version = "0.11.8" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48e50df39172a3e7eb17e14642445da64996989bc212b583015435d39a58537" +checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" dependencies = [ "bytes", "prost-derive", @@ -796,22 +784,22 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.11.8" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea9b0f8cbe5e15a8a042d030bd96668db28ecb567ec37d691971ff5731d2b1b" +checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e" dependencies = [ "anyhow", "itertools", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.55", ] [[package]] name = "prost-types" -version = "0.11.8" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "379119666929a1afd7a043aa6cf96fa67a6dce9af60c88095a4686dbce4c9c88" +checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e" dependencies = [ "prost", ] @@ -839,9 +827,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.26" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -857,22 +845,21 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom 0.2.6", ] [[package]] name = "rfc6979" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88c86280f057430a52f4861551b092a01b419b8eacefc7c995eacb9dc132fe32" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ - "crypto-bigint", "hmac", - "zeroize", + "subtle", ] [[package]] @@ -883,9 +870,9 @@ checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" [[package]] name = "schemars" -version = "0.8.11" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a5fb6c61f29e723026dc8e923d94c694313212abbecbbe5f55a7748eec5b307" +checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" dependencies = [ "dyn-clone", "schemars_derive", @@ -895,9 +882,9 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.11" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f188d036977451159430f3b8dc82ec76364a42b7e289c2b18a9a18f4470058e9" +checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" dependencies = [ "proc-macro2", "quote", @@ -907,9 +894,9 @@ dependencies = [ [[package]] name = "sec1" -version = "0.3.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct", "der", @@ -921,37 +908,37 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.14" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" [[package]] name = "serde" -version = "1.0.136" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde-json-wasm" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15bee9b04dd165c3f4e142628982ddde884c2022a89e8ddf99c4829bf2c3a58" +checksum = "9e9213a07d53faa0b8dd81e767a54a8188a242fdb9be99ab75ec576a774bfdd7" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.136" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.55", ] [[package]] @@ -997,24 +984,24 @@ checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.5", + "digest 0.10.7", ] [[package]] name = "signature" -version = "1.6.4" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "digest 0.10.5", - "rand_core 0.6.3", + "digest 0.10.7", + "rand_core 0.6.4", ] [[package]] name = "spki" -version = "0.6.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", "der", @@ -1045,9 +1032,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.11" +version = "2.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e3787bb71465627110e7d87ed4faaa36c1f61042ee67badb9e2ef173accc40" +checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" dependencies = [ "proc-macro2", "quote", @@ -1056,22 +1043,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.11", + "syn 2.0.55", ] [[package]] @@ -1080,18 +1067,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-ident" version = "1.0.8" @@ -1118,6 +1093,6 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "zeroize" -version = "1.5.7" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/contracts/hub-tf/Cargo.toml b/contracts/hub-tf/Cargo.toml index 12c45de..92ebd18 100644 --- a/contracts/hub-tf/Cargo.toml +++ b/contracts/hub-tf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub-tf" -version = "3.0.12" +version = "3.0.14" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" @@ -10,26 +10,26 @@ repository = "https://github.com/pfc-developer/steak-contracts" crate-type = ["cdylib", "rlib"] [features] -backtraces = ["cosmwasm-std/backtraces"] +#backtraces = ["cosmwasm-std/backtraces"] [dependencies] #cosmwasm-std = { version = "1.2.1", features=["iterator","stargate","cosmwasm_1_1","staking"]} -cosmwasm-std = { version = "1.0.1", features=["iterator","stargate","staking"]} +cosmwasm-std = { version = "1.5.3", features=["iterator","stargate","staking"]} -cw2= "1.0.1" +cw2= "1.1.2" #cw20 = "1.0.0" #cw20-base = { version = "1.0.0", features = ["library"] } -cw-storage-plus = "1.0.1" +cw-storage-plus = "1.2.0" cw-ownable = "0.5.0" cw-item-set = "0.7.0" -prost = {version = "0.11.0", default-features = false, features = ["prost-derive"]} -prost-types = {version = "0.11.1", default-features = false} +prost = {version = "0.12.3", default-features = false, features = ["prost-derive"]} +prost-types = {version = "0.12.3", default-features = false} schemars = "0.8.11" pfc-steak = { path = "../../packages/steak" } serde = { version = "1.0.103", default-features = false, features = ["derive"] } -pfc-fee-split = "0.2.5" +pfc-fee-split = "1.5.0" pfc-dust-collector="0.1.0" #{ path="../../packages/pfc-dust-collector" } -osmosis-std-derive="0.13.2" +osmosis-std-derive="0.15.3" [dev-dependencies] protobuf = { version = "3.1.0", features = ["with-bytes"] } \ No newline at end of file diff --git a/contracts/hub-tf/src/contract.rs b/contracts/hub-tf/src/contract.rs index e32cbcb..ef703a7 100644 --- a/contracts/hub-tf/src/contract.rs +++ b/contracts/hub-tf/src/contract.rs @@ -1,7 +1,7 @@ use std::convert::TryInto; use cosmwasm_std::{ - entry_point, to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdError, + entry_point, to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdError, StdResult, }; use cw2::{get_contract_version, set_contract_version, ContractVersion}; @@ -160,24 +160,24 @@ pub fn reply(_deps: DepsMut, _env: Env, reply: Reply) -> StdResult { #[entry_point] pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { - QueryMsg::Config {} => to_binary(&queries::config(deps)?), - QueryMsg::State {} => to_binary(&queries::state(deps, env)?), - QueryMsg::PendingBatch {} => to_binary(&queries::pending_batch(deps)?), - QueryMsg::PreviousBatch(id) => to_binary(&queries::previous_batch(deps, id)?), + QueryMsg::Config {} => to_json_binary(&queries::config(deps)?), + QueryMsg::State {} => to_json_binary(&queries::state(deps, env)?), + QueryMsg::PendingBatch {} => to_json_binary(&queries::pending_batch(deps)?), + QueryMsg::PreviousBatch(id) => to_json_binary(&queries::previous_batch(deps, id)?), QueryMsg::PreviousBatches { start_after, limit, - } => to_binary(&queries::previous_batches(deps, start_after, limit)?), + } => to_json_binary(&queries::previous_batches(deps, start_after, limit)?), QueryMsg::UnbondRequestsByBatch { id, start_after, limit, - } => to_binary(&queries::unbond_requests_by_batch(deps, id, start_after, limit)?), + } => to_json_binary(&queries::unbond_requests_by_batch(deps, id, start_after, limit)?), QueryMsg::UnbondRequestsByUser { user, start_after, limit, - } => to_binary(&queries::unbond_requests_by_user(deps, user, start_after, limit)?), + } => to_json_binary(&queries::unbond_requests_by_user(deps, user, start_after, limit)?), } } diff --git a/contracts/hub-tf/src/execute.rs b/contracts/hub-tf/src/execute.rs index 202c207..d90acaa 100644 --- a/contracts/hub-tf/src/execute.rs +++ b/contracts/hub-tf/src/execute.rs @@ -1,7 +1,7 @@ use std::{collections::HashSet, iter::FromIterator, str::FromStr}; use cosmwasm_std::{ - to_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, DepsMut, DistributionMsg, Env, Event, + to_json_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, DepsMut, DistributionMsg, Env, Event, Order, ReplyOn, Response, StdError, StdResult, SubMsg, Uint128, WasmMsg, }; use pfc_steak::{ @@ -266,7 +266,7 @@ pub fn bond( } else { CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: receiver.to_string(), - msg: to_binary(&exec_msg)?, + msg: to_json_binary(&exec_msg)?, funds: vec![Coin { denom: steak_denom, amount: usteak_to_mint, @@ -543,7 +543,7 @@ pub fn queue_unbond( if env.block.time.seconds() >= pending_batch.est_unbond_start_time { msgs.push(CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: env.contract.address.into(), - msg: to_binary(&ExecuteMsg::SubmitBatch {})?, + msg: to_json_binary(&ExecuteMsg::SubmitBatch {})?, funds: vec![], })); } @@ -1138,7 +1138,7 @@ pub fn collect_dust(deps: DepsMut, env: Env, max_tokens: usize) -> StdResult= remaining_underflow { batch.amount_unclaimed -= remaining_underflow; diff --git a/contracts/hub-tf/src/testing/custom_querier.rs b/contracts/hub-tf/src/testing/custom_querier.rs index 222ebd6..fbd5fdd 100644 --- a/contracts/hub-tf/src/testing/custom_querier.rs +++ b/contracts/hub-tf/src/testing/custom_querier.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{ - from_slice, + from_json, testing::{BankQuerier, StakingQuerier, MOCK_CONTRACT_ADDR}, Addr, Coin, Empty, FullDelegation, Querier, QuerierResult, QueryRequest, SystemError, WasmQuery, @@ -16,7 +16,7 @@ pub(super) struct CustomQuerier { impl Querier for CustomQuerier { fn raw_query(&self, bin_request: &[u8]) -> QuerierResult { - let request: QueryRequest<_> = match from_slice(bin_request) { + let request: QueryRequest<_> = match from_json(bin_request) { Ok(v) => v, Err(e) => { return Err(SystemError::InvalidRequest { diff --git a/contracts/hub-tf/src/testing/helpers.rs b/contracts/hub-tf/src/testing/helpers.rs index 9a5c6fd..08b8661 100644 --- a/contracts/hub-tf/src/testing/helpers.rs +++ b/contracts/hub-tf/src/testing/helpers.rs @@ -1,9 +1,4 @@ -use cosmwasm_std::{ - from_binary, - testing::{mock_env, MockApi, MockStorage, MOCK_CONTRACT_ADDR}, - Addr, BlockInfo, ContractInfo, Deps, Env, OwnedDeps, QuerierResult, SystemError, SystemResult, - Timestamp, -}; +use cosmwasm_std::{ testing::{mock_env, MockApi, MockStorage, MOCK_CONTRACT_ADDR}, Addr, BlockInfo, ContractInfo, Deps, Env, OwnedDeps, QuerierResult, SystemError, SystemResult, Timestamp, from_json}; use pfc_steak::hub::QueryMsg; use serde::de::DeserializeOwned; @@ -43,5 +38,5 @@ pub(super) fn mock_env_at_timestamp(timestamp: u64) -> Env { pub(super) fn query_helper(deps: Deps, msg: QueryMsg) -> T { let bin = query(deps, mock_env(), msg).unwrap(); //eprintln!("Query Response {:?}",&bin); - from_binary(&bin).unwrap() + from_json(&bin).unwrap() } diff --git a/contracts/hub-tf/src/testing/tests.rs b/contracts/hub-tf/src/testing/tests.rs index 46e150a..0f84e1d 100644 --- a/contracts/hub-tf/src/testing/tests.rs +++ b/contracts/hub-tf/src/testing/tests.rs @@ -1,10 +1,6 @@ use std::str::FromStr; -use cosmwasm_std::{ - testing::{mock_env, mock_info, MockApi, MockStorage, MOCK_CONTRACT_ADDR}, - to_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, DistributionMsg, Order, OwnedDeps, ReplyOn, - StdError, SubMsg, Uint128, WasmMsg, -}; +use cosmwasm_std::{testing::{mock_env, mock_info, MockApi, MockStorage, MOCK_CONTRACT_ADDR}, Addr, BankMsg, Coin, CosmosMsg, Decimal, DistributionMsg, Order, OwnedDeps, ReplyOn, StdError, SubMsg, Uint128, WasmMsg, to_json_binary}; use pfc_steak::{ hub::{ Batch, CallbackMsg, ConfigResponse, PendingBatch, QueryMsg, StateResponse, UnbondRequest, @@ -381,7 +377,7 @@ fn harvesting() { id: 0, msg: CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: MOCK_CONTRACT_ADDR.to_string(), - msg: to_binary(&ExecuteMsg::Callback(CallbackMsg::Reinvest {})).unwrap(), + msg: to_json_binary(&ExecuteMsg::Callback(CallbackMsg::Reinvest {})).unwrap(), funds: vec![], }), gas_limit: None, @@ -608,7 +604,7 @@ fn queuing_unbond() { id: 0, msg: CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: MOCK_CONTRACT_ADDR.to_string(), - msg: to_binary(&ExecuteMsg::SubmitBatch {}).unwrap(), + msg: to_json_binary(&ExecuteMsg::SubmitBatch {}).unwrap(), funds: vec![], }), gas_limit: None, @@ -1068,12 +1064,11 @@ fn withdrawing_unbonded() { ); let err = previous_batches().load(deps.as_ref().storage, 2u64.into()).unwrap_err(); - assert_eq!( - err, - StdError::NotFound { - kind: "pfc_steak::hub::Batch".to_string() - } - ); + match err { StdError::NotFound {..} => {}, _=> { + panic!("Should have been not found") + } }; + + // User 1's unbond requests in batches 1 and 2 should have been deleted let err1 = unbond_requests() @@ -1083,18 +1078,14 @@ fn withdrawing_unbonded() { .load(deps.as_ref().storage, (1u64.into(), &Addr::unchecked("user_1").as_str())) .unwrap_err(); - assert_eq!( - err1, - StdError::NotFound { - kind: "pfc_steak::hub::UnbondRequest".to_string() - } - ); - assert_eq!( - err2, - StdError::NotFound { - kind: "pfc_steak::hub::UnbondRequest".to_string() - } - ); + match err1 { StdError::NotFound {..} => {}, _=> { + panic!("Should have been not found") + } }; + + match err2 { StdError::NotFound {..} => {}, _=> { + panic!("Should have been not found") + } }; + // User 3 attempt to withdraw; also specifying a receiver let res = execute( @@ -1123,23 +1114,19 @@ fn withdrawing_unbonded() { // Batch 1 and user 2's unbonding request should have been purged from storage let err = previous_batches().load(deps.as_ref().storage, 1u64.into()).unwrap_err(); - assert_eq!( - err, - StdError::NotFound { - kind: "pfc_steak::hub::Batch".to_string() - } - ); + match err { StdError::NotFound {..} => {}, _=> { + panic!("Should have been not found") + } }; + let err = unbond_requests() .load(deps.as_ref().storage, (1u64.into(), &Addr::unchecked("user_3").as_str())) .unwrap_err(); - assert_eq!( - err, - StdError::NotFound { - kind: "pfc_steak::hub::UnbondRequest".to_string() - } - ); + match err { StdError::NotFound {..} => {}, _=> { + panic!("Should have been not found") + } }; + } #[test] diff --git a/contracts/hub-tf/src/types/coins.rs b/contracts/hub-tf/src/types/coins.rs index 073d86f..3cd73ef 100644 --- a/contracts/hub-tf/src/types/coins.rs +++ b/contracts/hub-tf/src/types/coins.rs @@ -48,8 +48,8 @@ impl Coins { pub fn find(&self, denom: &str) -> Coin { self.0 .iter() - .cloned() .find(|coin| coin.denom == denom) + .cloned() .unwrap_or_else(|| Coin::new(0, denom)) } } diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index 76ef1e3..5c1538a 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub" -version = "3.0.11" +version = "3.0.14" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub/src/contract.rs b/contracts/hub/src/contract.rs index a5847b0..afa18bb 100644 --- a/contracts/hub/src/contract.rs +++ b/contracts/hub/src/contract.rs @@ -1,7 +1,7 @@ use std::convert::TryInto; use cosmwasm_std::{ - entry_point, from_binary, to_binary, Binary, Decimal, Deps, DepsMut, Env, MessageInfo, Reply, + entry_point, from_json, to_json_binary, Binary, Decimal, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdError, StdResult, }; use cw2::{get_contract_version, set_contract_version, ContractVersion}; @@ -138,7 +138,7 @@ fn receive( cw20_msg: Cw20ReceiveMsg, ) -> StdResult { let api = deps.api; - match from_binary(&cw20_msg.msg)? { + match from_json(&cw20_msg.msg)? { ReceiveMsg::QueueUnbond { receiver, } => { @@ -191,24 +191,24 @@ pub fn reply(deps: DepsMut, env: Env, reply: Reply) -> StdResult { #[entry_point] pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { - QueryMsg::Config {} => to_binary(&queries::config(deps)?), - QueryMsg::State {} => to_binary(&queries::state(deps, env)?), - QueryMsg::PendingBatch {} => to_binary(&queries::pending_batch(deps)?), - QueryMsg::PreviousBatch(id) => to_binary(&queries::previous_batch(deps, id)?), + QueryMsg::Config {} => to_json_binary(&queries::config(deps)?), + QueryMsg::State {} => to_json_binary(&queries::state(deps, env)?), + QueryMsg::PendingBatch {} => to_json_binary(&queries::pending_batch(deps)?), + QueryMsg::PreviousBatch(id) => to_json_binary(&queries::previous_batch(deps, id)?), QueryMsg::PreviousBatches { start_after, limit, - } => to_binary(&queries::previous_batches(deps, start_after, limit)?), + } => to_json_binary(&queries::previous_batches(deps, start_after, limit)?), QueryMsg::UnbondRequestsByBatch { id, start_after, limit, - } => to_binary(&queries::unbond_requests_by_batch(deps, id, start_after, limit)?), + } => to_json_binary(&queries::unbond_requests_by_batch(deps, id, start_after, limit)?), QueryMsg::UnbondRequestsByUser { user, start_after, limit, - } => to_binary(&queries::unbond_requests_by_user(deps, user, start_after, limit)?), + } => to_json_binary(&queries::unbond_requests_by_user(deps, user, start_after, limit)?), } } diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index f7ca472..a2e0726 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -1,7 +1,7 @@ use std::{collections::HashSet, iter::FromIterator, str::FromStr}; use cosmwasm_std::{ - from_binary, to_binary, Addr, BankMsg, Binary, Coin, CosmosMsg, Decimal, DepsMut, + from_json, to_json_binary, Addr, BankMsg, Binary, Coin, CosmosMsg, Decimal, DepsMut, DistributionMsg, Env, Event, Order, Response, StdError, StdResult, SubMsg, SubMsgResponse, Uint128, WasmMsg, }; @@ -75,7 +75,7 @@ pub fn instantiate(deps: DepsMut, env: Env, msg: InstantiateMsg) -> StdResult = if mint_it { vec![CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: steak_token.to_string(), - msg: to_binary(&Cw20ExecuteMsg::Mint { + msg: to_json_binary(&Cw20ExecuteMsg::Mint { recipient: receiver.to_string(), amount: usteak_to_mint, })?, @@ -201,10 +201,10 @@ pub fn bond( let send_transfer_msg: CosmosMsg = match contract_info { Ok(_) => { if let Some(exec_msg) = bond_msg { - match from_binary(&exec_msg)? { + match from_json(exec_msg)? { Cw20HookMsg::Transfer {} => CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: steak_token.to_string(), - msg: to_binary(&Cw20ExecuteMsg::Transfer { + msg: to_json_binary(&Cw20ExecuteMsg::Transfer { recipient: receiver.to_string(), amount: usteak_to_mint, })?, @@ -212,10 +212,10 @@ pub fn bond( }), Cw20HookMsg::Distribute {} => CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: steak_token.to_string(), - msg: to_binary(&Cw20ExecuteMsg::Send { + msg: to_json_binary(&Cw20ExecuteMsg::Send { contract: receiver.to_string(), amount: usteak_to_mint, - msg: to_binary( + msg: to_json_binary( &funds_distributor_api::msg::Cw20HookMsg::Distribute {}, )?, })?, @@ -225,7 +225,7 @@ pub fn bond( } else { CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: steak_token.to_string(), - msg: to_binary(&Cw20ExecuteMsg::Send { + msg: to_json_binary(&Cw20ExecuteMsg::Send { contract: receiver.to_string(), amount: usteak_to_mint, msg: Default::default(), @@ -237,7 +237,7 @@ pub fn bond( Err(_) => { CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: steak_token.to_string(), - msg: to_binary(&Cw20ExecuteMsg::Transfer { + msg: to_json_binary(&Cw20ExecuteMsg::Transfer { recipient: receiver.to_string(), amount: usteak_to_mint, // msg: Default::default(), @@ -249,7 +249,7 @@ pub fn bond( vec![ CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: steak_token.to_string(), - msg: to_binary(&Cw20ExecuteMsg::Mint { + msg: to_json_binary(&Cw20ExecuteMsg::Mint { recipient: env.contract.address.to_string(), //receiver.to_string(), amount: usteak_to_mint, })?, @@ -482,7 +482,7 @@ pub fn queue_unbond( if env.block.time.seconds() >= pending_batch.est_unbond_start_time { msgs.push(CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: env.contract.address.into(), - msg: to_binary(&ExecuteMsg::SubmitBatch {})?, + msg: to_json_binary(&ExecuteMsg::SubmitBatch {})?, funds: vec![], })); } @@ -593,7 +593,7 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { let burn_msg = CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: steak_token.into(), - msg: to_binary(&Cw20ExecuteMsg::Burn { + msg: to_json_binary(&Cw20ExecuteMsg::Burn { amount: pending_batch.usteak_to_burn, })?, funds: vec![], diff --git a/contracts/hub/src/math.rs b/contracts/hub/src/math.rs index 4b752b9..9f2e344 100644 --- a/contracts/hub/src/math.rs +++ b/contracts/hub/src/math.rs @@ -274,7 +274,7 @@ pub(crate) fn reconcile_batches(batches: &mut [Batch], native_to_deduct: Uint128 if !remaining_underflow.is_zero() { // the remaining underflow will be applied by oldest batch first. - for (_, batch) in batches.iter_mut().enumerate() { + for batch in batches.iter_mut(){//.enumerate() { if !batch.amount_unclaimed.is_zero() && !remaining_underflow.is_zero() { if batch.amount_unclaimed >= remaining_underflow { batch.amount_unclaimed -= remaining_underflow; diff --git a/contracts/hub/src/testing/custom_querier.rs b/contracts/hub/src/testing/custom_querier.rs index 18f13c7..d13bff8 100644 --- a/contracts/hub/src/testing/custom_querier.rs +++ b/contracts/hub/src/testing/custom_querier.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use cosmwasm_std::{ - from_binary, from_slice, + from_json, testing::{BankQuerier, StakingQuerier, MOCK_CONTRACT_ADDR}, Addr, Coin, Empty, FullDelegation, Querier, QuerierResult, QueryRequest, SystemError, WasmQuery, @@ -20,7 +20,7 @@ pub(super) struct CustomQuerier { impl Querier for CustomQuerier { fn raw_query(&self, bin_request: &[u8]) -> QuerierResult { - let request: QueryRequest<_> = match from_slice(bin_request) { + let request: QueryRequest<_> = match from_json(bin_request) { Ok(v) => v, Err(e) => { return Err(SystemError::InvalidRequest { @@ -78,7 +78,7 @@ impl CustomQuerier { contract_addr, msg, }) => { - if let Ok(query) = from_binary::(msg) { + if let Ok(query) = from_json::(msg) { return self.cw20_querier.handle_query(&contract_addr, query); } diff --git a/contracts/hub/src/testing/cw20_querier.rs b/contracts/hub/src/testing/cw20_querier.rs index f9de6c7..533f66b 100644 --- a/contracts/hub/src/testing/cw20_querier.rs +++ b/contracts/hub/src/testing/cw20_querier.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use cosmwasm_std::{to_binary, QuerierResult, SystemError, Uint128}; +use cosmwasm_std::{to_json_binary, QuerierResult, SystemError, Uint128}; use cw20::{BalanceResponse, Cw20QueryMsg, TokenInfoResponse}; use super::helpers::err_unsupported_query; @@ -26,7 +26,7 @@ impl Cw20Querier { }) .unwrap(); - Ok(to_binary(&TokenInfoResponse { + Ok(to_json_binary(&TokenInfoResponse { name: "".to_string(), symbol: "".to_string(), decimals: 0, @@ -59,7 +59,7 @@ impl Cw20Querier { }) .unwrap(); - Ok(to_binary(&BalanceResponse { + Ok(to_json_binary(&BalanceResponse { balance: Uint128::new(*balance), }) .into()) diff --git a/contracts/hub/src/testing/helpers.rs b/contracts/hub/src/testing/helpers.rs index d48a360..9864f3f 100644 --- a/contracts/hub/src/testing/helpers.rs +++ b/contracts/hub/src/testing/helpers.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{ - from_binary, + from_json, testing::{mock_env, MockApi, MockStorage, MOCK_CONTRACT_ADDR}, Addr, BlockInfo, ContractInfo, Deps, Env, OwnedDeps, QuerierResult, SystemError, SystemResult, Timestamp, @@ -41,5 +41,5 @@ pub(super) fn mock_env_at_timestamp(timestamp: u64) -> Env { } pub(super) fn query_helper(deps: Deps, msg: QueryMsg) -> T { - from_binary(&query(deps, mock_env(), msg).unwrap()).unwrap() + from_json(&query(deps, mock_env(), msg).unwrap()).unwrap() } diff --git a/contracts/hub/src/testing/tests.rs b/contracts/hub/src/testing/tests.rs index 1e3e243..aec314c 100644 --- a/contracts/hub/src/testing/tests.rs +++ b/contracts/hub/src/testing/tests.rs @@ -2,7 +2,7 @@ use std::str::FromStr; use cosmwasm_std::{ testing::{mock_env, mock_info, MockApi, MockStorage, MOCK_CONTRACT_ADDR}, - to_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, DistributionMsg, Event, Order, OwnedDeps, + to_json_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, DistributionMsg, Event, Order, OwnedDeps, Reply, ReplyOn, StdError, SubMsg, SubMsgResponse, Uint128, WasmMsg, }; use cw20::{Cw20ExecuteMsg, MinterResponse}; @@ -69,7 +69,7 @@ fn setup_test() -> OwnedDeps { CosmosMsg::Wasm(WasmMsg::Instantiate { admin: Some("larry".to_string()), code_id: 69420, - msg: to_binary(&Cw20InstantiateMsg { + msg: to_json_binary(&Cw20InstantiateMsg { name: "Steak Token".to_string(), symbol: "STEAK".to_string(), decimals: 6, @@ -146,7 +146,7 @@ fn setup_test_fee_split() -> OwnedDeps { CosmosMsg::Wasm(WasmMsg::Instantiate { admin: Some("larry".to_string()), code_id: 69420, - msg: to_binary(&Cw20InstantiateMsg { + msg: to_json_binary(&Cw20InstantiateMsg { name: "Steak Token".to_string(), symbol: "STEAK".to_string(), decimals: 6, @@ -296,7 +296,7 @@ fn bonding() { id: 0, msg: CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: "steak_token".to_string(), - msg: to_binary(&Cw20ExecuteMsg::Mint { + msg: to_json_binary(&Cw20ExecuteMsg::Mint { recipient: "cosmos2contract".to_string(), amount: Uint128::new(1_000_000), }) @@ -314,7 +314,7 @@ fn bonding() { id: 0, msg: CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: "steak_token".to_string(), - msg: to_binary(&Cw20ExecuteMsg::Transfer { + msg: to_json_binary(&Cw20ExecuteMsg::Transfer { recipient: "user_1".to_string(), amount: Uint128::new(1000000), }) @@ -362,7 +362,7 @@ fn bonding() { id: 0, msg: CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: "steak_token".to_string(), - msg: to_binary(&Cw20ExecuteMsg::Mint { + msg: to_json_binary(&Cw20ExecuteMsg::Mint { recipient: "cosmos2contract".to_string(), amount: Uint128::new(12043), }) @@ -380,7 +380,7 @@ fn bonding() { id: 0, msg: CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: "steak_token".to_string(), - msg: to_binary(&Cw20ExecuteMsg::Transfer { + msg: to_json_binary(&Cw20ExecuteMsg::Transfer { recipient: "user_3".to_string(), amount: Uint128::new(12043), }) @@ -445,7 +445,7 @@ fn bond_ex() { id: 0, msg: CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: "steak_token".to_string(), - msg: to_binary(&Cw20ExecuteMsg::Mint { + msg: to_json_binary(&Cw20ExecuteMsg::Mint { recipient: "user_1".to_string(), amount: Uint128::new(1_000_000), }) @@ -507,7 +507,7 @@ fn harvesting() { id: 0, msg: CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: MOCK_CONTRACT_ADDR.to_string(), - msg: to_binary(&ExecuteMsg::Callback(CallbackMsg::Reinvest {})).unwrap(), + msg: to_json_binary(&ExecuteMsg::Callback(CallbackMsg::Reinvest {})).unwrap(), funds: vec![], }), gas_limit: None, @@ -719,7 +719,7 @@ fn queuing_unbond() { ExecuteMsg::Receive(cw20::Cw20ReceiveMsg { sender: "hacker".to_string(), amount: Uint128::new(69420), - msg: to_binary(&ReceiveMsg::QueueUnbond { + msg: to_json_binary(&ReceiveMsg::QueueUnbond { receiver: None, }) .unwrap(), @@ -738,7 +738,7 @@ fn queuing_unbond() { ExecuteMsg::Receive(cw20::Cw20ReceiveMsg { sender: "user_1".to_string(), amount: Uint128::new(23456), - msg: to_binary(&ReceiveMsg::QueueUnbond { + msg: to_json_binary(&ReceiveMsg::QueueUnbond { receiver: None, }) .unwrap(), @@ -757,7 +757,7 @@ fn queuing_unbond() { ExecuteMsg::Receive(cw20::Cw20ReceiveMsg { sender: "user_2".to_string(), amount: Uint128::new(69420), - msg: to_binary(&ReceiveMsg::QueueUnbond { + msg: to_json_binary(&ReceiveMsg::QueueUnbond { receiver: Some("user_3".to_string()), }) .unwrap(), @@ -772,7 +772,7 @@ fn queuing_unbond() { id: 0, msg: CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: MOCK_CONTRACT_ADDR.to_string(), - msg: to_binary(&ExecuteMsg::SubmitBatch {}).unwrap(), + msg: to_json_binary(&ExecuteMsg::SubmitBatch {}).unwrap(), funds: vec![], }), gas_limit: None, @@ -918,7 +918,7 @@ fn submitting_batch() { id: 0, msg: CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: "steak_token".to_string(), - msg: to_binary(&Cw20ExecuteMsg::Burn { + msg: to_json_binary(&Cw20ExecuteMsg::Burn { amount: Uint128::new(92876) }) .unwrap(), diff --git a/contracts/hub/src/types/coins.rs b/contracts/hub/src/types/coins.rs index 073d86f..a989120 100644 --- a/contracts/hub/src/types/coins.rs +++ b/contracts/hub/src/types/coins.rs @@ -48,8 +48,9 @@ impl Coins { pub fn find(&self, denom: &str) -> Coin { self.0 .iter() - .cloned() .find(|coin| coin.denom == denom) + .cloned() + .unwrap_or_else(|| Coin::new(0, denom)) } } diff --git a/packages/steak/Cargo.toml b/packages/steak/Cargo.toml index dd3b7e1..5d4db2b 100644 --- a/packages/steak/Cargo.toml +++ b/packages/steak/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak" -version = "3.0.12" +version = "3.0.14" authors = ["larry ", "PFC "] edition = "2018" description = "Liquid steaking protocol for the cosmos" @@ -9,10 +9,10 @@ homepage = "https://liquidsteaking.app" repository = "https://github.com/PFC-developer/steak-contracts" [dependencies] -cosmwasm-std = { version = "1.2.1", features = ["ibc3"] } -cosmwasm-schema = "1.2.1" -cw20 = "1.0.0" -cw20-base = { version = "1.0.0", features = ["library"] } +cosmwasm-std = { version = "1.5.3", features = ["ibc3"] } +cosmwasm-schema = "1.5.3" +cw20 = "1.1.2" +cw20-base = { version = "1.1.2", features = ["library"] } schemars = "0.8.1" serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/packages/steak/src/hub.rs b/packages/steak/src/hub.rs index 4b76042..3fd4c92 100644 --- a/packages/steak/src/hub.rs +++ b/packages/steak/src/hub.rs @@ -1,7 +1,7 @@ use std::str::FromStr; use cosmwasm_std::{ - to_binary, Addr, Binary, Coin, CosmosMsg, Decimal, Empty, StdResult, Uint128, WasmMsg, + to_json_binary, Addr, Binary, Coin, CosmosMsg, Decimal, Empty, StdResult, Uint128, WasmMsg, }; use cw20::Cw20ReceiveMsg; use cw20_base::msg::InstantiateMarketingInfo as Cw20InstantiateMarketingInfo; @@ -169,7 +169,7 @@ impl CallbackMsg { pub fn into_cosmos_msg(&self, contract_addr: &Addr) -> StdResult { Ok(CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: contract_addr.to_string(), - msg: to_binary(&ExecuteMsg::Callback(self.clone()))?, + msg: to_json_binary(&ExecuteMsg::Callback(self.clone()))?, funds: vec![], })) } From e6596839faa677367959a3b03af943371427d4e5 Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Wed, 24 Apr 2024 07:54:02 -0500 Subject: [PATCH 59/77] security: upgrade to cosmwasm-std 1.5.4 --- Cargo.lock | 28 ++++++++++++++-------------- contracts/hub-tf/Cargo.toml | 4 ++-- contracts/hub/Cargo.toml | 4 ++-- contracts/token/Cargo.toml | 4 ++-- packages/steak/Cargo.toml | 6 +++--- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 91c968c..161e2bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,9 +82,9 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "cosmwasm-crypto" -version = "1.5.3" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9934c79e58d9676edfd592557dee765d2a6ef54c09d5aa2edb06156b00148966" +checksum = "e6b4c3f9c4616d6413d4b5fc4c270a4cc32a374b9be08671e80e1a019f805d8f" dependencies = [ "digest 0.10.7", "ecdsa", @@ -96,18 +96,18 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.5.3" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5e72e330bd3bdab11c52b5ecbdeb6a8697a004c57964caeb5d876f0b088b3c" +checksum = "c586ced10c3b00e809ee664a895025a024f60d65d34fe4c09daed4a4db68a3f3" dependencies = [ "syn 1.0.109", ] [[package]] name = "cosmwasm-schema" -version = "1.5.3" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3e3a2136e2a60e8b6582f5dffca5d1a683ed77bf38537d330bc1dfccd69010" +checksum = "8467874827d384c131955ff6f4d47d02e72a956a08eb3c0ff24f8c903a5517b4" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -118,9 +118,9 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.5.3" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d803bea6bd9ed61bd1ee0b4a2eb09ee20dbb539cc6e0b8795614d20952ebb1" +checksum = "f6db85d98ac80922aef465e564d5b21fa9cfac5058cb62df7f116c3682337393" dependencies = [ "proc-macro2", "quote", @@ -129,9 +129,9 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.5.3" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8666e572a3a2519010dde88c04d16e9339ae751b56b2bb35081fe3f7d6be74" +checksum = "712fe58f39d55c812f7b2c84e097cdede3a39d520f89b6dc3153837e31741927" dependencies = [ "base64", "bech32", @@ -673,7 +673,7 @@ dependencies = [ [[package]] name = "pfc-steak" -version = "3.0.14" +version = "3.0.15" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -685,7 +685,7 @@ dependencies = [ [[package]] name = "pfc-steak-hub" -version = "3.0.14" +version = "3.0.15" dependencies = [ "cosmwasm-std", "cw-storage-plus 0.13.4", @@ -700,7 +700,7 @@ dependencies = [ [[package]] name = "pfc-steak-hub-tf" -version = "3.0.14" +version = "3.0.15" dependencies = [ "cosmwasm-std", "cw-item-set", @@ -720,7 +720,7 @@ dependencies = [ [[package]] name = "pfc-steak-token" -version = "2.0.1" +version = "2.0.2" dependencies = [ "cosmwasm-std", "cw20 0.13.4", diff --git a/contracts/hub-tf/Cargo.toml b/contracts/hub-tf/Cargo.toml index 92ebd18..5b3dcc1 100644 --- a/contracts/hub-tf/Cargo.toml +++ b/contracts/hub-tf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub-tf" -version = "3.0.14" +version = "3.0.15" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" @@ -14,7 +14,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] #cosmwasm-std = { version = "1.2.1", features=["iterator","stargate","cosmwasm_1_1","staking"]} -cosmwasm-std = { version = "1.5.3", features=["iterator","stargate","staking"]} +cosmwasm-std = { version = "1.5.4", features=["iterator","stargate","staking"]} cw2= "1.1.2" #cw20 = "1.0.0" diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index 5c1538a..ff085c3 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub" -version = "3.0.14" +version = "3.0.15" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" @@ -13,7 +13,7 @@ crate-type = ["cdylib", "rlib"] backtraces = ["cosmwasm-std/backtraces"] [dependencies] -cosmwasm-std = { version = "1.0", features = ["staking"] } +cosmwasm-std = { version = "1.5.4", features = ["staking"] } cw2= "1.0.0" cw20 = "1.0.1" cw20-base = { version = "1.0.0", features = ["library"] } diff --git a/contracts/token/Cargo.toml b/contracts/token/Cargo.toml index 5fcd874..e2af341 100644 --- a/contracts/token/Cargo.toml +++ b/contracts/token/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-token" -version = "2.0.1" +version = "2.0.2" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" @@ -13,6 +13,6 @@ crate-type = ["cdylib", "rlib"] backtraces = ["cosmwasm-std/backtraces"] [dependencies] -cosmwasm-std = { version = "1.0" } +cosmwasm-std = { version = "1.5.4" } cw20 = "0.13" cw20-base = { version = "0.13", features = ["library"] } diff --git a/packages/steak/Cargo.toml b/packages/steak/Cargo.toml index 5d4db2b..306dca2 100644 --- a/packages/steak/Cargo.toml +++ b/packages/steak/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak" -version = "3.0.14" +version = "3.0.15" authors = ["larry ", "PFC "] edition = "2018" description = "Liquid steaking protocol for the cosmos" @@ -9,8 +9,8 @@ homepage = "https://liquidsteaking.app" repository = "https://github.com/PFC-developer/steak-contracts" [dependencies] -cosmwasm-std = { version = "1.5.3", features = ["ibc3"] } -cosmwasm-schema = "1.5.3" +cosmwasm-std = { version = "1.5.4", features = ["ibc3"] } +cosmwasm-schema = "1.5.4" cw20 = "1.1.2" cw20-base = { version = "1.1.2", features = ["library"] } schemars = "0.8.1" From 3cec0588c6fcea1ae6c4fdcd549a6b1ee08e417f Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Wed, 24 Apr 2024 10:34:25 -0500 Subject: [PATCH 60/77] feat: add .github actions --- .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++++ .github/PULL_REQUEST_TEMPLATE.md | 17 +++++++ .github/workflows/release-artifacts.yml | 37 ++++++++++++++ .github/workflows/tests.yml | 61 +++++++++++++++++++++++ 4 files changed, 135 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/workflows/release-artifacts.yml create mode 100644 .github/workflows/tests.yml diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..bbcbbe7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..cdeb879 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,17 @@ +## 👀 What is Changed? + + * + +## 📣 API Changes + + * + +## 📝 Notes + + * + +## ☑️ Checklist + - [ ] The code compiles successfully + - [ ] I've run `cargo clippy` to lint and check code style + - [ ] I've written the test code and runs successfully + - [ ] I've updated API docs and noticed to the co-workers for updates diff --git a/.github/workflows/release-artifacts.yml b/.github/workflows/release-artifacts.yml new file mode 100644 index 0000000..787cddc --- /dev/null +++ b/.github/workflows/release-artifacts.yml @@ -0,0 +1,37 @@ +name: Release Artifacts + +on: + push: + tags: + - "v[0-9]+.[0-9]+.[0-9]+" # Push events to matching v*, i.e. v1.0, v20.15.10 + - "v[0-9]+.[0-9]+.[0-9]+-rc*" # Push events to matching v*, i.e. v1.0-rc1, v20.15.10-rc5 + +jobs: + release-artifacts: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install latest stable + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + components: rustfmt, clippy + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v1 + - name: Generate Cargo.lock + run: | + cargo build --workspace + cargo fetch --verbose + - name: Build Artifacts + run: | + docker run --rm -v "$(pwd)":/code \ + --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ + --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ + cosmwasm/workspace-optimizer:0.15.1 + tar -zcvf cosmwasm-artifacts.tar.gz artifacts + - name: Create Release + uses: softprops/action-gh-release@v1 + with: + files: cosmwasm-artifacts.tar.gz + body_path: CHANGELOG.md diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..58f0e6f --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,61 @@ +name: Formatting Check & Test + +on: + pull_request: + push: + branches: + - main + - develop + - "release/*" + +jobs: + clippy: + name: Actions - clippy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + components: clippy + profile: minimal + override: true + - run: cargo fetch --verbose + - run: cargo clippy --all --all-targets -- -D warnings + + rustfmt: + name: Actions - rustfmt + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + components: rustfmt + profile: minimal + override: true + - run: cargo +nightly fmt -- --check + + unit-test: + name: Actions - unit test + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ ubuntu-latest ] + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + - run: cargo fetch --verbose + - run: cargo build + - run: cargo test --verbose --all + env: + RUST_BACKTRACE: 1 From a0d9e47b4cdeef988c6f89f653c52c4ecd2b2094 Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Wed, 24 Apr 2024 10:35:53 -0500 Subject: [PATCH 61/77] chore: fmt --- contracts/hub-tf/src/contract.rs | 4 +- contracts/hub-tf/src/math.rs | 3 +- contracts/hub-tf/src/testing/helpers.rs | 7 ++- contracts/hub-tf/src/testing/tests.rs | 66 +++++++++++++++++-------- contracts/hub/src/contract.rs | 4 +- contracts/hub/src/math.rs | 3 +- contracts/hub/src/testing/tests.rs | 6 +-- contracts/hub/src/types/coins.rs | 1 - 8 files changed, 62 insertions(+), 32 deletions(-) diff --git a/contracts/hub-tf/src/contract.rs b/contracts/hub-tf/src/contract.rs index ef703a7..71ac3a9 100644 --- a/contracts/hub-tf/src/contract.rs +++ b/contracts/hub-tf/src/contract.rs @@ -1,8 +1,8 @@ use std::convert::TryInto; use cosmwasm_std::{ - entry_point, to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdError, - StdResult, + entry_point, to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, + StdError, StdResult, }; use cw2::{get_contract_version, set_contract_version, ContractVersion}; use pfc_steak::{ diff --git a/contracts/hub-tf/src/math.rs b/contracts/hub-tf/src/math.rs index 476ce55..45bed74 100644 --- a/contracts/hub-tf/src/math.rs +++ b/contracts/hub-tf/src/math.rs @@ -274,7 +274,8 @@ pub(crate) fn reconcile_batches(batches: &mut [Batch], native_to_deduct: Uint128 if !remaining_underflow.is_zero() { // the remaining underflow will be applied by oldest batch first. - for batch in batches.iter_mut() {//} .enumerate() { + for batch in batches.iter_mut() { + //} .enumerate() { if !batch.amount_unclaimed.is_zero() && !remaining_underflow.is_zero() { if batch.amount_unclaimed >= remaining_underflow { batch.amount_unclaimed -= remaining_underflow; diff --git a/contracts/hub-tf/src/testing/helpers.rs b/contracts/hub-tf/src/testing/helpers.rs index 08b8661..e7149de 100644 --- a/contracts/hub-tf/src/testing/helpers.rs +++ b/contracts/hub-tf/src/testing/helpers.rs @@ -1,4 +1,9 @@ -use cosmwasm_std::{ testing::{mock_env, MockApi, MockStorage, MOCK_CONTRACT_ADDR}, Addr, BlockInfo, ContractInfo, Deps, Env, OwnedDeps, QuerierResult, SystemError, SystemResult, Timestamp, from_json}; +use cosmwasm_std::{ + from_json, + testing::{mock_env, MockApi, MockStorage, MOCK_CONTRACT_ADDR}, + Addr, BlockInfo, ContractInfo, Deps, Env, OwnedDeps, QuerierResult, SystemError, SystemResult, + Timestamp, +}; use pfc_steak::hub::QueryMsg; use serde::de::DeserializeOwned; diff --git a/contracts/hub-tf/src/testing/tests.rs b/contracts/hub-tf/src/testing/tests.rs index 0f84e1d..68a5285 100644 --- a/contracts/hub-tf/src/testing/tests.rs +++ b/contracts/hub-tf/src/testing/tests.rs @@ -1,6 +1,10 @@ use std::str::FromStr; -use cosmwasm_std::{testing::{mock_env, mock_info, MockApi, MockStorage, MOCK_CONTRACT_ADDR}, Addr, BankMsg, Coin, CosmosMsg, Decimal, DistributionMsg, Order, OwnedDeps, ReplyOn, StdError, SubMsg, Uint128, WasmMsg, to_json_binary}; +use cosmwasm_std::{ + testing::{mock_env, mock_info, MockApi, MockStorage, MOCK_CONTRACT_ADDR}, + to_json_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, DistributionMsg, Order, OwnedDeps, + ReplyOn, StdError, SubMsg, Uint128, WasmMsg, +}; use pfc_steak::{ hub::{ Batch, CallbackMsg, ConfigResponse, PendingBatch, QueryMsg, StateResponse, UnbondRequest, @@ -1064,11 +1068,14 @@ fn withdrawing_unbonded() { ); let err = previous_batches().load(deps.as_ref().storage, 2u64.into()).unwrap_err(); - match err { StdError::NotFound {..} => {}, _=> { - panic!("Should have been not found") - } }; - - + match err { + StdError::NotFound { + .. + } => {}, + _ => { + panic!("Should have been not found") + }, + }; // User 1's unbond requests in batches 1 and 2 should have been deleted let err1 = unbond_requests() @@ -1078,14 +1085,23 @@ fn withdrawing_unbonded() { .load(deps.as_ref().storage, (1u64.into(), &Addr::unchecked("user_1").as_str())) .unwrap_err(); - match err1 { StdError::NotFound {..} => {}, _=> { - panic!("Should have been not found") - } }; - - match err2 { StdError::NotFound {..} => {}, _=> { - panic!("Should have been not found") - } }; + match err1 { + StdError::NotFound { + .. + } => {}, + _ => { + panic!("Should have been not found") + }, + }; + match err2 { + StdError::NotFound { + .. + } => {}, + _ => { + panic!("Should have been not found") + }, + }; // User 3 attempt to withdraw; also specifying a receiver let res = execute( @@ -1114,19 +1130,27 @@ fn withdrawing_unbonded() { // Batch 1 and user 2's unbonding request should have been purged from storage let err = previous_batches().load(deps.as_ref().storage, 1u64.into()).unwrap_err(); - match err { StdError::NotFound {..} => {}, _=> { - panic!("Should have been not found") - } }; - + match err { + StdError::NotFound { + .. + } => {}, + _ => { + panic!("Should have been not found") + }, + }; let err = unbond_requests() .load(deps.as_ref().storage, (1u64.into(), &Addr::unchecked("user_3").as_str())) .unwrap_err(); - match err { StdError::NotFound {..} => {}, _=> { - panic!("Should have been not found") - } }; - + match err { + StdError::NotFound { + .. + } => {}, + _ => { + panic!("Should have been not found") + }, + }; } #[test] diff --git a/contracts/hub/src/contract.rs b/contracts/hub/src/contract.rs index afa18bb..50d7ac3 100644 --- a/contracts/hub/src/contract.rs +++ b/contracts/hub/src/contract.rs @@ -1,8 +1,8 @@ use std::convert::TryInto; use cosmwasm_std::{ - entry_point, from_json, to_json_binary, Binary, Decimal, Deps, DepsMut, Env, MessageInfo, Reply, - Response, StdError, StdResult, + entry_point, from_json, to_json_binary, Binary, Decimal, Deps, DepsMut, Env, MessageInfo, + Reply, Response, StdError, StdResult, }; use cw2::{get_contract_version, set_contract_version, ContractVersion}; use cw20::Cw20ReceiveMsg; diff --git a/contracts/hub/src/math.rs b/contracts/hub/src/math.rs index 9f2e344..f26f4b6 100644 --- a/contracts/hub/src/math.rs +++ b/contracts/hub/src/math.rs @@ -274,7 +274,8 @@ pub(crate) fn reconcile_batches(batches: &mut [Batch], native_to_deduct: Uint128 if !remaining_underflow.is_zero() { // the remaining underflow will be applied by oldest batch first. - for batch in batches.iter_mut(){//.enumerate() { + for batch in batches.iter_mut() { + //.enumerate() { if !batch.amount_unclaimed.is_zero() && !remaining_underflow.is_zero() { if batch.amount_unclaimed >= remaining_underflow { batch.amount_unclaimed -= remaining_underflow; diff --git a/contracts/hub/src/testing/tests.rs b/contracts/hub/src/testing/tests.rs index aec314c..9383c3e 100644 --- a/contracts/hub/src/testing/tests.rs +++ b/contracts/hub/src/testing/tests.rs @@ -2,8 +2,8 @@ use std::str::FromStr; use cosmwasm_std::{ testing::{mock_env, mock_info, MockApi, MockStorage, MOCK_CONTRACT_ADDR}, - to_json_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, DistributionMsg, Event, Order, OwnedDeps, - Reply, ReplyOn, StdError, SubMsg, SubMsgResponse, Uint128, WasmMsg, + to_json_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, DistributionMsg, Event, Order, + OwnedDeps, Reply, ReplyOn, StdError, SubMsg, SubMsgResponse, Uint128, WasmMsg, }; use cw20::{Cw20ExecuteMsg, MinterResponse}; use cw20_base::msg::InstantiateMsg as Cw20InstantiateMsg; @@ -1234,7 +1234,7 @@ fn withdrawing_unbonded() { assert_eq!( err, StdError::NotFound { - kind: "pfc_steak::hub::Batch".to_string() + kind: "pfc_steak::hub::Batch".to_string(), } ); diff --git a/contracts/hub/src/types/coins.rs b/contracts/hub/src/types/coins.rs index a989120..3cd73ef 100644 --- a/contracts/hub/src/types/coins.rs +++ b/contracts/hub/src/types/coins.rs @@ -50,7 +50,6 @@ impl Coins { .iter() .find(|coin| coin.denom == denom) .cloned() - .unwrap_or_else(|| Coin::new(0, denom)) } } From 432c0b2e9f40faf2d2461fb80bf6161aab1cb9bb Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Wed, 24 Apr 2024 10:47:31 -0500 Subject: [PATCH 62/77] chore: clippy on tests --- contracts/hub-tf/src/testing/helpers.rs | 2 +- contracts/hub-tf/src/testing/tests.rs | 52 ++++++++++----------- contracts/hub/src/testing/custom_querier.rs | 2 +- contracts/hub/src/testing/helpers.rs | 2 +- contracts/hub/src/testing/tests.rs | 48 +++++++++---------- 5 files changed, 51 insertions(+), 55 deletions(-) diff --git a/contracts/hub-tf/src/testing/helpers.rs b/contracts/hub-tf/src/testing/helpers.rs index e7149de..636a264 100644 --- a/contracts/hub-tf/src/testing/helpers.rs +++ b/contracts/hub-tf/src/testing/helpers.rs @@ -43,5 +43,5 @@ pub(super) fn mock_env_at_timestamp(timestamp: u64) -> Env { pub(super) fn query_helper(deps: Deps, msg: QueryMsg) -> T { let bin = query(deps, mock_env(), msg).unwrap(); //eprintln!("Query Response {:?}",&bin); - from_json(&bin).unwrap() + from_json(bin).unwrap() } diff --git a/contracts/hub-tf/src/testing/tests.rs b/contracts/hub-tf/src/testing/tests.rs index 68a5285..b7f0be6 100644 --- a/contracts/hub-tf/src/testing/tests.rs +++ b/contracts/hub-tf/src/testing/tests.rs @@ -400,7 +400,7 @@ fn reinvesting() { Delegation::new("bob", 333333, "uxyz"), Delegation::new("charlie", 333333, "uxyz"), ]); - state.prev_denom.save(deps.as_mut().storage, &Uint128::from(0 as u32)).unwrap(); + state.prev_denom.save(deps.as_mut().storage, &Uint128::zero()).unwrap(); deps.querier.set_bank_balances(&[Coin::new(234u128, "uxyz")]); // After the swaps, `unlocked_coins` should contain only uxyz and unknown denoms @@ -472,7 +472,7 @@ fn reinvesting_fee_split() { Delegation::new("bob", 333333, "uxyz"), Delegation::new("charlie", 333333, "uxyz"), ]); - state.prev_denom.save(deps.as_mut().storage, &Uint128::from(0 as u32)).unwrap(); + state.prev_denom.save(deps.as_mut().storage, &Uint128::zero()).unwrap(); deps.querier.set_bank_balances(&[Coin::new(234u128, "uxyz")]); // After the swaps, `unlocked_coins` should contain only uxyz and unknown denoms @@ -618,10 +618,10 @@ fn queuing_unbond() { // The users' unbonding requests should have been saved let ubr1 = unbond_requests() - .load(deps.as_ref().storage, (1u64.into(), &Addr::unchecked("user_1").to_string())) + .load(deps.as_ref().storage, (1u64, Addr::unchecked("user_1").as_ref())) .unwrap(); let ubr2 = unbond_requests() - .load(deps.as_ref().storage, (1u64.into(), &Addr::unchecked("user_3").to_string())) + .load(deps.as_ref().storage, (1u64, Addr::unchecked("user_3").as_ref())) .unwrap(); assert_eq!( @@ -686,7 +686,7 @@ fn submitting_batch() { unbond_requests() .save( deps.as_mut().storage, - (unbond_request.id.into(), &Addr::unchecked(unbond_request.user.clone()).as_str()), + (unbond_request.id, &Addr::unchecked(unbond_request.user.clone()).as_str()), unbond_request, ) .unwrap(); @@ -781,7 +781,7 @@ fn submitting_batch() { ); // Previous batch should have been updated - let previous_batch = previous_batches().load(deps.as_ref().storage, 1u64.into()).unwrap(); + let previous_batch = previous_batches().load(deps.as_ref().storage, 1u64).unwrap(); assert_eq!( previous_batch, Batch { @@ -831,9 +831,7 @@ fn reconciling() { ]; for previous_batch in &previous_batch_list { - previous_batches() - .save(deps.as_mut().storage, previous_batch.id.into(), previous_batch) - .unwrap(); + previous_batches().save(deps.as_mut().storage, previous_batch.id, previous_batch).unwrap(); } state @@ -877,7 +875,7 @@ fn reconciling() { // remainder: 0 // batch 2: 1385 - 273 = 1112 // batch 3: 1506 - 273 = 1233 - let batch = previous_batches().load(deps.as_ref().storage, 2u64.into()).unwrap(); + let batch = previous_batches().load(deps.as_ref().storage, 2u64).unwrap(); assert_eq!( batch, Batch { @@ -889,7 +887,7 @@ fn reconciling() { } ); - let batch = previous_batches().load(deps.as_ref().storage, 3u64.into()).unwrap(); + let batch = previous_batches().load(deps.as_ref().storage, 3u64).unwrap(); assert_eq!( batch, Batch { @@ -902,10 +900,10 @@ fn reconciling() { ); // Batches 1 and 4 should not have changed - let batch = previous_batches().load(deps.as_ref().storage, 1u64.into()).unwrap(); + let batch = previous_batches().load(deps.as_ref().storage, 1u64).unwrap(); assert_eq!(batch, previous_batch_list[0]); - let batch = previous_batches().load(deps.as_ref().storage, 4u64.into()).unwrap(); + let batch = previous_batches().load(deps.as_ref().storage, 4u64).unwrap(); assert_eq!(batch, previous_batch_list[3]); } @@ -950,7 +948,7 @@ fn withdrawing_unbonded() { unbond_requests() .save( deps.as_mut().storage, - (unbond_request.id.into(), &Addr::unchecked(unbond_request.user.clone()).as_str()), + (unbond_request.id, &Addr::unchecked(unbond_request.user.clone()).as_str()), unbond_request, ) .unwrap(); @@ -989,9 +987,7 @@ fn withdrawing_unbonded() { ]; for previous_batch in &previous_batch_list { - previous_batches() - .save(deps.as_mut().storage, previous_batch.id.into(), previous_batch) - .unwrap(); + previous_batches().save(deps.as_mut().storage, previous_batch.id, previous_batch).unwrap(); } state @@ -1055,7 +1051,7 @@ fn withdrawing_unbonded() { ); // Previous batches should have been updated - let batch = previous_batches().load(deps.as_ref().storage, 1u64.into()).unwrap(); + let batch = previous_batches().load(deps.as_ref().storage, 1u64).unwrap(); assert_eq!( batch, Batch { @@ -1067,7 +1063,7 @@ fn withdrawing_unbonded() { } ); - let err = previous_batches().load(deps.as_ref().storage, 2u64.into()).unwrap_err(); + let err = previous_batches().load(deps.as_ref().storage, 2u64).unwrap_err(); match err { StdError::NotFound { .. @@ -1079,10 +1075,10 @@ fn withdrawing_unbonded() { // User 1's unbond requests in batches 1 and 2 should have been deleted let err1 = unbond_requests() - .load(deps.as_ref().storage, (1u64.into(), &Addr::unchecked("user_1").as_str())) + .load(deps.as_ref().storage, (1u64, &Addr::unchecked("user_1").as_str())) .unwrap_err(); let err2 = unbond_requests() - .load(deps.as_ref().storage, (1u64.into(), &Addr::unchecked("user_1").as_str())) + .load(deps.as_ref().storage, (1u64, &Addr::unchecked("user_1").as_str())) .unwrap_err(); match err1 { @@ -1129,7 +1125,7 @@ fn withdrawing_unbonded() { ); // Batch 1 and user 2's unbonding request should have been purged from storage - let err = previous_batches().load(deps.as_ref().storage, 1u64.into()).unwrap_err(); + let err = previous_batches().load(deps.as_ref().storage, 1u64).unwrap_err(); match err { StdError::NotFound { .. @@ -1140,7 +1136,7 @@ fn withdrawing_unbonded() { }; let err = unbond_requests() - .load(deps.as_ref().storage, (1u64.into(), &Addr::unchecked("user_3").as_str())) + .load(deps.as_ref().storage, (1u64, &Addr::unchecked("user_3").as_str())) .unwrap_err(); match err { @@ -1460,7 +1456,7 @@ fn querying_previous_batches() { //let state = State::default(); for batch in &batches { - previous_batches().save(deps.as_mut().storage, batch.id.into(), batch).unwrap(); + previous_batches().save(deps.as_mut().storage, batch.id, batch).unwrap(); } // Querying a single batch @@ -1558,7 +1554,7 @@ fn querying_unbond_requests() { unbond_requests() .save( deps.as_mut().storage, - (unbond_request.id.into(), &Addr::unchecked(unbond_request.user.clone()).as_str()), + (unbond_request.id, &Addr::unchecked(unbond_request.user.clone()).as_str()), unbond_request, ) .unwrap(); @@ -1724,7 +1720,7 @@ fn computing_redelegations_for_rebalancing() { compute_redelegations_for_rebalancing( active_validators, ¤t_delegations, - Uint128::from(10 as u64) + Uint128::from(10u64) ), expected, ); @@ -1741,7 +1737,7 @@ fn computing_redelegations_for_rebalancing() { compute_redelegations_for_rebalancing( partially_active.clone(), ¤t_delegations, - Uint128::from(10 as u64) + Uint128::from(10u64) ), partially_expected, ); @@ -1754,7 +1750,7 @@ fn computing_redelegations_for_rebalancing() { compute_redelegations_for_rebalancing( partially_active, ¤t_delegations, - Uint128::from(15_000 as u64) + Uint128::from(15_000u64) ), partially_expected_minimums, ); diff --git a/contracts/hub/src/testing/custom_querier.rs b/contracts/hub/src/testing/custom_querier.rs index d13bff8..7fe2cb0 100644 --- a/contracts/hub/src/testing/custom_querier.rs +++ b/contracts/hub/src/testing/custom_querier.rs @@ -79,7 +79,7 @@ impl CustomQuerier { msg, }) => { if let Ok(query) = from_json::(msg) { - return self.cw20_querier.handle_query(&contract_addr, query); + return self.cw20_querier.handle_query(contract_addr, query); } err_unsupported_query(msg) diff --git a/contracts/hub/src/testing/helpers.rs b/contracts/hub/src/testing/helpers.rs index 9864f3f..9f51d21 100644 --- a/contracts/hub/src/testing/helpers.rs +++ b/contracts/hub/src/testing/helpers.rs @@ -41,5 +41,5 @@ pub(super) fn mock_env_at_timestamp(timestamp: u64) -> Env { } pub(super) fn query_helper(deps: Deps, msg: QueryMsg) -> T { - from_json(&query(deps, mock_env(), msg).unwrap()).unwrap() + from_json(query(deps, mock_env(), msg).unwrap()).unwrap() } diff --git a/contracts/hub/src/testing/tests.rs b/contracts/hub/src/testing/tests.rs index 9383c3e..431b888 100644 --- a/contracts/hub/src/testing/tests.rs +++ b/contracts/hub/src/testing/tests.rs @@ -570,7 +570,7 @@ fn reinvesting() { Delegation::new("bob", 333333, "uxyz"), Delegation::new("charlie", 333333, "uxyz"), ]); - state.prev_denom.save(deps.as_mut().storage, &Uint128::from(0 as u32)).unwrap(); + state.prev_denom.save(deps.as_mut().storage, &Uint128::zero()).unwrap(); deps.querier.set_bank_balances(&[Coin::new(234u128, "uxyz")]); // After the swaps, `unlocked_coins` should contain only uxyz and unknown denoms @@ -642,7 +642,7 @@ fn reinvesting_fee_split() { Delegation::new("bob", 333333, "uxyz"), Delegation::new("charlie", 333333, "uxyz"), ]); - state.prev_denom.save(deps.as_mut().storage, &Uint128::from(0 as u32)).unwrap(); + state.prev_denom.save(deps.as_mut().storage, &Uint128::zero()).unwrap(); deps.querier.set_bank_balances(&[Coin::new(234u128, "uxyz")]); // After the swaps, `unlocked_coins` should contain only uxyz and unknown denoms @@ -783,11 +783,11 @@ fn queuing_unbond() { // The users' unbonding requests should have been saved let ubr1 = state .unbond_requests - .load(deps.as_ref().storage, (1u64.into(), &Addr::unchecked("user_1"))) + .load(deps.as_ref().storage, (1u64, &Addr::unchecked("user_1"))) .unwrap(); let ubr2 = state .unbond_requests - .load(deps.as_ref().storage, (1u64.into(), &Addr::unchecked("user_3"))) + .load(deps.as_ref().storage, (1u64, &Addr::unchecked("user_3"))) .unwrap(); assert_eq!( @@ -853,7 +853,7 @@ fn submitting_batch() { .unbond_requests .save( deps.as_mut().storage, - (unbond_request.id.into(), &Addr::unchecked(unbond_request.user.clone())), + (unbond_request.id, &Addr::unchecked(unbond_request.user.clone())), unbond_request, ) .unwrap(); @@ -941,7 +941,7 @@ fn submitting_batch() { ); // Previous batch should have been updated - let previous_batch = state.previous_batches.load(deps.as_ref().storage, 1u64.into()).unwrap(); + let previous_batch = state.previous_batches.load(deps.as_ref().storage, 1u64).unwrap(); assert_eq!( previous_batch, Batch { @@ -993,7 +993,7 @@ fn reconciling() { for previous_batch in &previous_batches { state .previous_batches - .save(deps.as_mut().storage, previous_batch.id.into(), previous_batch) + .save(deps.as_mut().storage, previous_batch.id, previous_batch) .unwrap(); } @@ -1038,7 +1038,7 @@ fn reconciling() { // remainder: 0 // batch 2: 1385 - 273 = 1112 // batch 3: 1506 - 273 = 1233 - let batch = state.previous_batches.load(deps.as_ref().storage, 2u64.into()).unwrap(); + let batch = state.previous_batches.load(deps.as_ref().storage, 2u64).unwrap(); assert_eq!( batch, Batch { @@ -1050,7 +1050,7 @@ fn reconciling() { } ); - let batch = state.previous_batches.load(deps.as_ref().storage, 3u64.into()).unwrap(); + let batch = state.previous_batches.load(deps.as_ref().storage, 3u64).unwrap(); assert_eq!( batch, Batch { @@ -1063,10 +1063,10 @@ fn reconciling() { ); // Batches 1 and 4 should not have changed - let batch = state.previous_batches.load(deps.as_ref().storage, 1u64.into()).unwrap(); + let batch = state.previous_batches.load(deps.as_ref().storage, 1u64).unwrap(); assert_eq!(batch, previous_batches[0]); - let batch = state.previous_batches.load(deps.as_ref().storage, 4u64.into()).unwrap(); + let batch = state.previous_batches.load(deps.as_ref().storage, 4u64).unwrap(); assert_eq!(batch, previous_batches[3]); } @@ -1112,7 +1112,7 @@ fn withdrawing_unbonded() { .unbond_requests .save( deps.as_mut().storage, - (unbond_request.id.into(), &Addr::unchecked(unbond_request.user.clone())), + (unbond_request.id, &Addr::unchecked(unbond_request.user.clone())), unbond_request, ) .unwrap(); @@ -1153,7 +1153,7 @@ fn withdrawing_unbonded() { for previous_batch in &previous_batches { state .previous_batches - .save(deps.as_mut().storage, previous_batch.id.into(), previous_batch) + .save(deps.as_mut().storage, previous_batch.id, previous_batch) .unwrap(); } @@ -1218,7 +1218,7 @@ fn withdrawing_unbonded() { ); // Previous batches should have been updated - let batch = state.previous_batches.load(deps.as_ref().storage, 1u64.into()).unwrap(); + let batch = state.previous_batches.load(deps.as_ref().storage, 1u64).unwrap(); assert_eq!( batch, Batch { @@ -1230,7 +1230,7 @@ fn withdrawing_unbonded() { } ); - let err = state.previous_batches.load(deps.as_ref().storage, 2u64.into()).unwrap_err(); + let err = state.previous_batches.load(deps.as_ref().storage, 2u64).unwrap_err(); assert_eq!( err, StdError::NotFound { @@ -1241,11 +1241,11 @@ fn withdrawing_unbonded() { // User 1's unbond requests in batches 1 and 2 should have been deleted let err1 = state .unbond_requests - .load(deps.as_ref().storage, (1u64.into(), &Addr::unchecked("user_1"))) + .load(deps.as_ref().storage, (1u64, &Addr::unchecked("user_1"))) .unwrap_err(); let err2 = state .unbond_requests - .load(deps.as_ref().storage, (1u64.into(), &Addr::unchecked("user_1"))) + .load(deps.as_ref().storage, (1u64, &Addr::unchecked("user_1"))) .unwrap_err(); assert_eq!( @@ -1287,7 +1287,7 @@ fn withdrawing_unbonded() { ); // Batch 1 and user 2's unbonding request should have been purged from storage - let err = state.previous_batches.load(deps.as_ref().storage, 1u64.into()).unwrap_err(); + let err = state.previous_batches.load(deps.as_ref().storage, 1u64).unwrap_err(); assert_eq!( err, StdError::NotFound { @@ -1297,7 +1297,7 @@ fn withdrawing_unbonded() { let err = state .unbond_requests - .load(deps.as_ref().storage, (1u64.into(), &Addr::unchecked("user_3"))) + .load(deps.as_ref().storage, (1u64, &Addr::unchecked("user_3"))) .unwrap_err(); assert_eq!( @@ -1615,7 +1615,7 @@ fn querying_previous_batches() { let state = State::default(); for batch in &batches { - state.previous_batches.save(deps.as_mut().storage, batch.id.into(), batch).unwrap(); + state.previous_batches.save(deps.as_mut().storage, batch.id, batch).unwrap(); } // Querying a single batch @@ -1716,7 +1716,7 @@ fn querying_unbond_requests() { .unbond_requests .save( deps.as_mut().storage, - (unbond_request.id.into(), &Addr::unchecked(unbond_request.user.clone())), + (unbond_request.id, &Addr::unchecked(unbond_request.user.clone())), unbond_request, ) .unwrap(); @@ -1877,7 +1877,7 @@ fn computing_redelegations_for_rebalancing() { compute_redelegations_for_rebalancing( active_validators, ¤t_delegations, - Uint128::from(10 as u64) + Uint128::from(10u64) ), expected, ); @@ -1894,7 +1894,7 @@ fn computing_redelegations_for_rebalancing() { compute_redelegations_for_rebalancing( partially_active.clone(), ¤t_delegations, - Uint128::from(10 as u64) + Uint128::from(10u64) ), partially_expected, ); @@ -1907,7 +1907,7 @@ fn computing_redelegations_for_rebalancing() { compute_redelegations_for_rebalancing( partially_active, ¤t_delegations, - Uint128::from(15_000 as u64) + Uint128::from(15_000u64) ), partially_expected_minimums, ); From def0b49ed42a54105e13672873a62826ed5a2282 Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Sun, 5 May 2024 11:21:01 -0500 Subject: [PATCH 63/77] 3.0.16: add feature to change base denom. add archway --- Cargo.lock | 10 +++++----- contracts/hub-tf/Cargo.toml | 2 +- contracts/hub-tf/src/contract.rs | 1 + contracts/hub-tf/src/execute.rs | 15 +++++++++++++++ contracts/hub/Cargo.toml | 2 +- contracts/hub/src/contract.rs | 1 + contracts/hub/src/execute.rs | 15 +++++++++++++++ init/archway_hub_init.json | 23 +++++++++++++++++++++++ packages/steak/Cargo.toml | 2 +- packages/steak/src/hub.rs | 4 ++++ packages/steak/src/hub_tf.rs | 4 ++++ 11 files changed, 71 insertions(+), 8 deletions(-) create mode 100644 init/archway_hub_init.json diff --git a/Cargo.lock b/Cargo.lock index 161e2bb..25719b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -492,8 +492,8 @@ checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" [[package]] name = "funds-distributor-api" -version = "0.1.0" -source = "git+https://github.com/terra-money/enterprise-contracts.git?branch=main#6d736c0bbc1a6ac52d862c8b80d63be6bb43d419" +version = "1.0.0" +source = "git+https://github.com/terra-money/enterprise-contracts.git?branch=main#847a3eece581f1e3be23c0cf43e2f9dc8c2b94f4" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -673,7 +673,7 @@ dependencies = [ [[package]] name = "pfc-steak" -version = "3.0.15" +version = "3.0.16" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -685,7 +685,7 @@ dependencies = [ [[package]] name = "pfc-steak-hub" -version = "3.0.15" +version = "3.0.16" dependencies = [ "cosmwasm-std", "cw-storage-plus 0.13.4", @@ -700,7 +700,7 @@ dependencies = [ [[package]] name = "pfc-steak-hub-tf" -version = "3.0.15" +version = "3.0.16" dependencies = [ "cosmwasm-std", "cw-item-set", diff --git a/contracts/hub-tf/Cargo.toml b/contracts/hub-tf/Cargo.toml index 5b3dcc1..33d18e9 100644 --- a/contracts/hub-tf/Cargo.toml +++ b/contracts/hub-tf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub-tf" -version = "3.0.15" +version = "3.0.16" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub-tf/src/contract.rs b/contracts/hub-tf/src/contract.rs index 71ac3a9..853398c 100644 --- a/contracts/hub-tf/src/contract.rs +++ b/contracts/hub-tf/src/contract.rs @@ -125,6 +125,7 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S ExecuteMsg::ReturnDenom {} => { execute::bond(deps, env, info.sender, info.funds, None, false) }, + ExecuteMsg::SetBaseDenom {new_denom} => execute::set_base_denom(deps,info.sender,new_denom), ExecuteMsg::ChangeTokenFactory { token_factory_type, } => execute::change_token_factory(deps, info.sender, &token_factory_type), diff --git a/contracts/hub-tf/src/execute.rs b/contracts/hub-tf/src/execute.rs index d90acaa..580bb1a 100644 --- a/contracts/hub-tf/src/execute.rs +++ b/contracts/hub-tf/src/execute.rs @@ -1185,6 +1185,21 @@ pub fn redelegate( .add_event(event) .add_attribute("action", "steakhub/redelegate")) } +pub fn set_base_denom( + deps: DepsMut, + sender: Addr, + new_denom: String, +) -> StdResult { + let state = State::default(); + + state.assert_owner(deps.storage, &sender)?; + state.denom.save(deps.storage, &new_denom)?; + + let event = Event::new("steak/set_base_denom") + .add_attribute("base_denom", new_denom); + + Ok(Response::new().add_event(event).add_attribute("action", "steakhub/set_base_denom")) +} pub fn change_token_factory( deps: DepsMut, diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index ff085c3..3a938cd 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub" -version = "3.0.15" +version = "3.0.16" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub/src/contract.rs b/contracts/hub/src/contract.rs index 50d7ac3..99b58e3 100644 --- a/contracts/hub/src/contract.rs +++ b/contracts/hub/src/contract.rs @@ -128,6 +128,7 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S } }, ExecuteMsg::ReturnDenom {} => execute::return_denom(deps, env, info.funds), + ExecuteMsg::SetBaseDenom {new_denom} => execute::set_base_denom(deps,info.sender, new_denom) } } diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index a2e0726..f9010b5 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -1033,6 +1033,21 @@ pub fn collect_dust(deps: DepsMut, _env: Env, _max_tokens: usize) -> StdResult StdResult { + let state = State::default(); + + state.assert_owner(deps.storage, &sender)?; + state.denom.save(deps.storage, &new_denom)?; + + let event = Event::new("steak/set_base_denom") + .add_attribute("base_denom", new_denom); + + Ok(Response::new().add_event(event).add_attribute("action", "steakhub/set_base_denom")) +} pub fn return_denom(deps: DepsMut, _env: Env, _funds: Vec) -> StdResult { let state = State::default(); diff --git a/init/archway_hub_init.json b/init/archway_hub_init.json new file mode 100644 index 0000000..ebcdddf --- /dev/null +++ b/init/archway_hub_init.json @@ -0,0 +1,23 @@ +{ + "cw20_code_id": 446, + "owner": "archway1lu92zj8q6cmrptu09rp3343x9969r9qrg72hzj", + "name": "boneARCH Token", + "symbol": "boneARCH", + "decimals": 18, + "epoch_period": 259200, + "unbond_period": 1814400, + "validators": [ + "archwayvaloper1cz6unq70w0sdm2gwghv8m4uqmhwxz5x4adam88" + ], + "denom": "aarch", + "fee_account": "archway1pzjprqczpn0fvn5kvqzs74mggg4539rt2vpmp07ffp3pufp6cc3qqmhdg3", + "fee_amount": "0.10", + "max_fee_amount": "0.10", + "fee_account_type": "FeeSplit", + "label": "boneARCH", + "marketing": { + "project": "https://backbonelabs.io", + "description": "The GraveDigger is the Liquid Staking Product of BackBone Labs. It's liquid staking derivative (LSD) is boneARCH.", + "marketing": "archway1lu92zj8q6cmrptu09rp3343x9969r9qrg72hzj" + } +} \ No newline at end of file diff --git a/packages/steak/Cargo.toml b/packages/steak/Cargo.toml index 306dca2..573cff5 100644 --- a/packages/steak/Cargo.toml +++ b/packages/steak/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak" -version = "3.0.15" +version = "3.0.16" authors = ["larry ", "PFC "] edition = "2018" description = "Liquid steaking protocol for the cosmos" diff --git a/packages/steak/src/hub.rs b/packages/steak/src/hub.rs index 3fd4c92..4dcce0d 100644 --- a/packages/steak/src/hub.rs +++ b/packages/steak/src/hub.rs @@ -146,6 +146,10 @@ pub enum ExecuteMsg { }, /// Return the Dust in shiny 'base denom' ReturnDenom {}, + /// admin: setBaseDenom - in case you muck up the init and need to change it + SetBaseDenom { + new_denom: String + } } #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, JsonSchema)] diff --git a/packages/steak/src/hub_tf.rs b/packages/steak/src/hub_tf.rs index 1622b4c..f0bfab5 100644 --- a/packages/steak/src/hub_tf.rs +++ b/packages/steak/src/hub_tf.rs @@ -155,6 +155,10 @@ pub enum ExecuteMsg { }, /// Return the Dust in shiny 'base denom' ReturnDenom {}, + /// admin: setBaseDenom - in case you muck up the init and need to change it + SetBaseDenom { + new_denom: String + }, /// change tokenfactory type (ADMIN only) ChangeTokenFactory { token_factory_type: String, From a0a66263b9badc97760516207eb61366d6a63653 Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Sun, 5 May 2024 11:31:11 -0500 Subject: [PATCH 64/77] chore: clippy + fmt --- contracts/hub-tf/src/contract.rs | 4 +++- contracts/hub-tf/src/execute.rs | 9 ++------- contracts/hub-tf/src/lib.rs | 2 +- contracts/hub-tf/src/testing/tests.rs | 12 ++++++------ contracts/hub/src/contract.rs | 4 +++- contracts/hub/src/execute.rs | 9 ++------- contracts/hub/src/lib.rs | 2 +- contracts/token/src/lib.rs | 8 ++++---- packages/steak/src/hub.rs | 14 ++++++++------ packages/steak/src/hub_tf.rs | 12 +++++++----- 10 files changed, 37 insertions(+), 39 deletions(-) diff --git a/contracts/hub-tf/src/contract.rs b/contracts/hub-tf/src/contract.rs index 853398c..96c3f13 100644 --- a/contracts/hub-tf/src/contract.rs +++ b/contracts/hub-tf/src/contract.rs @@ -125,7 +125,9 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S ExecuteMsg::ReturnDenom {} => { execute::bond(deps, env, info.sender, info.funds, None, false) }, - ExecuteMsg::SetBaseDenom {new_denom} => execute::set_base_denom(deps,info.sender,new_denom), + ExecuteMsg::SetBaseDenom { + new_denom, + } => execute::set_base_denom(deps, info.sender, new_denom), ExecuteMsg::ChangeTokenFactory { token_factory_type, } => execute::change_token_factory(deps, info.sender, &token_factory_type), diff --git a/contracts/hub-tf/src/execute.rs b/contracts/hub-tf/src/execute.rs index 580bb1a..66394f6 100644 --- a/contracts/hub-tf/src/execute.rs +++ b/contracts/hub-tf/src/execute.rs @@ -1185,18 +1185,13 @@ pub fn redelegate( .add_event(event) .add_attribute("action", "steakhub/redelegate")) } -pub fn set_base_denom( - deps: DepsMut, - sender: Addr, - new_denom: String, -) -> StdResult { +pub fn set_base_denom(deps: DepsMut, sender: Addr, new_denom: String) -> StdResult { let state = State::default(); state.assert_owner(deps.storage, &sender)?; state.denom.save(deps.storage, &new_denom)?; - let event = Event::new("steak/set_base_denom") - .add_attribute("base_denom", new_denom); + let event = Event::new("steak/set_base_denom").add_attribute("base_denom", new_denom); Ok(Response::new().add_event(event).add_attribute("action", "steakhub/set_base_denom")) } diff --git a/contracts/hub-tf/src/lib.rs b/contracts/hub-tf/src/lib.rs index 77aa148..74e5fc4 100644 --- a/contracts/hub-tf/src/lib.rs +++ b/contracts/hub-tf/src/lib.rs @@ -1,4 +1,4 @@ -#[cfg(not(feature = "library"))] +//#[cfg(not(feature = "library"))] pub mod contract; pub mod execute; diff --git a/contracts/hub-tf/src/testing/tests.rs b/contracts/hub-tf/src/testing/tests.rs index b7f0be6..748eaf7 100644 --- a/contracts/hub-tf/src/testing/tests.rs +++ b/contracts/hub-tf/src/testing/tests.rs @@ -686,7 +686,7 @@ fn submitting_batch() { unbond_requests() .save( deps.as_mut().storage, - (unbond_request.id, &Addr::unchecked(unbond_request.user.clone()).as_str()), + (unbond_request.id, Addr::unchecked(unbond_request.user.clone()).as_str()), unbond_request, ) .unwrap(); @@ -948,7 +948,7 @@ fn withdrawing_unbonded() { unbond_requests() .save( deps.as_mut().storage, - (unbond_request.id, &Addr::unchecked(unbond_request.user.clone()).as_str()), + (unbond_request.id, Addr::unchecked(unbond_request.user.clone()).as_str()), unbond_request, ) .unwrap(); @@ -1075,10 +1075,10 @@ fn withdrawing_unbonded() { // User 1's unbond requests in batches 1 and 2 should have been deleted let err1 = unbond_requests() - .load(deps.as_ref().storage, (1u64, &Addr::unchecked("user_1").as_str())) + .load(deps.as_ref().storage, (1u64, Addr::unchecked("user_1").as_str())) .unwrap_err(); let err2 = unbond_requests() - .load(deps.as_ref().storage, (1u64, &Addr::unchecked("user_1").as_str())) + .load(deps.as_ref().storage, (1u64, Addr::unchecked("user_1").as_str())) .unwrap_err(); match err1 { @@ -1136,7 +1136,7 @@ fn withdrawing_unbonded() { }; let err = unbond_requests() - .load(deps.as_ref().storage, (1u64, &Addr::unchecked("user_3").as_str())) + .load(deps.as_ref().storage, (1u64, Addr::unchecked("user_3").as_str())) .unwrap_err(); match err { @@ -1554,7 +1554,7 @@ fn querying_unbond_requests() { unbond_requests() .save( deps.as_mut().storage, - (unbond_request.id, &Addr::unchecked(unbond_request.user.clone()).as_str()), + (unbond_request.id, Addr::unchecked(unbond_request.user.clone()).as_str()), unbond_request, ) .unwrap(); diff --git a/contracts/hub/src/contract.rs b/contracts/hub/src/contract.rs index 99b58e3..dae22e5 100644 --- a/contracts/hub/src/contract.rs +++ b/contracts/hub/src/contract.rs @@ -128,7 +128,9 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S } }, ExecuteMsg::ReturnDenom {} => execute::return_denom(deps, env, info.funds), - ExecuteMsg::SetBaseDenom {new_denom} => execute::set_base_denom(deps,info.sender, new_denom) + ExecuteMsg::SetBaseDenom { + new_denom, + } => execute::set_base_denom(deps, info.sender, new_denom), } } diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index f9010b5..e15c125 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -1033,18 +1033,13 @@ pub fn collect_dust(deps: DepsMut, _env: Env, _max_tokens: usize) -> StdResult StdResult { +pub fn set_base_denom(deps: DepsMut, sender: Addr, new_denom: String) -> StdResult { let state = State::default(); state.assert_owner(deps.storage, &sender)?; state.denom.save(deps.storage, &new_denom)?; - let event = Event::new("steak/set_base_denom") - .add_attribute("base_denom", new_denom); + let event = Event::new("steak/set_base_denom").add_attribute("base_denom", new_denom); Ok(Response::new().add_event(event).add_attribute("action", "steakhub/set_base_denom")) } diff --git a/contracts/hub/src/lib.rs b/contracts/hub/src/lib.rs index c05dd01..0cb2fe7 100644 --- a/contracts/hub/src/lib.rs +++ b/contracts/hub/src/lib.rs @@ -1,4 +1,4 @@ -#[cfg(not(feature = "library"))] +//#[cfg(not(feature = "library"))] pub mod contract; pub mod execute; diff --git a/contracts/token/src/lib.rs b/contracts/token/src/lib.rs index 9fb5b36..e882986 100644 --- a/contracts/token/src/lib.rs +++ b/contracts/token/src/lib.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{ - entry_point, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdError, StdResult, + Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdError, StdResult, Storage, }; use cw20_base::{ @@ -9,7 +9,7 @@ use cw20_base::{ ContractError, }; -#[cfg_attr(not(feature = "library"), entry_point)] +//#[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( deps: DepsMut, env: Env, @@ -19,7 +19,7 @@ pub fn instantiate( cw20_instantiate(deps, env, info, msg) } -#[cfg_attr(not(feature = "library"), entry_point)] +//#[cfg_attr(not(feature = "library"), entry_point)] pub fn execute( deps: DepsMut, env: Env, @@ -57,7 +57,7 @@ fn assert_minter(storage: &dyn Storage, sender: &Addr) -> Result<(), ContractErr Ok(()) } -#[cfg_attr(not(feature = "library"), entry_point)] +//#[cfg_attr(not(feature = "library"), entry_point)] pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { cw20_query(deps, env, msg) } diff --git a/packages/steak/src/hub.rs b/packages/steak/src/hub.rs index 4dcce0d..693bd5f 100644 --- a/packages/steak/src/hub.rs +++ b/packages/steak/src/hub.rs @@ -1,3 +1,4 @@ +use std::fmt::Display; use std::str::FromStr; use cosmwasm_std::{ @@ -148,8 +149,8 @@ pub enum ExecuteMsg { ReturnDenom {}, /// admin: setBaseDenom - in case you muck up the init and need to change it SetBaseDenom { - new_denom: String - } + new_denom: String, + }, } #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, JsonSchema)] @@ -342,11 +343,12 @@ impl FromStr for FeeType { } } -impl ToString for FeeType { - fn to_string(&self) -> String { - match &self { +impl Display for FeeType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let str = match &self { FeeType::Wallet => String::from("Wallet"), FeeType::FeeSplit => String::from("FeeSplit"), - } + }; + write!(f, "{}", str) } } diff --git a/packages/steak/src/hub_tf.rs b/packages/steak/src/hub_tf.rs index f0bfab5..81636e2 100644 --- a/packages/steak/src/hub_tf.rs +++ b/packages/steak/src/hub_tf.rs @@ -1,3 +1,4 @@ +use std::fmt::Display; use std::str::FromStr; use cosmwasm_schema::cw_serde; @@ -13,14 +14,15 @@ pub enum TokenFactoryType { Injective = 3, Osmosis = 4, } -impl ToString for TokenFactoryType { - fn to_string(&self) -> String { - match &self { +impl Display for TokenFactoryType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let str = match &self { TokenFactoryType::CosmWasm => String::from("CosmWasm"), TokenFactoryType::Kujira => String::from("Kujira"), TokenFactoryType::Injective => String::from("Injective"), TokenFactoryType::Osmosis => String::from("Osmosis"), - } + }; + write!(f, "{}", str) } } impl FromStr for TokenFactoryType { @@ -157,7 +159,7 @@ pub enum ExecuteMsg { ReturnDenom {}, /// admin: setBaseDenom - in case you muck up the init and need to change it SetBaseDenom { - new_denom: String + new_denom: String, }, /// change tokenfactory type (ADMIN only) ChangeTokenFactory { From da6a11c333d4caf4e86d77aa31cd9005d8a0029c Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Sun, 5 May 2024 11:32:33 -0500 Subject: [PATCH 65/77] chore: fmt --- contracts/token/src/lib.rs | 3 +-- packages/steak/src/hub.rs | 3 +-- packages/steak/src/hub_tf.rs | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/contracts/token/src/lib.rs b/contracts/token/src/lib.rs index e882986..d50b0bd 100644 --- a/contracts/token/src/lib.rs +++ b/contracts/token/src/lib.rs @@ -1,6 +1,5 @@ use cosmwasm_std::{ - Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdError, StdResult, - Storage, + Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdError, StdResult, Storage, }; use cw20_base::{ contract::{execute as cw20_execute, instantiate as cw20_instantiate, query as cw20_query}, diff --git a/packages/steak/src/hub.rs b/packages/steak/src/hub.rs index 693bd5f..438d70d 100644 --- a/packages/steak/src/hub.rs +++ b/packages/steak/src/hub.rs @@ -1,5 +1,4 @@ -use std::fmt::Display; -use std::str::FromStr; +use std::{fmt::Display, str::FromStr}; use cosmwasm_std::{ to_json_binary, Addr, Binary, Coin, CosmosMsg, Decimal, Empty, StdResult, Uint128, WasmMsg, diff --git a/packages/steak/src/hub_tf.rs b/packages/steak/src/hub_tf.rs index 81636e2..fee4d35 100644 --- a/packages/steak/src/hub_tf.rs +++ b/packages/steak/src/hub_tf.rs @@ -1,5 +1,4 @@ -use std::fmt::Display; -use std::str::FromStr; +use std::{fmt::Display, str::FromStr}; use cosmwasm_schema::cw_serde; // From 99ba5ee91515ba5d5003c461e4b106f4020cda74 Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Wed, 8 May 2024 19:20:26 -0500 Subject: [PATCH 66/77] fix: don't try to burn 0 TF tokens --- contracts/hub-tf/Cargo.toml | 2 +- contracts/hub-tf/src/execute.rs | 87 +++++++++++++++++---------------- 2 files changed, 46 insertions(+), 43 deletions(-) diff --git a/contracts/hub-tf/Cargo.toml b/contracts/hub-tf/Cargo.toml index 33d18e9..186301f 100644 --- a/contracts/hub-tf/Cargo.toml +++ b/contracts/hub-tf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub-tf" -version = "3.0.16" +version = "3.0.17" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub-tf/src/execute.rs b/contracts/hub-tf/src/execute.rs index 66394f6..2c1553b 100644 --- a/contracts/hub-tf/src/execute.rs +++ b/contracts/hub-tf/src/execute.rs @@ -653,48 +653,51 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { }) .collect::>(); - let burn_msg = match token_factory_type { - TokenFactoryType::Kujira => { - >::into(kujira::denom::MsgBurn { - sender: env.contract.address.to_string(), - amount: Some(kujira::denom::Coin { - denom: steak_denom, - amount: pending_batch.usteak_to_burn.to_string(), - }), - }) - }, - TokenFactoryType::CosmWasm => >::into( - token_factory::denom::MsgBurn { - sender: env.contract.address.to_string(), - amount: Some(token_factory::denom::Coin { - denom: steak_denom, - amount: pending_batch.usteak_to_burn.to_string(), - }), + let mut msgs = vec![]; + if !pending_batch.usteak_to_burn.is_zero() { + let burn_msg = match token_factory_type { + TokenFactoryType::Kujira => { + >::into(kujira::denom::MsgBurn { + sender: env.contract.address.to_string(), + amount: Some(kujira::denom::Coin { + denom: steak_denom, + amount: pending_batch.usteak_to_burn.to_string(), + }), + }) }, - ), - TokenFactoryType::Injective => { - >::into(injective::denom::MsgBurn { - sender: env.contract.address.to_string(), - amount: Some(injective::denom::Coin { - denom: steak_denom, - amount: pending_batch.usteak_to_burn.to_string(), - }), - }) - }, - TokenFactoryType::Osmosis => { - >::into(osmosis::denom::MsgBurn { - sender: env.contract.address.to_string(), - amount: Some(osmosis::denom::Coin { - denom: steak_denom, - amount: pending_batch.usteak_to_burn.to_string(), - }), - burn_from_address: env.contract.address.to_string(), - }) - }, - }; - // yes.. this will fail if supply is less than the amount to burn. this is intentional. - state.steak_minted.save(deps.storage, &(usteak_supply - pending_batch.usteak_to_burn))?; - + TokenFactoryType::CosmWasm => >::into( + token_factory::denom::MsgBurn { + sender: env.contract.address.to_string(), + amount: Some(token_factory::denom::Coin { + denom: steak_denom, + amount: pending_batch.usteak_to_burn.to_string(), + }), + }, + ), + TokenFactoryType::Injective => { + >::into(injective::denom::MsgBurn { + sender: env.contract.address.to_string(), + amount: Some(injective::denom::Coin { + denom: steak_denom, + amount: pending_batch.usteak_to_burn.to_string(), + }), + }) + }, + TokenFactoryType::Osmosis => { + >::into(osmosis::denom::MsgBurn { + sender: env.contract.address.to_string(), + amount: Some(osmosis::denom::Coin { + denom: steak_denom, + amount: pending_batch.usteak_to_burn.to_string(), + }), + burn_from_address: env.contract.address.to_string(), + }) + }, + }; + // yes.. this will fail if supply is less than the amount to burn. this is intentional. + state.steak_minted.save(deps.storage, &(usteak_supply - pending_batch.usteak_to_burn))?; + msgs.push(burn_msg); + } let event = Event::new("steakhub/unbond_submitted") .add_attribute("time", env.block.time.seconds().to_string()) .add_attribute("height", env.block.height.to_string()) @@ -704,7 +707,7 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { Ok(Response::new() .add_submessages(undelegate_submsgs) - .add_message(burn_msg) + .add_messages(msgs) .add_event(event) .add_attribute("action", "steakhub/unbond")) } From 1b71a5202a75f36efcdbd3a48e302eb73f4cdaee Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Wed, 8 May 2024 19:26:33 -0500 Subject: [PATCH 67/77] fix: don't try to burn 0 TF tokens. update lock --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 25719b8..045cf0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -700,7 +700,7 @@ dependencies = [ [[package]] name = "pfc-steak-hub-tf" -version = "3.0.16" +version = "3.0.17" dependencies = [ "cosmwasm-std", "cw-item-set", From 84eba8e7cc07e8af11f54072c529a4e5108e5fb7 Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Wed, 8 May 2024 19:52:47 -0500 Subject: [PATCH 68/77] feat: add changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4997e6f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,4 @@ +# changelog + +## 3.0.17 +* started changelog \ No newline at end of file From 3333f289f483941422615b7203f79dc7605e08c9 Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Sat, 18 May 2024 14:36:13 +1000 Subject: [PATCH 69/77] chore: add inj init --- init/injective_hub-tf_init-mainnet.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 init/injective_hub-tf_init-mainnet.json diff --git a/init/injective_hub-tf_init-mainnet.json b/init/injective_hub-tf_init-mainnet.json new file mode 100644 index 0000000..719b67c --- /dev/null +++ b/init/injective_hub-tf_init-mainnet.json @@ -0,0 +1,16 @@ +{ + "owner": "inj1d3yya2s4cejxfe5et9djq38qgtvlsmn9nqjeu9", + "epoch_period": 259200, + "unbond_period": 1814400, + "validators": [ + "injvaloper15vlkdnu2c0k0gaclgycnyjm7c5f3hsde034f5p", + "injvaloper1p66ez0ywzssuq26f7056mx3ydgvkfqw8lf93e8" + ], + "denom": "inj", + "steak_denom": "bINJ", + "fee_account": "inj1rckxty603r8feyzzrgzdyp0y055k8htr90qqtc", + "fee_amount": "0.10", + "max_fee_amount": "0.10", + "fee_account_type": "FeeSplit", + "token_factory": "Injective" +} \ No newline at end of file From 959a1a4378f1abf21bb14007ca8a448c1b350932 Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Mon, 15 Jul 2024 11:29:34 -0500 Subject: [PATCH 70/77] fix: UnbondRequestsIndexes.user index had wrong PK defined. h/t @0xPhillip --- contracts/hub-tf/Cargo.toml | 14 ++++---- contracts/hub-tf/src/state.rs | 6 ++-- contracts/hub-tf/src/testing/tests.rs | 48 ++++++++++++++++++++++++++- 3 files changed, 57 insertions(+), 11 deletions(-) diff --git a/contracts/hub-tf/Cargo.toml b/contracts/hub-tf/Cargo.toml index 186301f..22764cc 100644 --- a/contracts/hub-tf/Cargo.toml +++ b/contracts/hub-tf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub-tf" -version = "3.0.17" +version = "3.0.18" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" @@ -14,22 +14,22 @@ crate-type = ["cdylib", "rlib"] [dependencies] #cosmwasm-std = { version = "1.2.1", features=["iterator","stargate","cosmwasm_1_1","staking"]} -cosmwasm-std = { version = "1.5.4", features=["iterator","stargate","staking"]} +cosmwasm-std = { version = "1.5.4", features = ["iterator", "stargate", "staking"] } -cw2= "1.1.2" +cw2 = "1.1.2" #cw20 = "1.0.0" #cw20-base = { version = "1.0.0", features = ["library"] } cw-storage-plus = "1.2.0" cw-ownable = "0.5.0" cw-item-set = "0.7.0" -prost = {version = "0.12.3", default-features = false, features = ["prost-derive"]} -prost-types = {version = "0.12.3", default-features = false} +prost = { version = "0.12.3", default-features = false, features = ["prost-derive"] } +prost-types = { version = "0.12.3", default-features = false } schemars = "0.8.11" pfc-steak = { path = "../../packages/steak" } serde = { version = "1.0.103", default-features = false, features = ["derive"] } pfc-fee-split = "1.5.0" -pfc-dust-collector="0.1.0" +pfc-dust-collector = "0.1.0" #{ path="../../packages/pfc-dust-collector" } -osmosis-std-derive="0.15.3" +osmosis-std-derive = "0.15.3" [dev-dependencies] protobuf = { version = "3.1.0", features = ["with-bytes"] } \ No newline at end of file diff --git a/contracts/hub-tf/src/state.rs b/contracts/hub-tf/src/state.rs index 5fe6bb4..7abbea1 100644 --- a/contracts/hub-tf/src/state.rs +++ b/contracts/hub-tf/src/state.rs @@ -132,8 +132,8 @@ pub fn unbond_requests_user_idx(_pk: &[u8], d: &UnbondRequest) -> String { d.user.to_string() } -pub fn unbond_requests<'a>() --> IndexedMap<'a, (u64, &'a str), UnbondRequest, UnbondRequestsIndexes<'a>> { +pub fn unbond_requests<'a>( +) -> IndexedMap<'a, (u64, &'a str), UnbondRequest, UnbondRequestsIndexes<'a>> { IndexedMap::new( UNBOND_KEY_V101, UnbondRequestsIndexes { @@ -156,7 +156,7 @@ impl<'a> IndexList for PreviousBatchesIndexes<'a> { pub struct UnbondRequestsIndexes<'a> { // pk goes to second tuple element - pub user: MultiIndex<'a, String, UnbondRequest, String>, + pub user: MultiIndex<'a, String, UnbondRequest, (u64, Addr)>, } impl<'a> IndexList for UnbondRequestsIndexes<'a> { diff --git a/contracts/hub-tf/src/testing/tests.rs b/contracts/hub-tf/src/testing/tests.rs index 748eaf7..4ebc198 100644 --- a/contracts/hub-tf/src/testing/tests.rs +++ b/contracts/hub-tf/src/testing/tests.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use cosmwasm_std::{ testing::{mock_env, mock_info, MockApi, MockStorage, MOCK_CONTRACT_ADDR}, to_json_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, DistributionMsg, Order, OwnedDeps, - ReplyOn, StdError, SubMsg, Uint128, WasmMsg, + ReplyOn, StdError, StdResult, SubMsg, Uint128, WasmMsg, }; use pfc_steak::{ hub::{ @@ -2054,3 +2054,49 @@ fn dust_return_denom() { } ); } +#[test] +fn test_128() { + let mut deps = mock_dependencies(); + + let user_addr = deps.api.addr_make("testing"); + + crate::state::unbond_requests() + .save( + deps.as_mut().storage, + (128u64, user_addr.as_str()), + &UnbondRequest { + id: 128u64, + user: user_addr.clone(), + shares: Uint128::new(18084001808), + }, + ) + .unwrap(); + + let mut_deps = deps.as_mut(); + + let elements = unbond_requests() + .range(mut_deps.storage, None, None, Order::Ascending) + .take(10) + .map(|item| { + let (_, v) = item?; + Ok(v.into()) + }) + .collect::>>() + .unwrap(); + assert_eq!(elements.len(), 1); + let elements = unbond_requests() + .idx + .user + .prefix(user_addr.to_string()) + // .prefix(contract.to_string()) + .range(mut_deps.storage, None, None, Order::Ascending) + .take(100) + .map(|item| { + let (_, v) = item?; + Ok(v.into()) + }) + .collect::>>() + .unwrap(); + + assert_eq!(elements.len(), 1); +} From abb548797a52db79d23c288b73e7497cadcd8214 Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Mon, 15 Jul 2024 11:31:19 -0500 Subject: [PATCH 71/77] fix: UnbondRequestsIndexes.user index had wrong PK defined. h/t @0xPhillip - clippy --- contracts/hub-tf/src/testing/tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/hub-tf/src/testing/tests.rs b/contracts/hub-tf/src/testing/tests.rs index 4ebc198..4f5b81f 100644 --- a/contracts/hub-tf/src/testing/tests.rs +++ b/contracts/hub-tf/src/testing/tests.rs @@ -2079,7 +2079,7 @@ fn test_128() { .take(10) .map(|item| { let (_, v) = item?; - Ok(v.into()) + Ok(v) }) .collect::>>() .unwrap(); @@ -2093,7 +2093,7 @@ fn test_128() { .take(100) .map(|item| { let (_, v) = item?; - Ok(v.into()) + Ok(v) }) .collect::>>() .unwrap(); From f40186f39ecf14bbea78792ac47c69137a5f5b02 Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Mon, 15 Jul 2024 11:34:21 -0500 Subject: [PATCH 72/77] fix: UnbondRequestsIndexes.user index had wrong PK defined. h/t @0xPhillip - fmt --- contracts/hub-tf/src/state.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/hub-tf/src/state.rs b/contracts/hub-tf/src/state.rs index 7abbea1..70b95f0 100644 --- a/contracts/hub-tf/src/state.rs +++ b/contracts/hub-tf/src/state.rs @@ -132,8 +132,8 @@ pub fn unbond_requests_user_idx(_pk: &[u8], d: &UnbondRequest) -> String { d.user.to_string() } -pub fn unbond_requests<'a>( -) -> IndexedMap<'a, (u64, &'a str), UnbondRequest, UnbondRequestsIndexes<'a>> { +pub fn unbond_requests<'a>() +-> IndexedMap<'a, (u64, &'a str), UnbondRequest, UnbondRequestsIndexes<'a>> { IndexedMap::new( UNBOND_KEY_V101, UnbondRequestsIndexes { From 4cd9f355b541529ed00547e95c761b693f1d9250 Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Wed, 7 Aug 2024 10:31:40 -0500 Subject: [PATCH 73/77] fix: previous_batches.reconciled index had wrong PK defined. feat: new query: unreconciled --- Cargo.lock | 48 +++++++++++++++++++++++++++----- Cargo.toml | 4 ++- contracts/hub-tf/Cargo.toml | 2 +- contracts/hub-tf/src/contract.rs | 1 + contracts/hub-tf/src/execute.rs | 2 +- contracts/hub-tf/src/queries.rs | 12 ++++++++ contracts/hub-tf/src/state.rs | 2 +- contracts/hub/src/contract.rs | 1 + contracts/hub/src/queries.rs | 15 ++++++++++ justfile | 6 ++-- packages/steak/src/hub.rs | 2 ++ 11 files changed, 81 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 045cf0f..8f1ddc0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,9 +82,9 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "cosmwasm-crypto" -version = "1.5.4" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b4c3f9c4616d6413d4b5fc4c270a4cc32a374b9be08671e80e1a019f805d8f" +checksum = "dd50718a2b6830ce9eb5d465de5a018a12e71729d66b70807ce97e6dd14f931d" dependencies = [ "digest 0.10.7", "ecdsa", @@ -96,9 +96,9 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.5.4" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c586ced10c3b00e809ee664a895025a024f60d65d34fe4c09daed4a4db68a3f3" +checksum = "242e98e7a231c122e08f300d9db3262d1007b51758a8732cd6210b3e9faa4f3a" dependencies = [ "syn 1.0.109", ] @@ -129,9 +129,9 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.5.4" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712fe58f39d55c812f7b2c84e097cdede3a39d520f89b6dc3153837e31741927" +checksum = "78c1556156fdf892a55cced6115968b961eaaadd6f724a2c2cb7d1e168e32dd3" dependencies = [ "base64", "bech32", @@ -683,6 +683,19 @@ dependencies = [ "serde", ] +[[package]] +name = "pfc-steak-dao" +version = "3.0.18" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw20 1.1.2", + "cw20-base 1.1.2", + "pfc-steak", + "schemars", + "serde", +] + [[package]] name = "pfc-steak-hub" version = "3.0.16" @@ -698,9 +711,30 @@ dependencies = [ "serde", ] +[[package]] +name = "pfc-steak-hub-dao" +version = "3.0.18" +dependencies = [ + "cosmwasm-std", + "cw-item-set", + "cw-ownable", + "cw-storage-plus 1.2.0", + "cw2 1.1.2", + "osmosis-std-derive", + "pfc-dust-collector", + "pfc-fee-split 1.5.0", + "pfc-steak", + "pfc-steak-dao", + "prost", + "prost-types", + "protobuf", + "schemars", + "serde", +] + [[package]] name = "pfc-steak-hub-tf" -version = "3.0.17" +version = "3.0.19" dependencies = [ "cosmwasm-std", "cw-item-set", diff --git a/Cargo.toml b/Cargo.toml index 1f043b0..a54486e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,7 @@ [workspace] -members = ["contracts/token", "contracts/hub", "contracts/hub-tf", "packages/*"] +members = ["contracts/token", "contracts/hub", "contracts/hub-tf", + "contracts/hub-dao", + "packages/*"] resolver = '1' [profile.release.package.pfc-steak] diff --git a/contracts/hub-tf/Cargo.toml b/contracts/hub-tf/Cargo.toml index 22764cc..ca5049c 100644 --- a/contracts/hub-tf/Cargo.toml +++ b/contracts/hub-tf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub-tf" -version = "3.0.18" +version = "3.0.19" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/contracts/hub-tf/src/contract.rs b/contracts/hub-tf/src/contract.rs index 96c3f13..18aff93 100644 --- a/contracts/hub-tf/src/contract.rs +++ b/contracts/hub-tf/src/contract.rs @@ -181,6 +181,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { start_after, limit, } => to_json_binary(&queries::unbond_requests_by_user(deps, user, start_after, limit)?), + QueryMsg::Unreconciled {} => to_json_binary(&queries::previous_batches_unreconciled(deps)?), } } diff --git a/contracts/hub-tf/src/execute.rs b/contracts/hub-tf/src/execute.rs index 2c1553b..9bd6e90 100644 --- a/contracts/hub-tf/src/execute.rs +++ b/contracts/hub-tf/src/execute.rs @@ -720,7 +720,7 @@ pub fn reconcile(deps: DepsMut, env: Env) -> StdResult { let all_batches = previous_batches() .idx .reconciled - .prefix("false".into()) + .prefix("false".to_string()) .range(deps.storage, None, None, Order::Ascending) .map(|item| { let (_, v) = item?; diff --git a/contracts/hub-tf/src/queries.rs b/contracts/hub-tf/src/queries.rs index 55b7212..4705912 100644 --- a/contracts/hub-tf/src/queries.rs +++ b/contracts/hub-tf/src/queries.rs @@ -113,6 +113,18 @@ pub fn previous_batches( }) .collect() } +pub fn previous_batches_unreconciled(deps: Deps) -> StdResult> { + state::previous_batches() + .idx + .reconciled + .prefix("false".into()) + .range(deps.storage, None, None, Order::Ascending) + .map(|item| { + let (_, v) = item?; + Ok(v) + }) + .collect() +} pub fn unbond_requests_by_batch( deps: Deps, diff --git a/contracts/hub-tf/src/state.rs b/contracts/hub-tf/src/state.rs index 70b95f0..714f88b 100644 --- a/contracts/hub-tf/src/state.rs +++ b/contracts/hub-tf/src/state.rs @@ -144,7 +144,7 @@ pub fn unbond_requests<'a>() pub struct PreviousBatchesIndexes<'a> { // pk goes to second tuple element - pub reconciled: MultiIndex<'a, String, Batch, String>, + pub reconciled: MultiIndex<'a, String, Batch, u64>, } impl<'a> IndexList for PreviousBatchesIndexes<'a> { diff --git a/contracts/hub/src/contract.rs b/contracts/hub/src/contract.rs index dae22e5..895f098 100644 --- a/contracts/hub/src/contract.rs +++ b/contracts/hub/src/contract.rs @@ -212,6 +212,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { start_after, limit, } => to_json_binary(&queries::unbond_requests_by_user(deps, user, start_after, limit)?), + QueryMsg::Unreconciled {} => to_json_binary(&queries::previous_batches_unreconciled(deps)?), } } diff --git a/contracts/hub/src/queries.rs b/contracts/hub/src/queries.rs index d308d76..14e8d9f 100644 --- a/contracts/hub/src/queries.rs +++ b/contracts/hub/src/queries.rs @@ -13,6 +13,7 @@ use pfc_steak::hub::{ use crate::{ helpers::{query_cw20_total_supply, query_delegations}, state::State, + types::BooleanKey, }; const MAX_LIMIT: u32 = 30; @@ -110,7 +111,21 @@ pub fn previous_batches( }) .collect() } +pub fn previous_batches_unreconciled(deps: Deps) -> StdResult> { + let state = State::default(); + state + .previous_batches + .idx + .reconciled + .prefix(BooleanKey::new(false)) + .range(deps.storage, None, None, Order::Ascending) + .map(|item| { + let (_, v) = item?; + Ok(v) + }) + .collect() +} pub fn unbond_requests_by_batch( deps: Deps, id: u64, diff --git a/justfile b/justfile index 6753226..16aa04d 100644 --- a/justfile +++ b/justfile @@ -2,7 +2,7 @@ check: cargo check --target wasm32-unknown-unknown --lib clippy: - cargo clippy --tests + cargo +nightly clippy --tests format: cargo +nightly fmt @@ -20,11 +20,11 @@ optimize-arm: --mount type=volume,source="$(basename "$(pwd)")_cache",target=/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ --platform linux/arm64 \ - cosmwasm/workspace-optimizer-arm64:0.15.1 + cosmwasm/optimizer-arm64:0.16.0 optimize-x86: docker run --rm -v "$(pwd)":/code \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ --platform linux/amd64 \ - cosmwasm/workspace-optimizer:0.15.1 + cosmwasm/optimizer:0.16.0 diff --git a/packages/steak/src/hub.rs b/packages/steak/src/hub.rs index 438d70d..9201669 100644 --- a/packages/steak/src/hub.rs +++ b/packages/steak/src/hub.rs @@ -188,6 +188,8 @@ pub enum QueryMsg { State {}, /// The current batch on unbonding requests pending submission. Response: `PendingBatch` PendingBatch {}, + /// list of batches with reconciled == false + Unreconciled {}, /// Query an individual batch that has previously been submitted for unbonding but have not yet /// fully withdrawn. Response: `Batch` PreviousBatch(u64), From b005765d90e90f1471087227a82a8ea1ed518bda Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Wed, 7 Aug 2024 10:33:43 -0500 Subject: [PATCH 74/77] fix: clippy warnings about docs --- contracts/hub-tf/src/execute.rs | 7 +++---- contracts/hub/src/execute.rs | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/contracts/hub-tf/src/execute.rs b/contracts/hub-tf/src/execute.rs index 9bd6e90..1eb1f9b 100644 --- a/contracts/hub-tf/src/execute.rs +++ b/contracts/hub-tf/src/execute.rs @@ -353,11 +353,10 @@ pub fn harvest(deps: DepsMut, env: Env) -> StdResult { /// NOTE: /// 1. When delegation Native denom here, we don't need to use a `SubMsg` to handle the received -/// coins, -/// because we have already withdrawn all claimable staking rewards previously in the same atomic -/// execution. +/// coins, because we have already withdrawn all claimable staking rewards previously in the same +/// atomic execution. /// 2. Same as with `bond`, in the latest implementation we only delegate staking rewards with the -/// validator that has the smallest delegation amount. +/// validator that has the smallest delegation amount. pub fn reinvest(deps: DepsMut, env: Env) -> StdResult { let state = State::default(); let denom = state.denom.load(deps.storage)?; diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index e15c125..969d7a1 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -307,11 +307,10 @@ pub fn harvest(deps: DepsMut, env: Env) -> StdResult { /// NOTE: /// 1. When delegation Native denom here, we don't need to use a `SubMsg` to handle the received -/// coins, -/// because we have already withdrawn all claimable staking rewards previously in the same atomic -/// execution. +/// coins, because we have already withdrawn all claimable staking rewards previously in the same +/// atomic execution. /// 2. Same as with `bond`, in the latest implementation we only delegate staking rewards with the -/// validator that has the smallest delegation amount. +/// validator that has the smallest delegation amount. pub fn reinvest(deps: DepsMut, env: Env) -> StdResult { let state = State::default(); let denom = state.denom.load(deps.storage)?; From be754e0a9dd128b0edb5920311b1585f4201f085 Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Wed, 7 Aug 2024 10:36:03 -0500 Subject: [PATCH 75/77] fix: fix workspace members --- Cargo.lock | 21 --------------------- Cargo.toml | 1 - 2 files changed, 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8f1ddc0..b8b37bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -711,27 +711,6 @@ dependencies = [ "serde", ] -[[package]] -name = "pfc-steak-hub-dao" -version = "3.0.18" -dependencies = [ - "cosmwasm-std", - "cw-item-set", - "cw-ownable", - "cw-storage-plus 1.2.0", - "cw2 1.1.2", - "osmosis-std-derive", - "pfc-dust-collector", - "pfc-fee-split 1.5.0", - "pfc-steak", - "pfc-steak-dao", - "prost", - "prost-types", - "protobuf", - "schemars", - "serde", -] - [[package]] name = "pfc-steak-hub-tf" version = "3.0.19" diff --git a/Cargo.toml b/Cargo.toml index a54486e..30c7359 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,5 @@ [workspace] members = ["contracts/token", "contracts/hub", "contracts/hub-tf", - "contracts/hub-dao", "packages/*"] resolver = '1' From 05444ea1211a165990c8934cf8dd442554a676e0 Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Mon, 14 Oct 2024 14:09:38 -0500 Subject: [PATCH 76/77] chore: versions, clippy, new format. remove backtrace feature --- Cargo.lock | 82 +- Cargo.toml | 2 +- contracts/hub-tf/Cargo.toml | 15 +- contracts/hub-tf/src/contract.rs | 6 +- contracts/hub-tf/src/execute.rs | 56 +- contracts/hub-tf/src/injective/denom.rs | 2 + contracts/hub-tf/src/kujira/denom.rs | 2 + contracts/hub-tf/src/osmosis/denom.rs | 2 + contracts/hub-tf/src/state.rs | 35 +- .../hub-tf/src/testing/custom_querier.rs | 5 +- contracts/hub-tf/src/testing/helpers.rs | 5 +- contracts/hub-tf/src/testing/tests.rs | 1190 +++++++---------- contracts/hub-tf/src/token_factory/denom.rs | 2 + contracts/hub-tf/src/types/keys.rs | 4 +- contracts/hub/Cargo.toml | 18 +- contracts/hub/src/contract.rs | 6 +- contracts/hub/src/execute.rs | 56 +- contracts/hub/src/migrations.rs | 4 +- contracts/hub/src/state.rs | 6 +- contracts/hub/src/testing/custom_querier.rs | 5 +- contracts/hub/src/testing/cw20_querier.rs | 2 +- contracts/hub/src/testing/helpers.rs | 5 +- contracts/hub/src/testing/tests.rs | 1128 +++++++--------- contracts/hub/src/types/keys.rs | 4 +- contracts/token/src/lib.rs | 60 +- packages/steak/Cargo.toml | 10 +- packages/steak/src/hub.rs | 2 +- rustfmt.toml | 12 +- 28 files changed, 1120 insertions(+), 1606 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b8b37bd..d0b890c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,12 +82,11 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "cosmwasm-crypto" -version = "1.5.5" +version = "1.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd50718a2b6830ce9eb5d465de5a018a12e71729d66b70807ce97e6dd14f931d" +checksum = "58535cbcd599b3c193e3967c8292fe1dbbb5de7c2a2d87380661091dd4744044" dependencies = [ "digest 0.10.7", - "ecdsa", "ed25519-zebra", "k256", "rand_core 0.6.4", @@ -96,18 +95,18 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.5.5" +version = "1.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "242e98e7a231c122e08f300d9db3262d1007b51758a8732cd6210b3e9faa4f3a" +checksum = "a8e07de16c800ac82fd188d055ecdb923ead0cf33960d3350089260bb982c09f" dependencies = [ "syn 1.0.109", ] [[package]] name = "cosmwasm-schema" -version = "1.5.4" +version = "1.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8467874827d384c131955ff6f4d47d02e72a956a08eb3c0ff24f8c903a5517b4" +checksum = "93d388adfa9cb449557a92e9318121ac1a481fc4f599213b03a5b62699b403b4" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -118,9 +117,9 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.5.4" +version = "1.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6db85d98ac80922aef465e564d5b21fa9cfac5058cb62df7f116c3682337393" +checksum = "2411b389e56e6484f81ba955b758d02522d620c98fc960c4bd2251d48b7aa19f" dependencies = [ "proc-macro2", "quote", @@ -129,9 +128,9 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.5.5" +version = "1.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c1556156fdf892a55cced6115968b961eaaadd6f724a2c2cb7d1e168e32dd3" +checksum = "c21fde95ccd20044a23c0ac6fd8c941f3e8c158169dc94b5aa6491a2d9551a8d" dependencies = [ "base64", "bech32", @@ -493,7 +492,7 @@ checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" [[package]] name = "funds-distributor-api" version = "1.0.0" -source = "git+https://github.com/terra-money/enterprise-contracts.git?branch=main#847a3eece581f1e3be23c0cf43e2f9dc8c2b94f4" +source = "git+https://github.com/terra-money/enterprise-contracts.git?branch=main#60f2fb438d283dc8dd28b94f54f2a49ffd6b8678" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -577,9 +576,9 @@ checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" [[package]] name = "k256" -version = "0.13.1" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ "cfg-if", "ecdsa", @@ -597,9 +596,9 @@ checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" [[package]] name = "once_cell" -version = "1.17.1" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "opaque-debug" @@ -673,7 +672,7 @@ dependencies = [ [[package]] name = "pfc-steak" -version = "3.0.16" +version = "3.0.20" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -683,22 +682,9 @@ dependencies = [ "serde", ] -[[package]] -name = "pfc-steak-dao" -version = "3.0.18" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw20 1.1.2", - "cw20-base 1.1.2", - "pfc-steak", - "schemars", - "serde", -] - [[package]] name = "pfc-steak-hub" -version = "3.0.16" +version = "3.0.20" dependencies = [ "cosmwasm-std", "cw-storage-plus 0.13.4", @@ -713,7 +699,7 @@ dependencies = [ [[package]] name = "pfc-steak-hub-tf" -version = "3.0.19" +version = "3.0.20" dependencies = [ "cosmwasm-std", "cw-item-set", @@ -819,9 +805,9 @@ dependencies = [ [[package]] name = "protobuf" -version = "3.2.0" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55bad9126f378a853655831eb7363b7b01b81d19f8cb1218861086ca4a1a61e" +checksum = "3018844a02746180074f621e847703737d27d89d7f0721a7a4da317f88b16385" dependencies = [ "bytes", "once_cell", @@ -831,9 +817,9 @@ dependencies = [ [[package]] name = "protobuf-support" -version = "3.2.0" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d4d7b8601c814cfb36bcebb79f0e61e45e1e93640cf778837833bbed05c372" +checksum = "faf96d872914fcda2b66d66ea3fff2be7c66865d31c7bb2790cff32c0e714880" dependencies = [ "thiserror", ] @@ -883,9 +869,9 @@ checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" [[package]] name = "schemars" -version = "0.8.16" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" +checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" dependencies = [ "dyn-clone", "schemars_derive", @@ -895,14 +881,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.16" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" +checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 1.0.109", + "syn 2.0.55", ] [[package]] @@ -927,9 +913,9 @@ checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" [[package]] name = "serde" -version = "1.0.197" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] @@ -945,9 +931,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -956,13 +942,13 @@ dependencies = [ [[package]] name = "serde_derive_internals" -version = "0.26.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.55", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 30c7359..fda51ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] members = ["contracts/token", "contracts/hub", "contracts/hub-tf", - "packages/*"] + "packages/steak"] resolver = '1' [profile.release.package.pfc-steak] diff --git a/contracts/hub-tf/Cargo.toml b/contracts/hub-tf/Cargo.toml index ca5049c..ff977d2 100644 --- a/contracts/hub-tf/Cargo.toml +++ b/contracts/hub-tf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub-tf" -version = "3.0.19" +version = "3.0.20" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" @@ -10,26 +10,25 @@ repository = "https://github.com/pfc-developer/steak-contracts" crate-type = ["cdylib", "rlib"] [features] -#backtraces = ["cosmwasm-std/backtraces"] [dependencies] #cosmwasm-std = { version = "1.2.1", features=["iterator","stargate","cosmwasm_1_1","staking"]} -cosmwasm-std = { version = "1.5.4", features = ["iterator", "stargate", "staking"] } +cosmwasm-std = { version = "1.5.8", features = ["iterator", "stargate", "staking"] } cw2 = "1.1.2" #cw20 = "1.0.0" #cw20-base = { version = "1.0.0", features = ["library"] } cw-storage-plus = "1.2.0" -cw-ownable = "0.5.0" -cw-item-set = "0.7.0" +cw-ownable = "0.5.1" +cw-item-set = "0.7.1" prost = { version = "0.12.3", default-features = false, features = ["prost-derive"] } prost-types = { version = "0.12.3", default-features = false } -schemars = "0.8.11" +schemars = "0.8.21" pfc-steak = { path = "../../packages/steak" } -serde = { version = "1.0.103", default-features = false, features = ["derive"] } +serde = { version = "1.0.210", default-features = false, features = ["derive"] } pfc-fee-split = "1.5.0" pfc-dust-collector = "0.1.0" #{ path="../../packages/pfc-dust-collector" } osmosis-std-derive = "0.15.3" [dev-dependencies] -protobuf = { version = "3.1.0", features = ["with-bytes"] } \ No newline at end of file +protobuf = { version = "3.6.0", features = ["with-bytes"] } \ No newline at end of file diff --git a/contracts/hub-tf/src/contract.rs b/contracts/hub-tf/src/contract.rs index 18aff93..b29f743 100644 --- a/contracts/hub-tf/src/contract.rs +++ b/contracts/hub-tf/src/contract.rs @@ -1,10 +1,10 @@ use std::convert::TryInto; use cosmwasm_std::{ - entry_point, to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, - StdError, StdResult, + Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdError, StdResult, entry_point, + to_json_binary, }; -use cw2::{get_contract_version, set_contract_version, ContractVersion}; +use cw2::{ContractVersion, get_contract_version, set_contract_version}; use pfc_steak::{ hub::{CallbackMsg, MigrateMsg, QueryMsg}, hub_tf::{ExecuteMsg, InstantiateMsg, TokenFactoryType}, diff --git a/contracts/hub-tf/src/execute.rs b/contracts/hub-tf/src/execute.rs index 1eb1f9b..9bd8a4c 100644 --- a/contracts/hub-tf/src/execute.rs +++ b/contracts/hub-tf/src/execute.rs @@ -1,13 +1,13 @@ use std::{collections::HashSet, iter::FromIterator, str::FromStr}; use cosmwasm_std::{ - to_json_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, DepsMut, DistributionMsg, Env, Event, - Order, ReplyOn, Response, StdError, StdResult, SubMsg, Uint128, WasmMsg, + Addr, BankMsg, Coin, CosmosMsg, Decimal, DepsMut, DistributionMsg, Env, Event, Order, ReplyOn, + Response, StdError, StdResult, SubMsg, Uint128, WasmMsg, to_json_binary, }; use pfc_steak::{ + DecimalCheckedOps, hub::{Batch, CallbackMsg, FeeType, PendingBatch, UnbondRequest}, hub_tf::{ExecuteMsg, InstantiateMsg, TokenFactoryType}, - DecimalCheckedOps, }; //use crate::token_factory::denom::{MsgBurn, MsgCreateDenom, MsgMint}; @@ -25,7 +25,7 @@ use crate::{ reconcile_batches, }, osmosis, - state::{previous_batches, unbond_requests, State, VALIDATORS, VALIDATORS_ACTIVE}, + state::{State, VALIDATORS, VALIDATORS_ACTIVE, previous_batches, unbond_requests}, token_factory, }; @@ -58,14 +58,11 @@ pub fn instantiate(deps: DepsMut, env: Env, msg: InstantiateMsg) -> StdResult StdResult StdResult { // // I don't have a solution for this... other than to manually fund contract with the slashed // amount. - previous_batches().save( - deps.storage, - pending_batch.id, - &Batch { - id: pending_batch.id, - reconciled: false, - total_shares: pending_batch.usteak_to_burn, - amount_unclaimed: amount_to_unbond, - est_unbond_end_time: current_time + unbond_period, - }, - )?; + previous_batches().save(deps.storage, pending_batch.id, &Batch { + id: pending_batch.id, + reconciled: false, + total_shares: pending_batch.usteak_to_burn, + amount_unclaimed: amount_to_unbond, + est_unbond_end_time: current_time + unbond_period, + })?; let epoch_period = state.epoch_period.load(deps.storage)?; - state.pending_batch.save( - deps.storage, - &PendingBatch { - id: pending_batch.id + 1, - usteak_to_burn: Uint128::zero(), - est_unbond_start_time: current_time + epoch_period, - }, - )?; + state.pending_batch.save(deps.storage, &PendingBatch { + id: pending_batch.id + 1, + usteak_to_burn: Uint128::zero(), + est_unbond_start_time: current_time + epoch_period, + })?; state.prev_denom.save( deps.storage, &get_denom_balance(&deps.querier, env.contract.address.clone(), denom)?, diff --git a/contracts/hub-tf/src/injective/denom.rs b/contracts/hub-tf/src/injective/denom.rs index 07ab8f3..ea71356 100644 --- a/contracts/hub-tf/src/injective/denom.rs +++ b/contracts/hub-tf/src/injective/denom.rs @@ -28,6 +28,8 @@ pub struct Coin { pub amount: ::prost::alloc::string::String, } +/// Create a Denom +/// /// MsgCreateDenom defines the message structure for the CreateDenom gRPC service /// method. It allows an account to create a new denom. It requires a sender /// address and a sub denomination. The (sender_address, sub_denomination) tuple diff --git a/contracts/hub-tf/src/kujira/denom.rs b/contracts/hub-tf/src/kujira/denom.rs index cda290d..1ffddeb 100644 --- a/contracts/hub-tf/src/kujira/denom.rs +++ b/contracts/hub-tf/src/kujira/denom.rs @@ -28,6 +28,8 @@ pub struct Coin { pub amount: ::prost::alloc::string::String, } +/// Create a Denom +/// /// MsgCreateDenom defines the message structure for the CreateDenom gRPC service /// method. It allows an account to create a new denom. It requires a sender /// address and a sub denomination. The (sender_address, sub_denomination) tuple diff --git a/contracts/hub-tf/src/osmosis/denom.rs b/contracts/hub-tf/src/osmosis/denom.rs index 304d3d8..e7a78a5 100644 --- a/contracts/hub-tf/src/osmosis/denom.rs +++ b/contracts/hub-tf/src/osmosis/denom.rs @@ -28,6 +28,8 @@ pub struct Coin { pub amount: ::prost::alloc::string::String, } +/// Create a Denom +/// /// MsgCreateDenom defines the message structure for the CreateDenom gRPC service /// method. It allows an account to create a new denom. It requires a sender /// address and a sub denomination. The (sender_address, sub_denomination) tuple diff --git a/contracts/hub-tf/src/state.rs b/contracts/hub-tf/src/state.rs index 714f88b..258008d 100644 --- a/contracts/hub-tf/src/state.rs +++ b/contracts/hub-tf/src/state.rs @@ -42,9 +42,8 @@ pub(crate) struct State<'a> { pub epoch_period: Item<'a, u64>, /// The staking module's unbonding time, in seconds pub unbond_period: Item<'a, u64>, - /// Validators who will receive the delegations + // Validators who will receive the delegations // pub validators: Item<'a, Vec>, - /// Coins that can be reinvested pub unlocked_coins: Item<'a, Vec>, /// The current batch of un-bonding requests queued to be executed @@ -96,7 +95,7 @@ impl Default for State<'static> { } } -impl<'a> State<'a> { +impl State<'_> { pub fn assert_owner(&self, storage: &dyn Storage, sender: &Addr) -> StdResult<()> { let owner = self.owner.load(storage)?; if *sender == owner { @@ -116,16 +115,13 @@ pub fn previous_batches_reconciled_idx(_pk: &[u8], d: &Batch) -> String { } pub fn previous_batches<'a>() -> IndexedMap<'a, u64, Batch, PreviousBatchesIndexes<'a>> { - IndexedMap::new( - BATCH_KEY_V101, - PreviousBatchesIndexes { - reconciled: MultiIndex::new( - previous_batches_reconciled_idx, - BATCH_KEY_V101, - BATCH_KEY_OWNER_V101, - ), - }, - ) + IndexedMap::new(BATCH_KEY_V101, PreviousBatchesIndexes { + reconciled: MultiIndex::new( + previous_batches_reconciled_idx, + BATCH_KEY_V101, + BATCH_KEY_OWNER_V101, + ), + }) } pub fn unbond_requests_user_idx(_pk: &[u8], d: &UnbondRequest) -> String { @@ -134,12 +130,9 @@ pub fn unbond_requests_user_idx(_pk: &[u8], d: &UnbondRequest) -> String { pub fn unbond_requests<'a>() -> IndexedMap<'a, (u64, &'a str), UnbondRequest, UnbondRequestsIndexes<'a>> { - IndexedMap::new( - UNBOND_KEY_V101, - UnbondRequestsIndexes { - user: MultiIndex::new(unbond_requests_user_idx, UNBOND_KEY_V101, UNBOND_KEY_USER_V101), - }, - ) + IndexedMap::new(UNBOND_KEY_V101, UnbondRequestsIndexes { + user: MultiIndex::new(unbond_requests_user_idx, UNBOND_KEY_V101, UNBOND_KEY_USER_V101), + }) } pub struct PreviousBatchesIndexes<'a> { @@ -147,7 +140,7 @@ pub struct PreviousBatchesIndexes<'a> { pub reconciled: MultiIndex<'a, String, Batch, u64>, } -impl<'a> IndexList for PreviousBatchesIndexes<'a> { +impl IndexList for PreviousBatchesIndexes<'_> { fn get_indexes(&'_ self) -> Box> + '_> { let v: Vec<&dyn Index> = vec![&self.reconciled]; Box::new(v.into_iter()) @@ -159,7 +152,7 @@ pub struct UnbondRequestsIndexes<'a> { pub user: MultiIndex<'a, String, UnbondRequest, (u64, Addr)>, } -impl<'a> IndexList for UnbondRequestsIndexes<'a> { +impl IndexList for UnbondRequestsIndexes<'_> { fn get_indexes(&'_ self) -> Box> + '_> { let v: Vec<&dyn Index> = vec![&self.user]; Box::new(v.into_iter()) diff --git a/contracts/hub-tf/src/testing/custom_querier.rs b/contracts/hub-tf/src/testing/custom_querier.rs index fbd5fdd..5929ac1 100644 --- a/contracts/hub-tf/src/testing/custom_querier.rs +++ b/contracts/hub-tf/src/testing/custom_querier.rs @@ -1,8 +1,7 @@ use cosmwasm_std::{ - from_json, - testing::{BankQuerier, StakingQuerier, MOCK_CONTRACT_ADDR}, Addr, Coin, Empty, FullDelegation, Querier, QuerierResult, QueryRequest, SystemError, - WasmQuery, + WasmQuery, from_json, + testing::{BankQuerier, MOCK_CONTRACT_ADDR, StakingQuerier}, }; use super::helpers::err_unsupported_query; diff --git a/contracts/hub-tf/src/testing/helpers.rs b/contracts/hub-tf/src/testing/helpers.rs index 636a264..60d1c2b 100644 --- a/contracts/hub-tf/src/testing/helpers.rs +++ b/contracts/hub-tf/src/testing/helpers.rs @@ -1,8 +1,7 @@ use cosmwasm_std::{ - from_json, - testing::{mock_env, MockApi, MockStorage, MOCK_CONTRACT_ADDR}, Addr, BlockInfo, ContractInfo, Deps, Env, OwnedDeps, QuerierResult, SystemError, SystemResult, - Timestamp, + Timestamp, from_json, + testing::{MOCK_CONTRACT_ADDR, MockApi, MockStorage, mock_env}, }; use pfc_steak::hub::QueryMsg; use serde::de::DeserializeOwned; diff --git a/contracts/hub-tf/src/testing/tests.rs b/contracts/hub-tf/src/testing/tests.rs index 4f5b81f..c1eb643 100644 --- a/contracts/hub-tf/src/testing/tests.rs +++ b/contracts/hub-tf/src/testing/tests.rs @@ -1,9 +1,10 @@ use std::str::FromStr; use cosmwasm_std::{ - testing::{mock_env, mock_info, MockApi, MockStorage, MOCK_CONTRACT_ADDR}, - to_json_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, DistributionMsg, Order, OwnedDeps, - ReplyOn, StdError, StdResult, SubMsg, Uint128, WasmMsg, + Addr, BankMsg, Coin, CosmosMsg, Decimal, DistributionMsg, Order, OwnedDeps, ReplyOn, StdError, + StdResult, SubMsg, Uint128, WasmMsg, + testing::{MOCK_CONTRACT_ADDR, MockApi, MockStorage, mock_env, mock_info}, + to_json_binary, }; use pfc_steak::{ hub::{ @@ -18,13 +19,13 @@ use super::{ helpers::{mock_dependencies, mock_env_at_timestamp, query_helper}, }; use crate::{ - contract::{execute, instantiate, REPLY_REGISTER_RECEIVED_COINS}, + contract::{REPLY_REGISTER_RECEIVED_COINS, execute, instantiate}, helpers::{parse_coin, parse_received_fund}, math::{ compute_redelegations_for_rebalancing, compute_redelegations_for_removal, compute_undelegations, }, - state::{previous_batches, unbond_requests, State, VALIDATORS}, + state::{State, VALIDATORS, previous_batches, unbond_requests}, token_factory::{ denom, denom::{MsgBurn, MsgCreateDenom, MsgMint}, @@ -115,68 +116,56 @@ fn proper_instantiation() { let deps = setup_test(); let res: ConfigResponse = query_helper(deps.as_ref(), QueryMsg::Config {}); - assert_eq!( - res, - ConfigResponse { - owner: "larry".to_string(), - new_owner: None, - steak_token: "factory/cosmos2contract/boneXYZ".to_string(), - epoch_period: 259200, - unbond_period: 1814400, - denom: "uxyz".to_string(), - fee_type: "Wallet".to_string(), - fee_account: "the_fee_man".to_string(), - fee_rate: Decimal::from_ratio(10_u128, 100_u128), - max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), - validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string(),], - paused_validators: vec![], - dust_collector: Some("dusty_1".to_string()), - token_factory: Some("CosmWasm".to_string()) - } - ); + assert_eq!(res, ConfigResponse { + owner: "larry".to_string(), + new_owner: None, + steak_token: "factory/cosmos2contract/boneXYZ".to_string(), + epoch_period: 259200, + unbond_period: 1814400, + denom: "uxyz".to_string(), + fee_type: "Wallet".to_string(), + fee_account: "the_fee_man".to_string(), + fee_rate: Decimal::from_ratio(10_u128, 100_u128), + max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), + validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string(),], + paused_validators: vec![], + dust_collector: Some("dusty_1".to_string()), + token_factory: Some("CosmWasm".to_string()) + }); let res: StateResponse = query_helper(deps.as_ref(), QueryMsg::State {}); - assert_eq!( - res, - StateResponse { - total_usteak: Uint128::zero(), - total_native: Uint128::zero(), - exchange_rate: Decimal::one(), - unlocked_coins: vec![], - }, - ); + assert_eq!(res, StateResponse { + total_usteak: Uint128::zero(), + total_native: Uint128::zero(), + exchange_rate: Decimal::one(), + unlocked_coins: vec![], + },); let res: PendingBatch = query_helper(deps.as_ref(), QueryMsg::PendingBatch {}); - assert_eq!( - res, - PendingBatch { - id: 1, - usteak_to_burn: Uint128::zero(), - est_unbond_start_time: 269200, // 10,000 + 259,200 - }, - ); + assert_eq!(res, PendingBatch { + id: 1, + usteak_to_burn: Uint128::zero(), + est_unbond_start_time: 269200, // 10,000 + 259,200 + },); let deps_fee_split = setup_test_fee_split(); let res_fee_split: ConfigResponse = query_helper(deps_fee_split.as_ref(), QueryMsg::Config {}); - assert_eq!( - res_fee_split, - ConfigResponse { - owner: "larry".to_string(), - new_owner: None, - steak_token: "factory/cosmos2contract/sXYZ".to_string(), - epoch_period: 259200, - unbond_period: 1814400, - denom: "uxyz".to_string(), - fee_type: "FeeSplit".to_string(), - fee_account: "fee_split_contract".to_string(), - fee_rate: Decimal::from_ratio(10_u128, 100_u128), - max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), - validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string(),], - paused_validators: vec![], - dust_collector: Some("dusty_2".to_string()), - token_factory: Some("CosmWasm".to_string()) - } - ); + assert_eq!(res_fee_split, ConfigResponse { + owner: "larry".to_string(), + new_owner: None, + steak_token: "factory/cosmos2contract/sXYZ".to_string(), + epoch_period: 259200, + unbond_period: 1814400, + denom: "uxyz".to_string(), + fee_type: "FeeSplit".to_string(), + fee_account: "fee_split_contract".to_string(), + fee_rate: Decimal::from_ratio(10_u128, 100_u128), + max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), + validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string(),], + paused_validators: vec![], + dust_collector: Some("dusty_2".to_string()), + token_factory: Some("CosmWasm".to_string()) + }); } #[test] @@ -201,15 +190,12 @@ fn bonding() { // 2 - mint token (to ourselves) // 3 - send/transfer it assert_eq!(res.messages.len(), 3); - assert_eq!( - res.messages[0], - SubMsg { - msg: Delegation::new("alice", 1_000_000, "uxyz").to_cosmos_msg(), - gas_limit: None, - id: REPLY_REGISTER_RECEIVED_COINS, - reply_on: ReplyOn::Never, - } - ); + assert_eq!(res.messages[0], SubMsg { + msg: Delegation::new("alice", 1_000_000, "uxyz").to_cosmos_msg(), + gas_limit: None, + id: REPLY_REGISTER_RECEIVED_COINS, + reply_on: ReplyOn::Never, + }); let mint_msg = >::into(MsgMint { sender: "cosmos2contract".to_string(), amount: Some(denom::Coin { @@ -217,31 +203,25 @@ fn bonding() { amount: Uint128::new(1_000_000).to_string(), }), }); - assert_eq!( - res.messages[1], - SubMsg { - id: 0, - msg: mint_msg, - gas_limit: None, - reply_on: ReplyOn::Never, - } - ); + assert_eq!(res.messages[1], SubMsg { + id: 0, + msg: mint_msg, + gas_limit: None, + reply_on: ReplyOn::Never, + }); - assert_eq!( - res.messages[2], - SubMsg { - id: 0, - msg: CosmosMsg::Bank(BankMsg::Send { - to_address: "user_1".to_string(), - amount: vec![Coin { - denom: "factory/cosmos2contract/boneXYZ".to_string(), - amount: Uint128::new(1_000_000) - }], - }), - gas_limit: None, - reply_on: ReplyOn::Never, - } - ); + assert_eq!(res.messages[2], SubMsg { + id: 0, + msg: CosmosMsg::Bank(BankMsg::Send { + to_address: "user_1".to_string(), + amount: vec![Coin { + denom: "factory/cosmos2contract/boneXYZ".to_string(), + amount: Uint128::new(1_000_000) + }], + }), + gas_limit: None, + reply_on: ReplyOn::Never, + }); // Bond when there are existing delegations, and Luna:Steak exchange rate is >1 // Previously user 1 delegated 1,000,000 uluna. We assume we have accumulated 2.5% yield at @@ -265,15 +245,12 @@ fn bonding() { .unwrap(); assert_eq!(res.messages.len(), 3); - assert_eq!( - res.messages[0], - SubMsg { - msg: Delegation::new("charlie", 12345, "uxyz").to_cosmos_msg(), - id: REPLY_REGISTER_RECEIVED_COINS, - gas_limit: None, - reply_on: ReplyOn::Never - } - ); + assert_eq!(res.messages[0], SubMsg { + msg: Delegation::new("charlie", 12345, "uxyz").to_cosmos_msg(), + id: REPLY_REGISTER_RECEIVED_COINS, + gas_limit: None, + reply_on: ReplyOn::Never + }); let mint_msg = >::into(MsgMint { sender: "cosmos2contract".to_string(), amount: Some(denom::Coin { @@ -281,32 +258,26 @@ fn bonding() { amount: Uint128::new(12_043).to_string(), }), }); - assert_eq!( - res.messages[1], - SubMsg { - id: 0, - msg: mint_msg, - gas_limit: None, - reply_on: ReplyOn::Never, - } - ); + assert_eq!(res.messages[1], SubMsg { + id: 0, + msg: mint_msg, + gas_limit: None, + reply_on: ReplyOn::Never, + }); - assert_eq!( - res.messages[2], - SubMsg { - id: 0, - msg: CosmosMsg::Bank(BankMsg::Send { - to_address: "user_3".to_string(), - - amount: vec![Coin { - denom: "factory/cosmos2contract/boneXYZ".to_string(), - amount: Uint128::new(12_043) - }], - }), - gas_limit: None, - reply_on: ReplyOn::Never, - } - ); + assert_eq!(res.messages[2], SubMsg { + id: 0, + msg: CosmosMsg::Bank(BankMsg::Send { + to_address: "user_3".to_string(), + + amount: vec![Coin { + denom: "factory/cosmos2contract/boneXYZ".to_string(), + amount: Uint128::new(12_043) + }], + }), + gas_limit: None, + reply_on: ReplyOn::Never, + }); // Check the state after bonding deps.querier.set_staking_delegations(&[ @@ -316,15 +287,12 @@ fn bonding() { ]); let res: StateResponse = query_helper(deps.as_ref(), QueryMsg::State {}); - assert_eq!( - res, - StateResponse { - total_usteak: Uint128::new(1012043), - total_native: Uint128::new(1037345), - exchange_rate: Decimal::from_ratio(1037345u128, 1012043u128), - unlocked_coins: vec![], - } - ); + assert_eq!(res, StateResponse { + total_usteak: Uint128::new(1012043), + total_native: Uint128::new(1037345), + exchange_rate: Decimal::from_ratio(1037345u128, 1012043u128), + unlocked_coins: vec![], + }); } #[test] @@ -342,52 +310,40 @@ fn harvesting() { .unwrap(); assert_eq!(res.messages.len(), 4); - assert_eq!( - res.messages[0], - SubMsg { - msg: CosmosMsg::Distribution(DistributionMsg::WithdrawDelegatorReward { - validator: "alice".to_string(), - }), - gas_limit: None, - id: REPLY_REGISTER_RECEIVED_COINS, - reply_on: ReplyOn::Never, - } - ); - assert_eq!( - res.messages[1], - SubMsg { - msg: CosmosMsg::Distribution(DistributionMsg::WithdrawDelegatorReward { - validator: "bob".to_string(), - }), - id: REPLY_REGISTER_RECEIVED_COINS, - gas_limit: None, - reply_on: ReplyOn::Never - } - ); - assert_eq!( - res.messages[2], - SubMsg { - msg: CosmosMsg::Distribution(DistributionMsg::WithdrawDelegatorReward { - validator: "charlie".to_string(), - }), - id: REPLY_REGISTER_RECEIVED_COINS, - gas_limit: None, - reply_on: ReplyOn::Never - } - ); - assert_eq!( - res.messages[3], - SubMsg { - id: 0, - msg: CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: MOCK_CONTRACT_ADDR.to_string(), - msg: to_json_binary(&ExecuteMsg::Callback(CallbackMsg::Reinvest {})).unwrap(), - funds: vec![], - }), - gas_limit: None, - reply_on: ReplyOn::Never, - } - ); + assert_eq!(res.messages[0], SubMsg { + msg: CosmosMsg::Distribution(DistributionMsg::WithdrawDelegatorReward { + validator: "alice".to_string(), + }), + gas_limit: None, + id: REPLY_REGISTER_RECEIVED_COINS, + reply_on: ReplyOn::Never, + }); + assert_eq!(res.messages[1], SubMsg { + msg: CosmosMsg::Distribution(DistributionMsg::WithdrawDelegatorReward { + validator: "bob".to_string(), + }), + id: REPLY_REGISTER_RECEIVED_COINS, + gas_limit: None, + reply_on: ReplyOn::Never + }); + assert_eq!(res.messages[2], SubMsg { + msg: CosmosMsg::Distribution(DistributionMsg::WithdrawDelegatorReward { + validator: "charlie".to_string(), + }), + id: REPLY_REGISTER_RECEIVED_COINS, + gas_limit: None, + reply_on: ReplyOn::Never + }); + assert_eq!(res.messages[3], SubMsg { + id: 0, + msg: CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: MOCK_CONTRACT_ADDR.to_string(), + msg: to_json_binary(&ExecuteMsg::Callback(CallbackMsg::Reinvest {})).unwrap(), + funds: vec![], + }), + gas_limit: None, + reply_on: ReplyOn::Never, + }); } #[test] @@ -406,16 +362,13 @@ fn reinvesting() { // After the swaps, `unlocked_coins` should contain only uxyz and unknown denoms state .unlocked_coins - .save( - deps.as_mut().storage, - &vec![ - Coin::new(234, "uxyz"), - Coin::new( - 69420, - "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", - ), - ], - ) + .save(deps.as_mut().storage, &vec![ + Coin::new(234, "uxyz"), + Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + ), + ]) .unwrap(); // Bob has the smallest amount of delegations, so all proceeds go to him @@ -428,38 +381,29 @@ fn reinvesting() { .unwrap(); assert_eq!(res.messages.len(), 2); - assert_eq!( - res.messages[0], - SubMsg { - id: 0, - msg: Delegation::new("bob", 234 - 23, "uxyz").to_cosmos_msg(), - gas_limit: None, - reply_on: ReplyOn::Never, - } - ); + assert_eq!(res.messages[0], SubMsg { + id: 0, + msg: Delegation::new("bob", 234 - 23, "uxyz").to_cosmos_msg(), + gas_limit: None, + reply_on: ReplyOn::Never, + }); let send_msg = BankMsg::Send { to_address: "the_fee_man".into(), amount: vec![Coin::new(23u128, "uxyz")], }; - assert_eq!( - res.messages[1], - SubMsg { - id: 0, - msg: CosmosMsg::Bank(send_msg), - gas_limit: None, - reply_on: ReplyOn::Never, - } - ); + assert_eq!(res.messages[1], SubMsg { + id: 0, + msg: CosmosMsg::Bank(send_msg), + gas_limit: None, + reply_on: ReplyOn::Never, + }); // Storage should have been updated let unlocked_coins = state.unlocked_coins.load(deps.as_ref().storage).unwrap(); - assert_eq!( - unlocked_coins, - vec![Coin::new( - 69420, - "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", - )], - ); + assert_eq!(unlocked_coins, vec![Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + )],); } #[test] @@ -478,16 +422,13 @@ fn reinvesting_fee_split() { // After the swaps, `unlocked_coins` should contain only uxyz and unknown denoms state .unlocked_coins - .save( - deps.as_mut().storage, - &vec![ - Coin::new(234, "uxyz"), - Coin::new( - 69420, - "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", - ), - ], - ) + .save(deps.as_mut().storage, &vec![ + Coin::new(234, "uxyz"), + Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + ), + ]) .unwrap(); // Bob has the smallest amount of delegations, so all proceeds go to him @@ -500,40 +441,31 @@ fn reinvesting_fee_split() { .unwrap(); assert_eq!(res.messages.len(), 2); - assert_eq!( - res.messages[0], - SubMsg { - id: 0, - msg: Delegation::new("bob", 234 - 23, "uxyz").to_cosmos_msg(), - gas_limit: None, - reply_on: ReplyOn::Never, - } - ); + assert_eq!(res.messages[0], SubMsg { + id: 0, + msg: Delegation::new("bob", 234 - 23, "uxyz").to_cosmos_msg(), + gas_limit: None, + reply_on: ReplyOn::Never, + }); let send_msg = pfc_fee_split::fee_split_msg::ExecuteMsg::Deposit { flush: false, }; - assert_eq!( - res.messages[1], - SubMsg { - id: 0, - msg: send_msg - .into_cosmos_msg("fee_split_contract", vec![Coin::new(23u128, "uxyz")]) - .unwrap(), - gas_limit: None, - reply_on: ReplyOn::Never, - } - ); + assert_eq!(res.messages[1], SubMsg { + id: 0, + msg: send_msg + .into_cosmos_msg("fee_split_contract", vec![Coin::new(23u128, "uxyz")]) + .unwrap(), + gas_limit: None, + reply_on: ReplyOn::Never, + }); // Storage should have been updated let unlocked_coins = state.unlocked_coins.load(deps.as_ref().storage).unwrap(); - assert_eq!( - unlocked_coins, - vec![Coin::new( - 69420, - "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", - )], - ); + assert_eq!(unlocked_coins, vec![Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + )],); } #[test] @@ -545,13 +477,10 @@ fn queuing_unbond() { let err = execute( deps.as_mut(), mock_env(), - mock_info( - "random_token", - &[Coin { - denom: "asldkj".into(), - amount: Uint128::new(69_420), - }], - ), + mock_info("random_token", &[Coin { + denom: "asldkj".into(), + amount: Uint128::new(69_420), + }]), ExecuteMsg::Unbond { receiver: None, }, @@ -568,13 +497,10 @@ fn queuing_unbond() { let res = execute( deps.as_mut(), mock_env_at_timestamp(12345), // est_unbond_start_time = 269200 - mock_info( - "steak_token", - &[Coin { - denom: "factory/cosmos2contract/boneXYZ".into(), - amount: Uint128::new(23_456), - }], - ), + mock_info("steak_token", &[Coin { + denom: "factory/cosmos2contract/boneXYZ".into(), + amount: Uint128::new(23_456), + }]), ExecuteMsg::Unbond { receiver: Some("user_1".to_string()), }, @@ -588,13 +514,10 @@ fn queuing_unbond() { let res = execute( deps.as_mut(), mock_env_at_timestamp(269201), // est_unbond_start_time = 269200 - mock_info( - "user_3", - &[Coin { - denom: "factory/cosmos2contract/boneXYZ".into(), - amount: Uint128::new(69_420), - }], - ), + mock_info("user_3", &[Coin { + denom: "factory/cosmos2contract/boneXYZ".into(), + amount: Uint128::new(69_420), + }]), ExecuteMsg::Unbond { receiver: None, }, @@ -602,19 +525,16 @@ fn queuing_unbond() { .unwrap(); assert_eq!(res.messages.len(), 1); - assert_eq!( - res.messages[0], - SubMsg { - id: 0, - msg: CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: MOCK_CONTRACT_ADDR.to_string(), - msg: to_json_binary(&ExecuteMsg::SubmitBatch {}).unwrap(), - funds: vec![], - }), - gas_limit: None, - reply_on: ReplyOn::Never, - } - ); + assert_eq!(res.messages[0], SubMsg { + id: 0, + msg: CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: MOCK_CONTRACT_ADDR.to_string(), + msg: to_json_binary(&ExecuteMsg::SubmitBatch {}).unwrap(), + funds: vec![], + }), + gas_limit: None, + reply_on: ReplyOn::Never, + }); // The users' unbonding requests should have been saved let ubr1 = unbond_requests() @@ -624,33 +544,24 @@ fn queuing_unbond() { .load(deps.as_ref().storage, (1u64, Addr::unchecked("user_3").as_ref())) .unwrap(); - assert_eq!( - ubr1, - UnbondRequest { - id: 1, - user: Addr::unchecked("user_1"), - shares: Uint128::new(23456), - } - ); - assert_eq!( - ubr2, - UnbondRequest { - id: 1, - user: Addr::unchecked("user_3"), - shares: Uint128::new(69420), - } - ); + assert_eq!(ubr1, UnbondRequest { + id: 1, + user: Addr::unchecked("user_1"), + shares: Uint128::new(23456), + }); + assert_eq!(ubr2, UnbondRequest { + id: 1, + user: Addr::unchecked("user_3"), + shares: Uint128::new(69420), + }); // Pending batch should have been updated let pending_batch = state.pending_batch.load(deps.as_ref().storage).unwrap(); - assert_eq!( - pending_batch, - PendingBatch { - id: 1, - usteak_to_burn: Uint128::new(92876), // 23,456 + 69,420 - est_unbond_start_time: 269200, - } - ); + assert_eq!(pending_batch, PendingBatch { + id: 1, + usteak_to_burn: Uint128::new(92876), // 23,456 + 69,420 + est_unbond_start_time: 269200, + }); } #[test] @@ -694,14 +605,11 @@ fn submitting_batch() { state .pending_batch - .save( - deps.as_mut().storage, - &PendingBatch { - id: 1, - usteak_to_burn: Uint128::new(92876), // 23,456 + 69,420 - est_unbond_start_time: 269200, - }, - ) + .save(deps.as_mut().storage, &PendingBatch { + id: 1, + usteak_to_burn: Uint128::new(92876), // 23,456 + 69,420 + est_unbond_start_time: 269200, + }) .unwrap(); // Anyone can invoke `submit_batch`. Here we continue from the previous test and assume it is @@ -724,33 +632,24 @@ fn submitting_batch() { .unwrap(); assert_eq!(res.messages.len(), 4); - assert_eq!( - res.messages[0], - SubMsg { - msg: Undelegation::new("alice", 31732, "uxyz").to_cosmos_msg(), - id: REPLY_REGISTER_RECEIVED_COINS, - gas_limit: None, - reply_on: ReplyOn::Never - } - ); - assert_eq!( - res.messages[1], - SubMsg { - msg: Undelegation::new("bob", 31733, "uxyz").to_cosmos_msg(), - id: REPLY_REGISTER_RECEIVED_COINS, - gas_limit: None, - reply_on: ReplyOn::Never - } - ); - assert_eq!( - res.messages[2], - SubMsg { - msg: Undelegation::new("charlie", 31732, "uxyz").to_cosmos_msg(), - id: REPLY_REGISTER_RECEIVED_COINS, - gas_limit: None, - reply_on: ReplyOn::Never - } - ); + assert_eq!(res.messages[0], SubMsg { + msg: Undelegation::new("alice", 31732, "uxyz").to_cosmos_msg(), + id: REPLY_REGISTER_RECEIVED_COINS, + gas_limit: None, + reply_on: ReplyOn::Never + }); + assert_eq!(res.messages[1], SubMsg { + msg: Undelegation::new("bob", 31733, "uxyz").to_cosmos_msg(), + id: REPLY_REGISTER_RECEIVED_COINS, + gas_limit: None, + reply_on: ReplyOn::Never + }); + assert_eq!(res.messages[2], SubMsg { + msg: Undelegation::new("charlie", 31732, "uxyz").to_cosmos_msg(), + id: REPLY_REGISTER_RECEIVED_COINS, + gas_limit: None, + reply_on: ReplyOn::Never + }); let burn_msg = >::into(MsgBurn { sender: "cosmos2contract".to_string(), @@ -759,39 +658,30 @@ fn submitting_batch() { amount: Uint128::new(92_876).to_string(), }), }); - assert_eq!( - res.messages[3], - SubMsg { - id: 0, - msg: burn_msg, - gas_limit: None, - reply_on: ReplyOn::Never, - } - ); + assert_eq!(res.messages[3], SubMsg { + id: 0, + msg: burn_msg, + gas_limit: None, + reply_on: ReplyOn::Never, + }); // A new pending batch should have been created let pending_batch = state.pending_batch.load(deps.as_ref().storage).unwrap(); - assert_eq!( - pending_batch, - PendingBatch { - id: 2, - usteak_to_burn: Uint128::zero(), - est_unbond_start_time: 528401, // 269,201 + 259,200 - } - ); + assert_eq!(pending_batch, PendingBatch { + id: 2, + usteak_to_burn: Uint128::zero(), + est_unbond_start_time: 528401, // 269,201 + 259,200 + }); // Previous batch should have been updated let previous_batch = previous_batches().load(deps.as_ref().storage, 1u64).unwrap(); - assert_eq!( - previous_batch, - Batch { - id: 1, - reconciled: false, - total_shares: Uint128::new(92876), - amount_unclaimed: Uint128::new(95197), - est_unbond_end_time: 2083601, // 269,201 + 1,814,400 - } - ); + assert_eq!(previous_batch, Batch { + id: 1, + reconciled: false, + total_shares: Uint128::new(92876), + amount_unclaimed: Uint128::new(95197), + est_unbond_end_time: 2083601, // 269,201 + 1,814,400 + }); } #[test] @@ -836,18 +726,15 @@ fn reconciling() { state .unlocked_coins - .save( - deps.as_mut().storage, - &vec![ - Coin::new(10000, "uxyz"), - Coin::new(234, "ukrw"), - Coin::new(345, "uusd"), - Coin::new( - 69420, - "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", - ), - ], - ) + .save(deps.as_mut().storage, &vec![ + Coin::new(10000, "uxyz"), + Coin::new(234, "ukrw"), + Coin::new(345, "uusd"), + Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + ), + ]) .unwrap(); deps.querier.set_bank_balances(&[ @@ -876,28 +763,22 @@ fn reconciling() { // batch 2: 1385 - 273 = 1112 // batch 3: 1506 - 273 = 1233 let batch = previous_batches().load(deps.as_ref().storage, 2u64).unwrap(); - assert_eq!( - batch, - Batch { - id: 2, - reconciled: true, - total_shares: Uint128::new(1345), - amount_unclaimed: Uint128::new(1112), // 1385 - 273 - est_unbond_end_time: 20000, - } - ); + assert_eq!(batch, Batch { + id: 2, + reconciled: true, + total_shares: Uint128::new(1345), + amount_unclaimed: Uint128::new(1112), // 1385 - 273 + est_unbond_end_time: 20000, + }); let batch = previous_batches().load(deps.as_ref().storage, 3u64).unwrap(); - assert_eq!( - batch, - Batch { - id: 3, - reconciled: true, - total_shares: Uint128::new(1456), - amount_unclaimed: Uint128::new(1233), // 1506 - 273 - est_unbond_end_time: 30000, - } - ); + assert_eq!(batch, Batch { + id: 3, + reconciled: true, + total_shares: Uint128::new(1456), + amount_unclaimed: Uint128::new(1233), // 1506 - 273 + est_unbond_end_time: 30000, + }); // Batches 1 and 4 should not have changed let batch = previous_batches().load(deps.as_ref().storage, 1u64).unwrap(); @@ -992,14 +873,11 @@ fn withdrawing_unbonded() { state .pending_batch - .save( - deps.as_mut().storage, - &PendingBatch { - id: 4, - usteak_to_burn: Uint128::new(56789), - est_unbond_start_time: 100000, - }, - ) + .save(deps.as_mut().storage, &PendingBatch { + id: 4, + usteak_to_burn: Uint128::new(56789), + est_unbond_start_time: 100000, + }) .unwrap(); // Attempt to withdraw before any batch has completed unbonding. Should error @@ -1037,31 +915,25 @@ fn withdrawing_unbonded() { .unwrap(); assert_eq!(res.messages.len(), 1); - assert_eq!( - res.messages[0], - SubMsg { - id: 0, - msg: CosmosMsg::Bank(BankMsg::Send { - to_address: "user_1".to_string(), - amount: vec![Coin::new(59646, "uxyz")], - }), - gas_limit: None, - reply_on: ReplyOn::Never, - } - ); + assert_eq!(res.messages[0], SubMsg { + id: 0, + msg: CosmosMsg::Bank(BankMsg::Send { + to_address: "user_1".to_string(), + amount: vec![Coin::new(59646, "uxyz")], + }), + gas_limit: None, + reply_on: ReplyOn::Never, + }); // Previous batches should have been updated let batch = previous_batches().load(deps.as_ref().storage, 1u64).unwrap(); - assert_eq!( - batch, - Batch { - id: 1, - reconciled: true, - total_shares: Uint128::new(69420), - amount_unclaimed: Uint128::new(71155), - est_unbond_end_time: 10000, - } - ); + assert_eq!(batch, Batch { + id: 1, + reconciled: true, + total_shares: Uint128::new(69420), + amount_unclaimed: Uint128::new(71155), + est_unbond_end_time: 10000, + }); let err = previous_batches().load(deps.as_ref().storage, 2u64).unwrap_err(); match err { @@ -1111,18 +983,15 @@ fn withdrawing_unbonded() { .unwrap(); assert_eq!(res.messages.len(), 1); - assert_eq!( - res.messages[0], - SubMsg { - id: 0, - msg: CosmosMsg::Bank(BankMsg::Send { - to_address: "user_2".to_string(), - amount: vec![Coin::new(71155, "uxyz")], - }), - gas_limit: None, - reply_on: ReplyOn::Never, - } - ); + assert_eq!(res.messages[0], SubMsg { + id: 0, + msg: CosmosMsg::Bank(BankMsg::Send { + to_address: "user_2".to_string(), + amount: vec![Coin::new(71155, "uxyz")], + }), + gas_limit: None, + reply_on: ReplyOn::Never, + }); // Batch 1 and user 2's unbonding request should have been purged from storage let err = previous_batches().load(deps.as_ref().storage, 1u64).unwrap_err(); @@ -1154,39 +1023,27 @@ fn adding_validator() { let mut deps = setup_test(); //let state = State::default(); - let err = execute( - deps.as_mut(), - mock_env(), - mock_info("jake", &[]), - ExecuteMsg::AddValidator { + let err = + execute(deps.as_mut(), mock_env(), mock_info("jake", &[]), ExecuteMsg::AddValidator { validator: "dave".to_string(), - }, - ) - .unwrap_err(); + }) + .unwrap_err(); assert_eq!(err, StdError::generic_err("unauthorized: sender is not owner")); - let err = execute( - deps.as_mut(), - mock_env(), - mock_info("larry", &[]), - ExecuteMsg::AddValidator { + let err = + execute(deps.as_mut(), mock_env(), mock_info("larry", &[]), ExecuteMsg::AddValidator { validator: "alice".to_string(), - }, - ) - .unwrap_err(); + }) + .unwrap_err(); assert_eq!(err, StdError::generic_err("validator is already whitelisted")); - let res = execute( - deps.as_mut(), - mock_env(), - mock_info("larry", &[]), - ExecuteMsg::AddValidator { + let res = + execute(deps.as_mut(), mock_env(), mock_info("larry", &[]), ExecuteMsg::AddValidator { validator: "dave".to_string(), - }, - ) - .unwrap(); + }) + .unwrap(); assert_eq!(res.messages.len(), 0); @@ -1208,27 +1065,19 @@ fn removing_validator() { Delegation::new("charlie", 341666, "uxyz"), ]); - let err = execute( - deps.as_mut(), - mock_env(), - mock_info("jake", &[]), - ExecuteMsg::RemoveValidator { + let err = + execute(deps.as_mut(), mock_env(), mock_info("jake", &[]), ExecuteMsg::RemoveValidator { validator: "charlie".to_string(), - }, - ) - .unwrap_err(); + }) + .unwrap_err(); assert_eq!(err, StdError::generic_err("unauthorized: sender is not owner")); - let err = execute( - deps.as_mut(), - mock_env(), - mock_info("larry", &[]), - ExecuteMsg::RemoveValidator { + let err = + execute(deps.as_mut(), mock_env(), mock_info("larry", &[]), ExecuteMsg::RemoveValidator { validator: "dave".to_string(), - }, - ) - .unwrap_err(); + }) + .unwrap_err(); assert_eq!(err, StdError::generic_err("validator is not already whitelisted")); @@ -1236,35 +1085,25 @@ fn removing_validator() { // Remainder: 0 // Alice: 512500 + 0 - 341667 = 170833 // Bob: 512500 + 0 - 341667 = 170833 - let res = execute( - deps.as_mut(), - mock_env(), - mock_info("larry", &[]), - ExecuteMsg::RemoveValidator { + let res = + execute(deps.as_mut(), mock_env(), mock_info("larry", &[]), ExecuteMsg::RemoveValidator { validator: "charlie".to_string(), - }, - ) - .unwrap(); + }) + .unwrap(); assert_eq!(res.messages.len(), 2); - assert_eq!( - res.messages[0], - SubMsg { - msg: Redelegation::new("charlie", "alice", 170833, "uxyz").to_cosmos_msg(), - id: REPLY_REGISTER_RECEIVED_COINS, - gas_limit: None, - reply_on: ReplyOn::Never - }, - ); - assert_eq!( - res.messages[1], - SubMsg { - msg: Redelegation::new("charlie", "bob", 170833, "uxyz").to_cosmos_msg(), - id: REPLY_REGISTER_RECEIVED_COINS, - gas_limit: None, - reply_on: ReplyOn::Never - }, - ); + assert_eq!(res.messages[0], SubMsg { + msg: Redelegation::new("charlie", "alice", 170833, "uxyz").to_cosmos_msg(), + id: REPLY_REGISTER_RECEIVED_COINS, + gas_limit: None, + reply_on: ReplyOn::Never + },); + assert_eq!(res.messages[1], SubMsg { + msg: Redelegation::new("charlie", "bob", 170833, "uxyz").to_cosmos_msg(), + id: REPLY_REGISTER_RECEIVED_COINS, + gas_limit: None, + reply_on: ReplyOn::Never + },); assert!(VALIDATORS.contains(deps.as_ref().storage, "alice")); assert!(VALIDATORS.contains(deps.as_ref().storage, "bob")); @@ -1276,15 +1115,11 @@ fn transferring_ownership() { let mut deps = setup_test(); let state = State::default(); - let err = execute( - deps.as_mut(), - mock_env(), - mock_info("jake", &[]), - ExecuteMsg::TransferOwnership { + let err = + execute(deps.as_mut(), mock_env(), mock_info("jake", &[]), ExecuteMsg::TransferOwnership { new_owner: "jake".to_string(), - }, - ) - .unwrap_err(); + }) + .unwrap_err(); assert_eq!(err, StdError::generic_err("unauthorized: sender is not owner")); @@ -1353,67 +1188,51 @@ fn splitting_fees() { assert_eq!(err, StdError::generic_err("Invalid Fee type: Wallet or FeeSplit only")); - execute( - deps.as_mut(), - mock_env(), - mock_info("larry", &[]), - ExecuteMsg::TransferFeeAccount { - fee_account_type: "Wallet".to_string(), - new_fee_account: "charlie".to_string(), - }, - ) + execute(deps.as_mut(), mock_env(), mock_info("larry", &[]), ExecuteMsg::TransferFeeAccount { + fee_account_type: "Wallet".to_string(), + new_fee_account: "charlie".to_string(), + }) .unwrap(); let res: ConfigResponse = query_helper(deps.as_ref(), QueryMsg::Config {}); - assert_eq!( - res, - ConfigResponse { - owner: "larry".to_string(), - new_owner: None, - steak_token: "factory/cosmos2contract/boneXYZ".to_string(), - epoch_period: 259200, - unbond_period: 1814400, - denom: "uxyz".to_string(), - fee_type: "Wallet".to_string(), - fee_account: "charlie".to_string(), - fee_rate: Decimal::from_ratio(10_u128, 100_u128), - max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), - validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string(),], - paused_validators: vec![], - dust_collector: Some("dusty_1".to_string()), - token_factory: Some("CosmWasm".to_string()) - } - ); + assert_eq!(res, ConfigResponse { + owner: "larry".to_string(), + new_owner: None, + steak_token: "factory/cosmos2contract/boneXYZ".to_string(), + epoch_period: 259200, + unbond_period: 1814400, + denom: "uxyz".to_string(), + fee_type: "Wallet".to_string(), + fee_account: "charlie".to_string(), + fee_rate: Decimal::from_ratio(10_u128, 100_u128), + max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), + validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string(),], + paused_validators: vec![], + dust_collector: Some("dusty_1".to_string()), + token_factory: Some("CosmWasm".to_string()) + }); - execute( - deps.as_mut(), - mock_env(), - mock_info("larry", &[]), - ExecuteMsg::TransferFeeAccount { - fee_account_type: "FeeSplit".to_string(), - new_fee_account: "contract".to_string(), - }, - ) + execute(deps.as_mut(), mock_env(), mock_info("larry", &[]), ExecuteMsg::TransferFeeAccount { + fee_account_type: "FeeSplit".to_string(), + new_fee_account: "contract".to_string(), + }) .unwrap(); let res: ConfigResponse = query_helper(deps.as_ref(), QueryMsg::Config {}); - assert_eq!( - res, - ConfigResponse { - owner: "larry".to_string(), - new_owner: None, - steak_token: "factory/cosmos2contract/boneXYZ".to_string(), - epoch_period: 259200, - unbond_period: 1814400, - denom: "uxyz".to_string(), - fee_type: "FeeSplit".to_string(), - fee_account: "contract".to_string(), - fee_rate: Decimal::from_ratio(10_u128, 100_u128), - max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), - validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string(),], - paused_validators: vec![], - dust_collector: Some("dusty_1".to_string()), - token_factory: Some("CosmWasm".to_string()) - } - ); + assert_eq!(res, ConfigResponse { + owner: "larry".to_string(), + new_owner: None, + steak_token: "factory/cosmos2contract/boneXYZ".to_string(), + epoch_period: 259200, + unbond_period: 1814400, + denom: "uxyz".to_string(), + fee_type: "FeeSplit".to_string(), + fee_account: "contract".to_string(), + fee_rate: Decimal::from_ratio(10_u128, 100_u128), + max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), + validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string(),], + paused_validators: vec![], + dust_collector: Some("dusty_1".to_string()), + token_factory: Some("CosmWasm".to_string()) + }); } //-------------------------------------------------------------------------------------------------- // Queries @@ -1467,31 +1286,22 @@ fn querying_previous_batches() { assert_eq!(res, batches[1].clone()); // Query multiple batches - let res: Vec = query_helper( - deps.as_ref(), - QueryMsg::PreviousBatches { - start_after: None, - limit: None, - }, - ); + let res: Vec = query_helper(deps.as_ref(), QueryMsg::PreviousBatches { + start_after: None, + limit: None, + }); assert_eq!(res, batches.clone()); - let res: Vec = query_helper( - deps.as_ref(), - QueryMsg::PreviousBatches { - start_after: Some(1), - limit: None, - }, - ); + let res: Vec = query_helper(deps.as_ref(), QueryMsg::PreviousBatches { + start_after: Some(1), + limit: None, + }); assert_eq!(res, vec![batches[1].clone(), batches[2].clone(), batches[3].clone()]); - let res: Vec = query_helper( - deps.as_ref(), - QueryMsg::PreviousBatches { - start_after: Some(4), - limit: None, - }, - ); + let res: Vec = query_helper(deps.as_ref(), QueryMsg::PreviousBatches { + start_after: Some(4), + limit: None, + }); assert_eq!(res, vec![]); // Query multiple batches, indexed by whether it has been reconciled @@ -1560,41 +1370,32 @@ fn querying_unbond_requests() { .unwrap(); } - let res: Vec = query_helper( - deps.as_ref(), - QueryMsg::UnbondRequestsByBatch { + let res: Vec = + query_helper(deps.as_ref(), QueryMsg::UnbondRequestsByBatch { id: 1, start_after: None, limit: None, - }, - ); - assert_eq!( - res, - vec![ - unbond_reqs[0].clone().into(), - unbond_reqs[1].clone().into(), - unbond_reqs[2].clone().into(), - ] - ); + }); + assert_eq!(res, vec![ + unbond_reqs[0].clone().into(), + unbond_reqs[1].clone().into(), + unbond_reqs[2].clone().into(), + ]); - let res: Vec = query_helper( - deps.as_ref(), - QueryMsg::UnbondRequestsByBatch { + let res: Vec = + query_helper(deps.as_ref(), QueryMsg::UnbondRequestsByBatch { id: 2, start_after: None, limit: None, - }, - ); + }); assert_eq!(res, vec![unbond_reqs[3].clone().into()]); - let res: Vec = query_helper( - deps.as_ref(), - QueryMsg::UnbondRequestsByUser { + let res: Vec = + query_helper(deps.as_ref(), QueryMsg::UnbondRequestsByUser { user: "alice".to_string(), start_after: None, limit: None, - }, - ); + }); assert_eq!(res, vec![unbond_reqs[0].clone().into(), unbond_reqs[3].clone().into(),]); /* for x in unbond_requests().range(deps.as_ref().storage, None, None, Order::Ascending) { @@ -1602,14 +1403,12 @@ fn querying_unbond_requests() { eprintln!("Key {}/{} = {:?}", rec.0.0, rec.0.1, rec.1) } */ - let res: Vec = query_helper( - deps.as_ref(), - QueryMsg::UnbondRequestsByUser { + let res: Vec = + query_helper(deps.as_ref(), QueryMsg::UnbondRequestsByUser { user: "alice".to_string(), start_after: Some(1), limit: None, - }, - ); + }); assert_eq!(res, vec![unbond_reqs[3].clone().into()]); } @@ -1803,10 +1602,11 @@ fn adding_coins() { assert_eq!(coins.0, vec![Coin::new(12345, "uatom"), Coin::new(23456, "uxyz")]); coins.add_many(&Coins::from_str("76543uatom,69420uusd").unwrap()).unwrap(); - assert_eq!( - coins.0, - vec![Coin::new(88888, "uatom"), Coin::new(23456, "uxyz"), Coin::new(69420, "uusd"),] - ); + assert_eq!(coins.0, vec![ + Coin::new(88888, "uatom"), + Coin::new(23456, "uxyz"), + Coin::new(69420, "uusd"), + ]); } #[test] @@ -1867,18 +1667,15 @@ fn reconciling_underflow() { } state .unlocked_coins - .save( - deps.as_mut().storage, - &vec![ - Coin::new(10000, "uatom"), - Coin::new(234, "ukrw"), - Coin::new(345, "uusd"), - Coin::new( - 69420, - "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", - ), - ], - ) + .save(deps.as_mut().storage, &vec![ + Coin::new(10000, "uatom"), + Coin::new(234, "ukrw"), + Coin::new(345, "uusd"), + Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + ), + ]) .unwrap(); deps.querier.set_bank_balances(&[ Coin::new(12345, "uatom"), @@ -1934,18 +1731,15 @@ fn reconciling_underflow_second() { } state .unlocked_coins - .save( - deps.as_mut().storage, - &vec![ - Coin::new(10000, "uatom"), - Coin::new(234, "ukrw"), - Coin::new(345, "uusd"), - Coin::new( - 69420, - "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", - ), - ], - ) + .save(deps.as_mut().storage, &vec![ + Coin::new(10000, "uatom"), + Coin::new(234, "ukrw"), + Coin::new(345, "uusd"), + Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + ), + ]) .unwrap(); deps.querier.set_bank_balances(&[ Coin::new(12345 - 1323, "uatom"), @@ -1983,15 +1777,12 @@ fn dust_return_denom() { // 2 - mint token (to ourselves) // 3 - send/transfer it assert_eq!(res.messages.len(), 3); - assert_eq!( - res.messages[0], - SubMsg { - msg: Delegation::new("alice", 1_000_000, "uxyz").to_cosmos_msg(), - id: REPLY_REGISTER_RECEIVED_COINS, - gas_limit: None, - reply_on: ReplyOn::Never - } - ); + assert_eq!(res.messages[0], SubMsg { + msg: Delegation::new("alice", 1_000_000, "uxyz").to_cosmos_msg(), + id: REPLY_REGISTER_RECEIVED_COINS, + gas_limit: None, + reply_on: ReplyOn::Never + }); let mint_msg = >::into(MsgMint { sender: "cosmos2contract".to_string(), amount: Some(denom::Coin { @@ -1999,31 +1790,25 @@ fn dust_return_denom() { amount: Uint128::new(1_000_000).to_string(), }), }); - assert_eq!( - res.messages[1], - SubMsg { - id: 0, - msg: mint_msg, - gas_limit: None, - reply_on: ReplyOn::Never, - } - ); + assert_eq!(res.messages[1], SubMsg { + id: 0, + msg: mint_msg, + gas_limit: None, + reply_on: ReplyOn::Never, + }); - assert_eq!( - res.messages[2], - SubMsg { - id: 0, - msg: CosmosMsg::Bank(BankMsg::Send { - to_address: "user_1".to_string(), - amount: vec![Coin { - denom: "factory/cosmos2contract/boneXYZ".to_string(), - amount: Uint128::new(1_000_000) - }], - }), - gas_limit: None, - reply_on: ReplyOn::Never, - } - ); + assert_eq!(res.messages[2], SubMsg { + id: 0, + msg: CosmosMsg::Bank(BankMsg::Send { + to_address: "user_1".to_string(), + amount: vec![Coin { + denom: "factory/cosmos2contract/boneXYZ".to_string(), + amount: Uint128::new(1_000_000) + }], + }), + gas_limit: None, + reply_on: ReplyOn::Never, + }); // Bond when there are existing delegations, and Luna:Steak exchange rate is >1 // Previously user 1 delegated 1,000,000 uluna. We assume we have accumulated 2.5% yield at @@ -2044,15 +1829,12 @@ fn dust_return_denom() { .unwrap(); eprintln!("{:?}", res.messages); assert_eq!(res.messages.len(), 1); - assert_eq!( - res.messages[0], - SubMsg { - msg: Delegation::new("charlie", 12345, "uxyz").to_cosmos_msg(), - id: REPLY_REGISTER_RECEIVED_COINS, - gas_limit: None, - reply_on: ReplyOn::Never - } - ); + assert_eq!(res.messages[0], SubMsg { + msg: Delegation::new("charlie", 12345, "uxyz").to_cosmos_msg(), + id: REPLY_REGISTER_RECEIVED_COINS, + gas_limit: None, + reply_on: ReplyOn::Never + }); } #[test] fn test_128() { @@ -2061,15 +1843,11 @@ fn test_128() { let user_addr = deps.api.addr_make("testing"); crate::state::unbond_requests() - .save( - deps.as_mut().storage, - (128u64, user_addr.as_str()), - &UnbondRequest { - id: 128u64, - user: user_addr.clone(), - shares: Uint128::new(18084001808), - }, - ) + .save(deps.as_mut().storage, (128u64, user_addr.as_str()), &UnbondRequest { + id: 128u64, + user: user_addr.clone(), + shares: Uint128::new(18084001808), + }) .unwrap(); let mut_deps = deps.as_mut(); diff --git a/contracts/hub-tf/src/token_factory/denom.rs b/contracts/hub-tf/src/token_factory/denom.rs index a7804d5..bcb58c4 100644 --- a/contracts/hub-tf/src/token_factory/denom.rs +++ b/contracts/hub-tf/src/token_factory/denom.rs @@ -28,6 +28,8 @@ pub struct Coin { pub amount: ::prost::alloc::string::String, } +/// Create a Denom +/// /// MsgCreateDenom defines the message structure for the CreateDenom gRPC service /// method. It allows an account to create a new denom. It requires a sender /// address and a sub denomination. The (sender_address, sub_denomination) tuple diff --git a/contracts/hub-tf/src/types/keys.rs b/contracts/hub-tf/src/types/keys.rs index 5c2af96..25dab9a 100644 --- a/contracts/hub-tf/src/types/keys.rs +++ b/contracts/hub-tf/src/types/keys.rs @@ -27,7 +27,7 @@ impl From for BooleanKey { } } -impl<'a> PrimaryKey<'a> for BooleanKey { +impl PrimaryKey<'_> for BooleanKey { type Prefix = (); type SubPrefix = (); type Suffix = (); @@ -38,7 +38,7 @@ impl<'a> PrimaryKey<'a> for BooleanKey { } } -impl<'a> Prefixer<'a> for BooleanKey { +impl Prefixer<'_> for BooleanKey { fn prefix(&self) -> Vec { self.wrapped.prefix() } diff --git a/contracts/hub/Cargo.toml b/contracts/hub/Cargo.toml index 3a938cd..58ee3eb 100644 --- a/contracts/hub/Cargo.toml +++ b/contracts/hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-hub" -version = "3.0.16" +version = "3.0.20" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" @@ -10,18 +10,18 @@ repository = "https://github.com/st4k3h0us3/steak-contracts" crate-type = ["cdylib", "rlib"] [features] -backtraces = ["cosmwasm-std/backtraces"] +# backtraces = ["cosmwasm-std/backtraces"] [dependencies] -cosmwasm-std = { version = "1.5.4", features = ["staking"] } -cw2= "1.0.0" -cw20 = "1.0.1" -cw20-base = { version = "1.0.0", features = ["library"] } +cosmwasm-std = { version = "1.5.8", features = ["staking"] } +cw2 = "1.1.2" +cw20 = "1.1.2" +cw20-base = { version = "1.1.2", features = ["library"] } cw-storage-plus = "0.13" pfc-steak = { path = "../../packages/steak" } -serde = { version = "1.0.103", default-features = false, features = ["derive"] } -pfc-fee-split = "0.2.3" +serde = { version = "1.0.210", default-features = false, features = ["derive"] } +pfc-fee-split = "0.2.5" #pfc-dust-collector = {path = "../../packages/pfc-dust-collector"} -funds-distributor-api = {git="https://github.com/terra-money/enterprise-contracts.git" , branch="main"} +funds-distributor-api = { git = "https://github.com/terra-money/enterprise-contracts.git", branch = "main" } #[dev-dependencies] #serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/contracts/hub/src/contract.rs b/contracts/hub/src/contract.rs index 895f098..bbf0597 100644 --- a/contracts/hub/src/contract.rs +++ b/contracts/hub/src/contract.rs @@ -1,10 +1,10 @@ use std::convert::TryInto; use cosmwasm_std::{ - entry_point, from_json, to_json_binary, Binary, Decimal, Deps, DepsMut, Env, MessageInfo, - Reply, Response, StdError, StdResult, + Binary, Decimal, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdError, StdResult, + entry_point, from_json, to_json_binary, }; -use cw2::{get_contract_version, set_contract_version, ContractVersion}; +use cw2::{ContractVersion, get_contract_version, set_contract_version}; use cw20::Cw20ReceiveMsg; use pfc_steak::hub::{ CallbackMsg, ExecuteMsg, FeeType, InstantiateMsg, MigrateMsg, QueryMsg, ReceiveMsg, diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index 969d7a1..7df505f 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -1,18 +1,18 @@ use std::{collections::HashSet, iter::FromIterator, str::FromStr}; use cosmwasm_std::{ - from_json, to_json_binary, Addr, BankMsg, Binary, Coin, CosmosMsg, Decimal, DepsMut, - DistributionMsg, Env, Event, Order, Response, StdError, StdResult, SubMsg, SubMsgResponse, - Uint128, WasmMsg, + Addr, BankMsg, Binary, Coin, CosmosMsg, Decimal, DepsMut, DistributionMsg, Env, Event, Order, + Response, StdError, StdResult, SubMsg, SubMsgResponse, Uint128, WasmMsg, from_json, + to_json_binary, }; use cw20::{Cw20ExecuteMsg, MinterResponse}; use cw20_base::msg::InstantiateMsg as Cw20InstantiateMsg; use pfc_steak::{ + DecimalCheckedOps, hub::{ Batch, CallbackMsg, Cw20HookMsg, ExecuteMsg, FeeType, InstantiateMsg, PendingBatch, UnbondRequest, }, - DecimalCheckedOps, }; use crate::{ @@ -60,14 +60,11 @@ pub fn instantiate(deps: DepsMut, env: Env, msg: InstantiateMsg) -> StdResult StdResul // Bonding and harvesting logics //-------------------------------------------------------------------------------------------------- -/// NOTE: In a previous implementation, we split up the deposited Luna over all validators, so that -/// they all have the same amount of delegation. This is however quite gas-expensive: $1.5 cost in -/// the case of 15 validators. +/// bond tokens (XXX) to validators, returning bXXXX /// /// To save gas for users, now we simply delegate all deposited Luna to the validator with the /// smallest amount of delegation. If delegations become severely unbalance as a result of this @@ -546,27 +541,20 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { // // I don't have a solution for this... other than to manually fund contract with the slashed // amount. - state.previous_batches.save( - deps.storage, - pending_batch.id, - &Batch { - id: pending_batch.id, - reconciled: false, - total_shares: pending_batch.usteak_to_burn, - amount_unclaimed: amount_to_bond, - est_unbond_end_time: current_time + unbond_period, - }, - )?; + state.previous_batches.save(deps.storage, pending_batch.id, &Batch { + id: pending_batch.id, + reconciled: false, + total_shares: pending_batch.usteak_to_burn, + amount_unclaimed: amount_to_bond, + est_unbond_end_time: current_time + unbond_period, + })?; let epoch_period = state.epoch_period.load(deps.storage)?; - state.pending_batch.save( - deps.storage, - &PendingBatch { - id: pending_batch.id + 1, - usteak_to_burn: Uint128::zero(), - est_unbond_start_time: current_time + epoch_period, - }, - )?; + state.pending_batch.save(deps.storage, &PendingBatch { + id: pending_batch.id + 1, + usteak_to_burn: Uint128::zero(), + est_unbond_start_time: current_time + epoch_period, + })?; state .prev_denom .save(deps.storage, &get_denom_balance(&deps.querier, env.contract.address, denom)?)?; diff --git a/contracts/hub/src/migrations.rs b/contracts/hub/src/migrations.rs index 6b86e7b..6e8288f 100644 --- a/contracts/hub/src/migrations.rs +++ b/contracts/hub/src/migrations.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; use crate::{ helpers::get_denom_balance, - state::{State, BATCH_KEY_V101}, + state::{BATCH_KEY_V101, State}, types::BooleanKey, }; @@ -88,7 +88,7 @@ pub(crate) struct PreviousBatchesIndexesV100<'a> { pub reconciled: MultiIndex<'a, BooleanKey, BatchV100, Vec>, } -impl<'a> IndexList for PreviousBatchesIndexesV100<'a> { +impl IndexList for PreviousBatchesIndexesV100<'_> { fn get_indexes(&'_ self) -> Box> + '_> { let v: Vec<&dyn Index> = vec![&self.reconciled]; Box::new(v.into_iter()) diff --git a/contracts/hub/src/state.rs b/contracts/hub/src/state.rs index 597d56d..9a04cd8 100644 --- a/contracts/hub/src/state.rs +++ b/contracts/hub/src/state.rs @@ -84,7 +84,7 @@ impl Default for State<'static> { } } -impl<'a> State<'a> { +impl State<'_> { pub fn assert_owner(&self, storage: &dyn Storage, sender: &Addr) -> StdResult<()> { let owner = self.owner.load(storage)?; if *sender == owner { @@ -100,7 +100,7 @@ pub(crate) struct PreviousBatchesIndexes<'a> { pub reconciled: MultiIndex<'a, BooleanKey, Batch, Vec>, } -impl<'a> IndexList for PreviousBatchesIndexes<'a> { +impl IndexList for PreviousBatchesIndexes<'_> { fn get_indexes(&'_ self) -> Box> + '_> { let v: Vec<&dyn Index> = vec![&self.reconciled]; Box::new(v.into_iter()) @@ -112,7 +112,7 @@ pub(crate) struct UnbondRequestsIndexes<'a> { pub user: MultiIndex<'a, String, UnbondRequest, Vec>, } -impl<'a> IndexList for UnbondRequestsIndexes<'a> { +impl IndexList for UnbondRequestsIndexes<'_> { fn get_indexes(&'_ self) -> Box> + '_> { let v: Vec<&dyn Index> = vec![&self.user]; Box::new(v.into_iter()) diff --git a/contracts/hub/src/testing/custom_querier.rs b/contracts/hub/src/testing/custom_querier.rs index 7fe2cb0..f70715f 100644 --- a/contracts/hub/src/testing/custom_querier.rs +++ b/contracts/hub/src/testing/custom_querier.rs @@ -1,10 +1,9 @@ use std::collections::HashMap; use cosmwasm_std::{ - from_json, - testing::{BankQuerier, StakingQuerier, MOCK_CONTRACT_ADDR}, Addr, Coin, Empty, FullDelegation, Querier, QuerierResult, QueryRequest, SystemError, - WasmQuery, + WasmQuery, from_json, + testing::{BankQuerier, MOCK_CONTRACT_ADDR, StakingQuerier}, }; use cw20::Cw20QueryMsg; diff --git a/contracts/hub/src/testing/cw20_querier.rs b/contracts/hub/src/testing/cw20_querier.rs index 533f66b..4a3be01 100644 --- a/contracts/hub/src/testing/cw20_querier.rs +++ b/contracts/hub/src/testing/cw20_querier.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use cosmwasm_std::{to_json_binary, QuerierResult, SystemError, Uint128}; +use cosmwasm_std::{QuerierResult, SystemError, Uint128, to_json_binary}; use cw20::{BalanceResponse, Cw20QueryMsg, TokenInfoResponse}; use super::helpers::err_unsupported_query; diff --git a/contracts/hub/src/testing/helpers.rs b/contracts/hub/src/testing/helpers.rs index 9f51d21..fd1aeca 100644 --- a/contracts/hub/src/testing/helpers.rs +++ b/contracts/hub/src/testing/helpers.rs @@ -1,8 +1,7 @@ use cosmwasm_std::{ - from_json, - testing::{mock_env, MockApi, MockStorage, MOCK_CONTRACT_ADDR}, Addr, BlockInfo, ContractInfo, Deps, Env, OwnedDeps, QuerierResult, SystemError, SystemResult, - Timestamp, + Timestamp, from_json, + testing::{MOCK_CONTRACT_ADDR, MockApi, MockStorage, mock_env}, }; use pfc_steak::hub::QueryMsg; use serde::de::DeserializeOwned; diff --git a/contracts/hub/src/testing/tests.rs b/contracts/hub/src/testing/tests.rs index 431b888..e68f927 100644 --- a/contracts/hub/src/testing/tests.rs +++ b/contracts/hub/src/testing/tests.rs @@ -1,9 +1,10 @@ use std::str::FromStr; use cosmwasm_std::{ - testing::{mock_env, mock_info, MockApi, MockStorage, MOCK_CONTRACT_ADDR}, - to_json_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, DistributionMsg, Event, Order, - OwnedDeps, Reply, ReplyOn, StdError, SubMsg, SubMsgResponse, Uint128, WasmMsg, + Addr, BankMsg, Coin, CosmosMsg, Decimal, DistributionMsg, Event, Order, OwnedDeps, Reply, + ReplyOn, StdError, SubMsg, SubMsgResponse, Uint128, WasmMsg, + testing::{MOCK_CONTRACT_ADDR, MockApi, MockStorage, mock_env, mock_info}, + to_json_binary, }; use cw20::{Cw20ExecuteMsg, MinterResponse}; use cw20_base::msg::InstantiateMsg as Cw20InstantiateMsg; @@ -19,7 +20,7 @@ use super::{ }; use crate::{ contract::{ - execute, instantiate, reply, REPLY_INSTANTIATE_TOKEN, REPLY_REGISTER_RECEIVED_COINS, + REPLY_INSTANTIATE_TOKEN, REPLY_REGISTER_RECEIVED_COINS, execute, instantiate, reply, }, helpers::{parse_coin, parse_received_fund}, math::{ @@ -92,17 +93,13 @@ fn setup_test() -> OwnedDeps { .add_attribute("code_id", "69420") .add_attribute("_contract_address", "steak_token"); - let res = reply( - deps.as_mut(), - mock_env_at_timestamp(10000), - Reply { - id: REPLY_INSTANTIATE_TOKEN, - result: cosmwasm_std::SubMsgResult::Ok(SubMsgResponse { - events: vec![event], - data: None, - }), - }, - ) + let res = reply(deps.as_mut(), mock_env_at_timestamp(10000), Reply { + id: REPLY_INSTANTIATE_TOKEN, + result: cosmwasm_std::SubMsgResult::Ok(SubMsgResponse { + events: vec![event], + data: None, + }), + }) .unwrap(); assert_eq!(res.messages.len(), 0); @@ -169,17 +166,13 @@ fn setup_test_fee_split() -> OwnedDeps { .add_attribute("code_id", "69420") .add_attribute("_contract_address", "steak_token"); - let res = reply( - deps.as_mut(), - mock_env_at_timestamp(10000), - Reply { - id: REPLY_INSTANTIATE_TOKEN, - result: cosmwasm_std::SubMsgResult::Ok(SubMsgResponse { - events: vec![event], - data: None, - }), - }, - ) + let res = reply(deps.as_mut(), mock_env_at_timestamp(10000), Reply { + id: REPLY_INSTANTIATE_TOKEN, + result: cosmwasm_std::SubMsgResult::Ok(SubMsgResponse { + events: vec![event], + data: None, + }), + }) .unwrap(); assert_eq!(res.messages.len(), 0); @@ -197,68 +190,56 @@ fn proper_instantiation() { let deps = setup_test(); let res: ConfigResponse = query_helper(deps.as_ref(), QueryMsg::Config {}); - assert_eq!( - res, - ConfigResponse { - owner: "larry".to_string(), - new_owner: None, - steak_token: "steak_token".to_string(), - epoch_period: 259200, - unbond_period: 1814400, - denom: "uxyz".to_string(), - fee_type: "Wallet".to_string(), - fee_account: "the_fee_man".to_string(), - fee_rate: Decimal::from_ratio(10_u128, 100_u128), - max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), - validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string(),], - paused_validators: vec![], - dust_collector: None, - token_factory: None - } - ); + assert_eq!(res, ConfigResponse { + owner: "larry".to_string(), + new_owner: None, + steak_token: "steak_token".to_string(), + epoch_period: 259200, + unbond_period: 1814400, + denom: "uxyz".to_string(), + fee_type: "Wallet".to_string(), + fee_account: "the_fee_man".to_string(), + fee_rate: Decimal::from_ratio(10_u128, 100_u128), + max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), + validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string(),], + paused_validators: vec![], + dust_collector: None, + token_factory: None + }); let res: StateResponse = query_helper(deps.as_ref(), QueryMsg::State {}); - assert_eq!( - res, - StateResponse { - total_usteak: Uint128::zero(), - total_native: Uint128::zero(), - exchange_rate: Decimal::one(), - unlocked_coins: vec![], - }, - ); + assert_eq!(res, StateResponse { + total_usteak: Uint128::zero(), + total_native: Uint128::zero(), + exchange_rate: Decimal::one(), + unlocked_coins: vec![], + },); let res: PendingBatch = query_helper(deps.as_ref(), QueryMsg::PendingBatch {}); - assert_eq!( - res, - PendingBatch { - id: 1, - usteak_to_burn: Uint128::zero(), - est_unbond_start_time: 269200, // 10,000 + 259,200 - }, - ); + assert_eq!(res, PendingBatch { + id: 1, + usteak_to_burn: Uint128::zero(), + est_unbond_start_time: 269200, // 10,000 + 259,200 + },); let deps_fee_split = setup_test_fee_split(); let res_fee_split: ConfigResponse = query_helper(deps_fee_split.as_ref(), QueryMsg::Config {}); - assert_eq!( - res_fee_split, - ConfigResponse { - owner: "larry".to_string(), - new_owner: None, - steak_token: "steak_token".to_string(), - epoch_period: 259200, - unbond_period: 1814400, - denom: "uxyz".to_string(), - fee_type: "FeeSplit".to_string(), - fee_account: "fee_split_contract".to_string(), - fee_rate: Decimal::from_ratio(10_u128, 100_u128), - max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), - validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string(),], - paused_validators: vec![], - dust_collector: None, - token_factory: None - } - ); + assert_eq!(res_fee_split, ConfigResponse { + owner: "larry".to_string(), + new_owner: None, + steak_token: "steak_token".to_string(), + epoch_period: 259200, + unbond_period: 1814400, + denom: "uxyz".to_string(), + fee_type: "FeeSplit".to_string(), + fee_account: "fee_split_contract".to_string(), + fee_rate: Decimal::from_ratio(10_u128, 100_u128), + max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), + validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string(),], + paused_validators: vec![], + dust_collector: None, + token_factory: None + }); } #[test] @@ -290,41 +271,35 @@ fn bonding() { REPLY_REGISTER_RECEIVED_COINS ) ); - assert_eq!( - res.messages[1], - SubMsg { - id: 0, - msg: CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: "steak_token".to_string(), - msg: to_json_binary(&Cw20ExecuteMsg::Mint { - recipient: "cosmos2contract".to_string(), - amount: Uint128::new(1_000_000), - }) - .unwrap(), - funds: vec![], - }), - gas_limit: None, - reply_on: ReplyOn::Never, - } - ); - - assert_eq!( - res.messages[2], - SubMsg { - id: 0, - msg: CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: "steak_token".to_string(), - msg: to_json_binary(&Cw20ExecuteMsg::Transfer { - recipient: "user_1".to_string(), - amount: Uint128::new(1000000), - }) - .unwrap(), - funds: vec![], - }), - gas_limit: None, - reply_on: ReplyOn::Never, - } - ); + assert_eq!(res.messages[1], SubMsg { + id: 0, + msg: CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: "steak_token".to_string(), + msg: to_json_binary(&Cw20ExecuteMsg::Mint { + recipient: "cosmos2contract".to_string(), + amount: Uint128::new(1_000_000), + }) + .unwrap(), + funds: vec![], + }), + gas_limit: None, + reply_on: ReplyOn::Never, + }); + + assert_eq!(res.messages[2], SubMsg { + id: 0, + msg: CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: "steak_token".to_string(), + msg: to_json_binary(&Cw20ExecuteMsg::Transfer { + recipient: "user_1".to_string(), + amount: Uint128::new(1000000), + }) + .unwrap(), + funds: vec![], + }), + gas_limit: None, + reply_on: ReplyOn::Never, + }); // Bond when there are existing delegations, and Luna:Steak exchange rate is >1 // Previously user 1 delegated 1,000,000 uluna. We assume we have accumulated 2.5% yield at @@ -356,41 +331,35 @@ fn bonding() { REPLY_REGISTER_RECEIVED_COINS ) ); - assert_eq!( - res.messages[1], - SubMsg { - id: 0, - msg: CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: "steak_token".to_string(), - msg: to_json_binary(&Cw20ExecuteMsg::Mint { - recipient: "cosmos2contract".to_string(), - amount: Uint128::new(12043), - }) - .unwrap(), - funds: vec![], - }), - gas_limit: None, - reply_on: ReplyOn::Never, - } - ); - - assert_eq!( - res.messages[2], - SubMsg { - id: 0, - msg: CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: "steak_token".to_string(), - msg: to_json_binary(&Cw20ExecuteMsg::Transfer { - recipient: "user_3".to_string(), - amount: Uint128::new(12043), - }) - .unwrap(), - funds: vec![], - }), - gas_limit: None, - reply_on: ReplyOn::Never, - } - ); + assert_eq!(res.messages[1], SubMsg { + id: 0, + msg: CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: "steak_token".to_string(), + msg: to_json_binary(&Cw20ExecuteMsg::Mint { + recipient: "cosmos2contract".to_string(), + amount: Uint128::new(12043), + }) + .unwrap(), + funds: vec![], + }), + gas_limit: None, + reply_on: ReplyOn::Never, + }); + + assert_eq!(res.messages[2], SubMsg { + id: 0, + msg: CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: "steak_token".to_string(), + msg: to_json_binary(&Cw20ExecuteMsg::Transfer { + recipient: "user_3".to_string(), + amount: Uint128::new(12043), + }) + .unwrap(), + funds: vec![], + }), + gas_limit: None, + reply_on: ReplyOn::Never, + }); // Check the state after bonding deps.querier.set_staking_delegations(&[ @@ -401,15 +370,12 @@ fn bonding() { deps.querier.set_cw20_total_supply("steak_token", 1012043); let res: StateResponse = query_helper(deps.as_ref(), QueryMsg::State {}); - assert_eq!( - res, - StateResponse { - total_usteak: Uint128::new(1012043), - total_native: Uint128::new(1037345), - exchange_rate: Decimal::from_ratio(1037345u128, 1012043u128), - unlocked_coins: vec![], - } - ); + assert_eq!(res, StateResponse { + total_usteak: Uint128::new(1012043), + total_native: Uint128::new(1037345), + exchange_rate: Decimal::from_ratio(1037345u128, 1012043u128), + unlocked_coins: vec![], + }); } #[test] fn bond_ex() { @@ -439,23 +405,20 @@ fn bond_ex() { REPLY_REGISTER_RECEIVED_COINS ) ); - assert_eq!( - res.messages[1], - SubMsg { - id: 0, - msg: CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: "steak_token".to_string(), - msg: to_json_binary(&Cw20ExecuteMsg::Mint { - recipient: "user_1".to_string(), - amount: Uint128::new(1_000_000), - }) - .unwrap(), - funds: vec![], - }), - gas_limit: None, - reply_on: ReplyOn::Never, - } - ); + assert_eq!(res.messages[1], SubMsg { + id: 0, + msg: CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: "steak_token".to_string(), + msg: to_json_binary(&Cw20ExecuteMsg::Mint { + recipient: "user_1".to_string(), + amount: Uint128::new(1_000_000), + }) + .unwrap(), + funds: vec![], + }), + gas_limit: None, + reply_on: ReplyOn::Never, + }); } #[test] @@ -501,19 +464,16 @@ fn harvesting() { REPLY_REGISTER_RECEIVED_COINS, ) ); - assert_eq!( - res.messages[3], - SubMsg { - id: 0, - msg: CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: MOCK_CONTRACT_ADDR.to_string(), - msg: to_json_binary(&ExecuteMsg::Callback(CallbackMsg::Reinvest {})).unwrap(), - funds: vec![], - }), - gas_limit: None, - reply_on: ReplyOn::Never, - } - ); + assert_eq!(res.messages[3], SubMsg { + id: 0, + msg: CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: MOCK_CONTRACT_ADDR.to_string(), + msg: to_json_binary(&ExecuteMsg::Callback(CallbackMsg::Reinvest {})).unwrap(), + funds: vec![], + }), + gas_limit: None, + reply_on: ReplyOn::Never, + }); } #[test] @@ -531,33 +491,23 @@ fn registering_unlocked_coins() { 0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", ); - reply( - deps.as_mut(), - mock_env(), - Reply { - id: 2, - result: cosmwasm_std::SubMsgResult::Ok(SubMsgResponse { - events: vec![event], - data: None, - }), - }, - ) + reply(deps.as_mut(), mock_env(), Reply { + id: 2, + result: cosmwasm_std::SubMsgResult::Ok(SubMsgResponse { + events: vec![event], + data: None, + }), + }) .unwrap(); // Unlocked coins in contract state should have been updated let unlocked_coins = state.unlocked_coins.load(deps.as_ref().storage).unwrap(); - assert_eq!( - unlocked_coins, - vec![ - Coin::new(123, "ukrw"), - Coin::new(234, "uxyz"), - Coin::new(345, "uusd"), - Coin::new( - 69420, - "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", - ), - ] - ); + assert_eq!(unlocked_coins, vec![ + Coin::new(123, "ukrw"), + Coin::new(234, "uxyz"), + Coin::new(345, "uusd"), + Coin::new(69420, "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B",), + ]); } #[test] @@ -576,16 +526,13 @@ fn reinvesting() { // After the swaps, `unlocked_coins` should contain only uxyz and unknown denoms state .unlocked_coins - .save( - deps.as_mut().storage, - &vec![ - Coin::new(234, "uxyz"), - Coin::new( - 69420, - "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", - ), - ], - ) + .save(deps.as_mut().storage, &vec![ + Coin::new(234, "uxyz"), + Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + ), + ]) .unwrap(); // Bob has the smallest amount of delegations, so all proceeds go to him @@ -598,38 +545,29 @@ fn reinvesting() { .unwrap(); assert_eq!(res.messages.len(), 2); - assert_eq!( - res.messages[0], - SubMsg { - id: 0, - msg: Delegation::new("bob", 234 - 23, "uxyz").to_cosmos_msg(), - gas_limit: None, - reply_on: ReplyOn::Never, - } - ); + assert_eq!(res.messages[0], SubMsg { + id: 0, + msg: Delegation::new("bob", 234 - 23, "uxyz").to_cosmos_msg(), + gas_limit: None, + reply_on: ReplyOn::Never, + }); let send_msg = BankMsg::Send { to_address: "the_fee_man".into(), amount: vec![Coin::new(23u128, "uxyz")], }; - assert_eq!( - res.messages[1], - SubMsg { - id: 0, - msg: CosmosMsg::Bank(send_msg), - gas_limit: None, - reply_on: ReplyOn::Never, - } - ); + assert_eq!(res.messages[1], SubMsg { + id: 0, + msg: CosmosMsg::Bank(send_msg), + gas_limit: None, + reply_on: ReplyOn::Never, + }); // Storage should have been updated let unlocked_coins = state.unlocked_coins.load(deps.as_ref().storage).unwrap(); - assert_eq!( - unlocked_coins, - vec![Coin::new( - 69420, - "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", - )], - ); + assert_eq!(unlocked_coins, vec![Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + )],); } #[test] @@ -648,16 +586,13 @@ fn reinvesting_fee_split() { // After the swaps, `unlocked_coins` should contain only uxyz and unknown denoms state .unlocked_coins - .save( - deps.as_mut().storage, - &vec![ - Coin::new(234, "uxyz"), - Coin::new( - 69420, - "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", - ), - ], - ) + .save(deps.as_mut().storage, &vec![ + Coin::new(234, "uxyz"), + Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + ), + ]) .unwrap(); // Bob has the smallest amount of delegations, so all proceeds go to him @@ -670,40 +605,31 @@ fn reinvesting_fee_split() { .unwrap(); assert_eq!(res.messages.len(), 2); - assert_eq!( - res.messages[0], - SubMsg { - id: 0, - msg: Delegation::new("bob", 234 - 23, "uxyz").to_cosmos_msg(), - gas_limit: None, - reply_on: ReplyOn::Never, - } - ); + assert_eq!(res.messages[0], SubMsg { + id: 0, + msg: Delegation::new("bob", 234 - 23, "uxyz").to_cosmos_msg(), + gas_limit: None, + reply_on: ReplyOn::Never, + }); let send_msg = pfc_fee_split::fee_split_msg::ExecuteMsg::Deposit { flush: false, }; - assert_eq!( - res.messages[1], - SubMsg { - id: 0, - msg: send_msg - .into_cosmos_msg("fee_split_contract", vec![Coin::new(23u128, "uxyz")]) - .unwrap(), - gas_limit: None, - reply_on: ReplyOn::Never, - } - ); + assert_eq!(res.messages[1], SubMsg { + id: 0, + msg: send_msg + .into_cosmos_msg("fee_split_contract", vec![Coin::new(23u128, "uxyz")]) + .unwrap(), + gas_limit: None, + reply_on: ReplyOn::Never, + }); // Storage should have been updated let unlocked_coins = state.unlocked_coins.load(deps.as_ref().storage).unwrap(); - assert_eq!( - unlocked_coins, - vec![Coin::new( - 69420, - "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", - )], - ); + assert_eq!(unlocked_coins, vec![Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + )],); } #[test] @@ -766,19 +692,16 @@ fn queuing_unbond() { .unwrap(); assert_eq!(res.messages.len(), 1); - assert_eq!( - res.messages[0], - SubMsg { - id: 0, - msg: CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: MOCK_CONTRACT_ADDR.to_string(), - msg: to_json_binary(&ExecuteMsg::SubmitBatch {}).unwrap(), - funds: vec![], - }), - gas_limit: None, - reply_on: ReplyOn::Never, - } - ); + assert_eq!(res.messages[0], SubMsg { + id: 0, + msg: CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: MOCK_CONTRACT_ADDR.to_string(), + msg: to_json_binary(&ExecuteMsg::SubmitBatch {}).unwrap(), + funds: vec![], + }), + gas_limit: None, + reply_on: ReplyOn::Never, + }); // The users' unbonding requests should have been saved let ubr1 = state @@ -790,33 +713,24 @@ fn queuing_unbond() { .load(deps.as_ref().storage, (1u64, &Addr::unchecked("user_3"))) .unwrap(); - assert_eq!( - ubr1, - UnbondRequest { - id: 1, - user: Addr::unchecked("user_1"), - shares: Uint128::new(23456), - } - ); - assert_eq!( - ubr2, - UnbondRequest { - id: 1, - user: Addr::unchecked("user_3"), - shares: Uint128::new(69420), - } - ); + assert_eq!(ubr1, UnbondRequest { + id: 1, + user: Addr::unchecked("user_1"), + shares: Uint128::new(23456), + }); + assert_eq!(ubr2, UnbondRequest { + id: 1, + user: Addr::unchecked("user_3"), + shares: Uint128::new(69420), + }); // Pending batch should have been updated let pending_batch = state.pending_batch.load(deps.as_ref().storage).unwrap(); - assert_eq!( - pending_batch, - PendingBatch { - id: 1, - usteak_to_burn: Uint128::new(92876), // 23,456 + 69,420 - est_unbond_start_time: 269200, - } - ); + assert_eq!(pending_batch, PendingBatch { + id: 1, + usteak_to_burn: Uint128::new(92876), // 23,456 + 69,420 + est_unbond_start_time: 269200, + }); } #[test] @@ -861,14 +775,11 @@ fn submitting_batch() { state .pending_batch - .save( - deps.as_mut().storage, - &PendingBatch { - id: 1, - usteak_to_burn: Uint128::new(92876), // 23,456 + 69,420 - est_unbond_start_time: 269200, - }, - ) + .save(deps.as_mut().storage, &PendingBatch { + id: 1, + usteak_to_burn: Uint128::new(92876), // 23,456 + 69,420 + est_unbond_start_time: 269200, + }) .unwrap(); // Anyone can invoke `submit_batch`. Here we continue from the previous test and assume it is @@ -912,46 +823,37 @@ fn submitting_batch() { REPLY_REGISTER_RECEIVED_COINS, ) ); - assert_eq!( - res.messages[3], - SubMsg { - id: 0, - msg: CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: "steak_token".to_string(), - msg: to_json_binary(&Cw20ExecuteMsg::Burn { - amount: Uint128::new(92876) - }) - .unwrap(), - funds: vec![], - }), - gas_limit: None, - reply_on: ReplyOn::Never, - } - ); + assert_eq!(res.messages[3], SubMsg { + id: 0, + msg: CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: "steak_token".to_string(), + msg: to_json_binary(&Cw20ExecuteMsg::Burn { + amount: Uint128::new(92876) + }) + .unwrap(), + funds: vec![], + }), + gas_limit: None, + reply_on: ReplyOn::Never, + }); // A new pending batch should have been created let pending_batch = state.pending_batch.load(deps.as_ref().storage).unwrap(); - assert_eq!( - pending_batch, - PendingBatch { - id: 2, - usteak_to_burn: Uint128::zero(), - est_unbond_start_time: 528401, // 269,201 + 259,200 - } - ); + assert_eq!(pending_batch, PendingBatch { + id: 2, + usteak_to_burn: Uint128::zero(), + est_unbond_start_time: 528401, // 269,201 + 259,200 + }); // Previous batch should have been updated let previous_batch = state.previous_batches.load(deps.as_ref().storage, 1u64).unwrap(); - assert_eq!( - previous_batch, - Batch { - id: 1, - reconciled: false, - total_shares: Uint128::new(92876), - amount_unclaimed: Uint128::new(95197), - est_unbond_end_time: 2083601, // 269,201 + 1,814,400 - } - ); + assert_eq!(previous_batch, Batch { + id: 1, + reconciled: false, + total_shares: Uint128::new(92876), + amount_unclaimed: Uint128::new(95197), + est_unbond_end_time: 2083601, // 269,201 + 1,814,400 + }); } #[test] @@ -999,18 +901,15 @@ fn reconciling() { state .unlocked_coins - .save( - deps.as_mut().storage, - &vec![ - Coin::new(10000, "uxyz"), - Coin::new(234, "ukrw"), - Coin::new(345, "uusd"), - Coin::new( - 69420, - "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", - ), - ], - ) + .save(deps.as_mut().storage, &vec![ + Coin::new(10000, "uxyz"), + Coin::new(234, "ukrw"), + Coin::new(345, "uusd"), + Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + ), + ]) .unwrap(); deps.querier.set_bank_balances(&[ @@ -1039,28 +938,22 @@ fn reconciling() { // batch 2: 1385 - 273 = 1112 // batch 3: 1506 - 273 = 1233 let batch = state.previous_batches.load(deps.as_ref().storage, 2u64).unwrap(); - assert_eq!( - batch, - Batch { - id: 2, - reconciled: true, - total_shares: Uint128::new(1345), - amount_unclaimed: Uint128::new(1112), // 1385 - 273 - est_unbond_end_time: 20000, - } - ); + assert_eq!(batch, Batch { + id: 2, + reconciled: true, + total_shares: Uint128::new(1345), + amount_unclaimed: Uint128::new(1112), // 1385 - 273 + est_unbond_end_time: 20000, + }); let batch = state.previous_batches.load(deps.as_ref().storage, 3u64).unwrap(); - assert_eq!( - batch, - Batch { - id: 3, - reconciled: true, - total_shares: Uint128::new(1456), - amount_unclaimed: Uint128::new(1233), // 1506 - 273 - est_unbond_end_time: 30000, - } - ); + assert_eq!(batch, Batch { + id: 3, + reconciled: true, + total_shares: Uint128::new(1456), + amount_unclaimed: Uint128::new(1233), // 1506 - 273 + est_unbond_end_time: 30000, + }); // Batches 1 and 4 should not have changed let batch = state.previous_batches.load(deps.as_ref().storage, 1u64).unwrap(); @@ -1159,14 +1052,11 @@ fn withdrawing_unbonded() { state .pending_batch - .save( - deps.as_mut().storage, - &PendingBatch { - id: 4, - usteak_to_burn: Uint128::new(56789), - est_unbond_start_time: 100000, - }, - ) + .save(deps.as_mut().storage, &PendingBatch { + id: 4, + usteak_to_burn: Uint128::new(56789), + est_unbond_start_time: 100000, + }) .unwrap(); // Attempt to withdraw before any batch has completed unbonding. Should error @@ -1204,39 +1094,30 @@ fn withdrawing_unbonded() { .unwrap(); assert_eq!(res.messages.len(), 1); - assert_eq!( - res.messages[0], - SubMsg { - id: 0, - msg: CosmosMsg::Bank(BankMsg::Send { - to_address: "user_1".to_string(), - amount: vec![Coin::new(59646, "uxyz")], - }), - gas_limit: None, - reply_on: ReplyOn::Never, - } - ); + assert_eq!(res.messages[0], SubMsg { + id: 0, + msg: CosmosMsg::Bank(BankMsg::Send { + to_address: "user_1".to_string(), + amount: vec![Coin::new(59646, "uxyz")], + }), + gas_limit: None, + reply_on: ReplyOn::Never, + }); // Previous batches should have been updated let batch = state.previous_batches.load(deps.as_ref().storage, 1u64).unwrap(); - assert_eq!( - batch, - Batch { - id: 1, - reconciled: true, - total_shares: Uint128::new(69420), - amount_unclaimed: Uint128::new(71155), - est_unbond_end_time: 10000, - } - ); + assert_eq!(batch, Batch { + id: 1, + reconciled: true, + total_shares: Uint128::new(69420), + amount_unclaimed: Uint128::new(71155), + est_unbond_end_time: 10000, + }); let err = state.previous_batches.load(deps.as_ref().storage, 2u64).unwrap_err(); - assert_eq!( - err, - StdError::NotFound { - kind: "pfc_steak::hub::Batch".to_string(), - } - ); + assert_eq!(err, StdError::NotFound { + kind: "pfc_steak::hub::Batch".to_string(), + }); // User 1's unbond requests in batches 1 and 2 should have been deleted let err1 = state @@ -1248,18 +1129,12 @@ fn withdrawing_unbonded() { .load(deps.as_ref().storage, (1u64, &Addr::unchecked("user_1"))) .unwrap_err(); - assert_eq!( - err1, - StdError::NotFound { - kind: "pfc_steak::hub::UnbondRequest".to_string() - } - ); - assert_eq!( - err2, - StdError::NotFound { - kind: "pfc_steak::hub::UnbondRequest".to_string() - } - ); + assert_eq!(err1, StdError::NotFound { + kind: "pfc_steak::hub::UnbondRequest".to_string() + }); + assert_eq!(err2, StdError::NotFound { + kind: "pfc_steak::hub::UnbondRequest".to_string() + }); // User 3 attempt to withdraw; also specifying a receiver let res = execute( @@ -1273,39 +1148,30 @@ fn withdrawing_unbonded() { .unwrap(); assert_eq!(res.messages.len(), 1); - assert_eq!( - res.messages[0], - SubMsg { - id: 0, - msg: CosmosMsg::Bank(BankMsg::Send { - to_address: "user_2".to_string(), - amount: vec![Coin::new(71155, "uxyz")], - }), - gas_limit: None, - reply_on: ReplyOn::Never, - } - ); + assert_eq!(res.messages[0], SubMsg { + id: 0, + msg: CosmosMsg::Bank(BankMsg::Send { + to_address: "user_2".to_string(), + amount: vec![Coin::new(71155, "uxyz")], + }), + gas_limit: None, + reply_on: ReplyOn::Never, + }); // Batch 1 and user 2's unbonding request should have been purged from storage let err = state.previous_batches.load(deps.as_ref().storage, 1u64).unwrap_err(); - assert_eq!( - err, - StdError::NotFound { - kind: "pfc_steak::hub::Batch".to_string() - } - ); + assert_eq!(err, StdError::NotFound { + kind: "pfc_steak::hub::Batch".to_string() + }); let err = state .unbond_requests .load(deps.as_ref().storage, (1u64, &Addr::unchecked("user_3"))) .unwrap_err(); - assert_eq!( - err, - StdError::NotFound { - kind: "pfc_steak::hub::UnbondRequest".to_string() - } - ); + assert_eq!(err, StdError::NotFound { + kind: "pfc_steak::hub::UnbondRequest".to_string() + }); } #[test] @@ -1313,52 +1179,37 @@ fn adding_validator() { let mut deps = setup_test(); let state = State::default(); - let err = execute( - deps.as_mut(), - mock_env(), - mock_info("jake", &[]), - ExecuteMsg::AddValidator { + let err = + execute(deps.as_mut(), mock_env(), mock_info("jake", &[]), ExecuteMsg::AddValidator { validator: "dave".to_string(), - }, - ) - .unwrap_err(); + }) + .unwrap_err(); assert_eq!(err, StdError::generic_err("unauthorized: sender is not owner")); - let err = execute( - deps.as_mut(), - mock_env(), - mock_info("larry", &[]), - ExecuteMsg::AddValidator { + let err = + execute(deps.as_mut(), mock_env(), mock_info("larry", &[]), ExecuteMsg::AddValidator { validator: "alice".to_string(), - }, - ) - .unwrap_err(); + }) + .unwrap_err(); assert_eq!(err, StdError::generic_err("validator is already whitelisted")); - let res = execute( - deps.as_mut(), - mock_env(), - mock_info("larry", &[]), - ExecuteMsg::AddValidator { + let res = + execute(deps.as_mut(), mock_env(), mock_info("larry", &[]), ExecuteMsg::AddValidator { validator: "dave".to_string(), - }, - ) - .unwrap(); + }) + .unwrap(); assert_eq!(res.messages.len(), 0); let validators = state.validators.load(deps.as_ref().storage).unwrap(); - assert_eq!( - validators, - vec![ - String::from("alice"), - String::from("bob"), - String::from("charlie"), - String::from("dave"), - ], - ); + assert_eq!(validators, vec![ + String::from("alice"), + String::from("bob"), + String::from("charlie"), + String::from("dave"), + ],); } #[test] @@ -1372,27 +1223,19 @@ fn removing_validator() { Delegation::new("charlie", 341666, "uxyz"), ]); - let err = execute( - deps.as_mut(), - mock_env(), - mock_info("jake", &[]), - ExecuteMsg::RemoveValidator { + let err = + execute(deps.as_mut(), mock_env(), mock_info("jake", &[]), ExecuteMsg::RemoveValidator { validator: "charlie".to_string(), - }, - ) - .unwrap_err(); + }) + .unwrap_err(); assert_eq!(err, StdError::generic_err("unauthorized: sender is not owner")); - let err = execute( - deps.as_mut(), - mock_env(), - mock_info("larry", &[]), - ExecuteMsg::RemoveValidator { + let err = + execute(deps.as_mut(), mock_env(), mock_info("larry", &[]), ExecuteMsg::RemoveValidator { validator: "dave".to_string(), - }, - ) - .unwrap_err(); + }) + .unwrap_err(); assert_eq!(err, StdError::generic_err("validator is not already whitelisted")); @@ -1400,15 +1243,11 @@ fn removing_validator() { // Remainder: 0 // Alice: 512500 + 0 - 341667 = 170833 // Bob: 512500 + 0 - 341667 = 170833 - let res = execute( - deps.as_mut(), - mock_env(), - mock_info("larry", &[]), - ExecuteMsg::RemoveValidator { + let res = + execute(deps.as_mut(), mock_env(), mock_info("larry", &[]), ExecuteMsg::RemoveValidator { validator: "charlie".to_string(), - }, - ) - .unwrap(); + }) + .unwrap(); assert_eq!(res.messages.len(), 2); assert_eq!( @@ -1435,15 +1274,11 @@ fn transferring_ownership() { let mut deps = setup_test(); let state = State::default(); - let err = execute( - deps.as_mut(), - mock_env(), - mock_info("jake", &[]), - ExecuteMsg::TransferOwnership { + let err = + execute(deps.as_mut(), mock_env(), mock_info("jake", &[]), ExecuteMsg::TransferOwnership { new_owner: "jake".to_string(), - }, - ) - .unwrap_err(); + }) + .unwrap_err(); assert_eq!(err, StdError::generic_err("unauthorized: sender is not owner")); @@ -1512,67 +1347,51 @@ fn splitting_fees() { assert_eq!(err, StdError::generic_err("Invalid Fee type: Wallet or FeeSplit only")); - execute( - deps.as_mut(), - mock_env(), - mock_info("larry", &[]), - ExecuteMsg::TransferFeeAccount { - fee_account_type: "Wallet".to_string(), - new_fee_account: "charlie".to_string(), - }, - ) + execute(deps.as_mut(), mock_env(), mock_info("larry", &[]), ExecuteMsg::TransferFeeAccount { + fee_account_type: "Wallet".to_string(), + new_fee_account: "charlie".to_string(), + }) .unwrap(); let res: ConfigResponse = query_helper(deps.as_ref(), QueryMsg::Config {}); - assert_eq!( - res, - ConfigResponse { - owner: "larry".to_string(), - new_owner: None, - steak_token: "steak_token".to_string(), - epoch_period: 259200, - unbond_period: 1814400, - denom: "uxyz".to_string(), - fee_type: "Wallet".to_string(), - fee_account: "charlie".to_string(), - fee_rate: Decimal::from_ratio(10_u128, 100_u128), - max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), - validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string(),], - paused_validators: vec![], - dust_collector: None, - token_factory: None - } - ); - - execute( - deps.as_mut(), - mock_env(), - mock_info("larry", &[]), - ExecuteMsg::TransferFeeAccount { - fee_account_type: "FeeSplit".to_string(), - new_fee_account: "contract".to_string(), - }, - ) + assert_eq!(res, ConfigResponse { + owner: "larry".to_string(), + new_owner: None, + steak_token: "steak_token".to_string(), + epoch_period: 259200, + unbond_period: 1814400, + denom: "uxyz".to_string(), + fee_type: "Wallet".to_string(), + fee_account: "charlie".to_string(), + fee_rate: Decimal::from_ratio(10_u128, 100_u128), + max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), + validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string(),], + paused_validators: vec![], + dust_collector: None, + token_factory: None + }); + + execute(deps.as_mut(), mock_env(), mock_info("larry", &[]), ExecuteMsg::TransferFeeAccount { + fee_account_type: "FeeSplit".to_string(), + new_fee_account: "contract".to_string(), + }) .unwrap(); let res: ConfigResponse = query_helper(deps.as_ref(), QueryMsg::Config {}); - assert_eq!( - res, - ConfigResponse { - owner: "larry".to_string(), - new_owner: None, - steak_token: "steak_token".to_string(), - epoch_period: 259200, - unbond_period: 1814400, - denom: "uxyz".to_string(), - fee_type: "FeeSplit".to_string(), - fee_account: "contract".to_string(), - fee_rate: Decimal::from_ratio(10_u128, 100_u128), - max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), - validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string(),], - paused_validators: vec![], - dust_collector: None, - token_factory: None - } - ); + assert_eq!(res, ConfigResponse { + owner: "larry".to_string(), + new_owner: None, + steak_token: "steak_token".to_string(), + epoch_period: 259200, + unbond_period: 1814400, + denom: "uxyz".to_string(), + fee_type: "FeeSplit".to_string(), + fee_account: "contract".to_string(), + fee_rate: Decimal::from_ratio(10_u128, 100_u128), + max_fee_rate: Decimal::from_ratio(20_u128, 100_u128), + validators: vec!["alice".to_string(), "bob".to_string(), "charlie".to_string(),], + paused_validators: vec![], + dust_collector: None, + token_factory: None + }); } //-------------------------------------------------------------------------------------------------- // Queries @@ -1626,31 +1445,22 @@ fn querying_previous_batches() { assert_eq!(res, batches[1].clone()); // Query multiple batches - let res: Vec = query_helper( - deps.as_ref(), - QueryMsg::PreviousBatches { - start_after: None, - limit: None, - }, - ); + let res: Vec = query_helper(deps.as_ref(), QueryMsg::PreviousBatches { + start_after: None, + limit: None, + }); assert_eq!(res, batches.clone()); - let res: Vec = query_helper( - deps.as_ref(), - QueryMsg::PreviousBatches { - start_after: Some(1), - limit: None, - }, - ); + let res: Vec = query_helper(deps.as_ref(), QueryMsg::PreviousBatches { + start_after: Some(1), + limit: None, + }); assert_eq!(res, vec![batches[1].clone(), batches[2].clone(), batches[3].clone()]); - let res: Vec = query_helper( - deps.as_ref(), - QueryMsg::PreviousBatches { - start_after: Some(4), - limit: None, - }, - ); + let res: Vec = query_helper(deps.as_ref(), QueryMsg::PreviousBatches { + start_after: Some(4), + limit: None, + }); assert_eq!(res, vec![]); // Query multiple batches, indexed by whether it has been reconciled @@ -1722,51 +1532,40 @@ fn querying_unbond_requests() { .unwrap(); } - let res: Vec = query_helper( - deps.as_ref(), - QueryMsg::UnbondRequestsByBatch { + let res: Vec = + query_helper(deps.as_ref(), QueryMsg::UnbondRequestsByBatch { id: 1, start_after: None, limit: None, - }, - ); - assert_eq!( - res, - vec![ - unbond_requests[0].clone().into(), - unbond_requests[1].clone().into(), - unbond_requests[2].clone().into(), - ] - ); + }); + assert_eq!(res, vec![ + unbond_requests[0].clone().into(), + unbond_requests[1].clone().into(), + unbond_requests[2].clone().into(), + ]); - let res: Vec = query_helper( - deps.as_ref(), - QueryMsg::UnbondRequestsByBatch { + let res: Vec = + query_helper(deps.as_ref(), QueryMsg::UnbondRequestsByBatch { id: 2, start_after: None, limit: None, - }, - ); + }); assert_eq!(res, vec![unbond_requests[3].clone().into()]); - let res: Vec = query_helper( - deps.as_ref(), - QueryMsg::UnbondRequestsByUser { + let res: Vec = + query_helper(deps.as_ref(), QueryMsg::UnbondRequestsByUser { user: "alice".to_string(), start_after: None, limit: None, - }, - ); + }); assert_eq!(res, vec![unbond_requests[0].clone().into(), unbond_requests[3].clone().into(),]); - let res: Vec = query_helper( - deps.as_ref(), - QueryMsg::UnbondRequestsByUser { + let res: Vec = + query_helper(deps.as_ref(), QueryMsg::UnbondRequestsByUser { user: "alice".to_string(), start_after: Some(2), limit: None, - }, - ); + }); assert_eq!(res, vec![unbond_requests[3].clone().into()]); } @@ -1960,10 +1759,11 @@ fn adding_coins() { assert_eq!(coins.0, vec![Coin::new(12345, "uatom"), Coin::new(23456, "uxyz")]); coins.add_many(&Coins::from_str("76543uatom,69420uusd").unwrap()).unwrap(); - assert_eq!( - coins.0, - vec![Coin::new(88888, "uatom"), Coin::new(23456, "uxyz"), Coin::new(69420, "uusd"),] - ); + assert_eq!(coins.0, vec![ + Coin::new(88888, "uatom"), + Coin::new(23456, "uxyz"), + Coin::new(69420, "uusd"), + ]); } #[test] @@ -2027,18 +1827,15 @@ fn reconciling_underflow() { } state .unlocked_coins - .save( - deps.as_mut().storage, - &vec![ - Coin::new(10000, "uatom"), - Coin::new(234, "ukrw"), - Coin::new(345, "uusd"), - Coin::new( - 69420, - "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", - ), - ], - ) + .save(deps.as_mut().storage, &vec![ + Coin::new(10000, "uatom"), + Coin::new(234, "ukrw"), + Coin::new(345, "uusd"), + Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + ), + ]) .unwrap(); deps.querier.set_bank_balances(&[ Coin::new(12345, "uatom"), @@ -2097,18 +1894,15 @@ fn reconciling_underflow_second() { } state .unlocked_coins - .save( - deps.as_mut().storage, - &vec![ - Coin::new(10000, "uatom"), - Coin::new(234, "ukrw"), - Coin::new(345, "uusd"), - Coin::new( - 69420, - "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", - ), - ], - ) + .save(deps.as_mut().storage, &vec![ + Coin::new(10000, "uatom"), + Coin::new(234, "ukrw"), + Coin::new(345, "uusd"), + Coin::new( + 69420, + "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", + ), + ]) .unwrap(); deps.querier.set_bank_balances(&[ Coin::new(12345 - 1323, "uatom"), diff --git a/contracts/hub/src/types/keys.rs b/contracts/hub/src/types/keys.rs index 5c2af96..25dab9a 100644 --- a/contracts/hub/src/types/keys.rs +++ b/contracts/hub/src/types/keys.rs @@ -27,7 +27,7 @@ impl From for BooleanKey { } } -impl<'a> PrimaryKey<'a> for BooleanKey { +impl PrimaryKey<'_> for BooleanKey { type Prefix = (); type SubPrefix = (); type Suffix = (); @@ -38,7 +38,7 @@ impl<'a> PrimaryKey<'a> for BooleanKey { } } -impl<'a> Prefixer<'a> for BooleanKey { +impl Prefixer<'_> for BooleanKey { fn prefix(&self) -> Vec { self.wrapped.prefix() } diff --git a/contracts/token/src/lib.rs b/contracts/token/src/lib.rs index d50b0bd..f03b7e8 100644 --- a/contracts/token/src/lib.rs +++ b/contracts/token/src/lib.rs @@ -2,10 +2,10 @@ use cosmwasm_std::{ Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdError, StdResult, Storage, }; use cw20_base::{ + ContractError, contract::{execute as cw20_execute, instantiate as cw20_instantiate, query as cw20_query}, msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, state::{MinterData, TOKEN_INFO}, - ContractError, }; //#[cfg_attr(not(feature = "library"), entry_point)] @@ -64,10 +64,10 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { #[cfg(test)] mod tests { use cosmwasm_std::{ - testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, OwnedDeps, Uint128, + testing::{MockApi, MockQuerier, MockStorage, mock_dependencies, mock_env, mock_info}, }; - use cw20_base::state::{TokenInfo, BALANCES}; + use cw20_base::state::{BALANCES, TokenInfo}; use super::*; @@ -75,19 +75,16 @@ mod tests { let mut deps = mock_dependencies(); TOKEN_INFO - .save( - deps.as_mut().storage, - &TokenInfo { - name: "Steak Token".to_string(), - symbol: "STEAK".to_string(), - decimals: 6, - total_supply: Uint128::new(200), - mint: Some(MinterData { - minter: Addr::unchecked("steak_hub"), - cap: None, - }), - }, - ) + .save(deps.as_mut().storage, &TokenInfo { + name: "Steak Token".to_string(), + symbol: "STEAK".to_string(), + decimals: 6, + total_supply: Uint128::new(200), + mint: Some(MinterData { + minter: Addr::unchecked("steak_hub"), + cap: None, + }), + }) .unwrap(); BALANCES @@ -106,25 +103,16 @@ mod tests { let mut deps = setup_test(); // Alice is not allowed to burn her balance - let res = execute( - deps.as_mut(), - mock_env(), - mock_info("alice", &[]), - ExecuteMsg::Burn { - amount: Uint128::new(100), - }, - ); + let res = execute(deps.as_mut(), mock_env(), mock_info("alice", &[]), ExecuteMsg::Burn { + amount: Uint128::new(100), + }); assert_eq!(res, Err(StdError::generic_err("only minter can execute token burn").into())); // Steak Hub can burn - let res = execute( - deps.as_mut(), - mock_env(), - mock_info("steak_hub", &[]), - ExecuteMsg::Burn { + let res = + execute(deps.as_mut(), mock_env(), mock_info("steak_hub", &[]), ExecuteMsg::Burn { amount: Uint128::new(100), - }, - ); + }); assert!(res.is_ok()); // Steak Hub's token balance should have been reduced @@ -141,15 +129,11 @@ mod tests { let mut deps = setup_test(); // Not even Steak Hub can invoke `burn_from` - let res = execute( - deps.as_mut(), - mock_env(), - mock_info("steak_hub", &[]), - ExecuteMsg::BurnFrom { + let res = + execute(deps.as_mut(), mock_env(), mock_info("steak_hub", &[]), ExecuteMsg::BurnFrom { owner: "alice".to_string(), amount: Uint128::new(100), - }, - ); + }); assert_eq!(res, Err(StdError::generic_err("`burn_from` command is disabled").into())); } } diff --git a/packages/steak/Cargo.toml b/packages/steak/Cargo.toml index 573cff5..2c6e836 100644 --- a/packages/steak/Cargo.toml +++ b/packages/steak/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak" -version = "3.0.16" +version = "3.0.20" authors = ["larry ", "PFC "] edition = "2018" description = "Liquid steaking protocol for the cosmos" @@ -9,10 +9,10 @@ homepage = "https://liquidsteaking.app" repository = "https://github.com/PFC-developer/steak-contracts" [dependencies] -cosmwasm-std = { version = "1.5.4", features = ["ibc3"] } -cosmwasm-schema = "1.5.4" +cosmwasm-std = { version = "1.5.8", features = ["ibc3"] } +cosmwasm-schema = "1.5.8" cw20 = "1.1.2" cw20-base = { version = "1.1.2", features = ["library"] } -schemars = "0.8.1" -serde = { version = "1.0.103", default-features = false, features = ["derive"] } +schemars = "0.8.21" +serde = { version = "1.0.210", default-features = false, features = ["derive"] } diff --git a/packages/steak/src/hub.rs b/packages/steak/src/hub.rs index 9201669..d5e71f3 100644 --- a/packages/steak/src/hub.rs +++ b/packages/steak/src/hub.rs @@ -1,7 +1,7 @@ use std::{fmt::Display, str::FromStr}; use cosmwasm_std::{ - to_json_binary, Addr, Binary, Coin, CosmosMsg, Decimal, Empty, StdResult, Uint128, WasmMsg, + Addr, Binary, Coin, CosmosMsg, Decimal, Empty, StdResult, Uint128, WasmMsg, to_json_binary, }; use cw20::Cw20ReceiveMsg; use cw20_base::msg::InstantiateMarketingInfo as Cw20InstantiateMarketingInfo; diff --git a/rustfmt.toml b/rustfmt.toml index ce337f2..023436f 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,13 +1,13 @@ # https://rust-lang.github.io/rustfmt format_code_in_doc_comments = true # nightly -group_imports = "StdExternalCrate" # nightly -imports_granularity = "Crate" # nightly -match_block_trailing_comma = true -max_width = 100 -use_small_heuristics = "Off" +group_imports = "StdExternalCrate" # nightly +imports_granularity = "Crate" # nightly +match_block_trailing_comma = true +max_width = 100 +use_small_heuristics = "Off" edition = "2021" -version = "Two" +style_edition = "2024" comment_width = 100 From 376a7fce0346f32d200a7521d3fd4378c2a64d85 Mon Sep 17 00:00:00 2001 From: PFC <81114960+PFC-developer@users.noreply.github.com> Date: Mon, 14 Oct 2024 15:15:13 -0500 Subject: [PATCH 77/77] fix: use ALL delegations, instead of inactive ones --- Cargo.lock | 2 +- contracts/hub-tf/src/execute.rs | 42 +++++++-------------------- contracts/hub-tf/src/math.rs | 20 ++++++------- contracts/hub-tf/src/testing/tests.rs | 2 +- contracts/hub/src/execute.rs | 42 ++++++++------------------- contracts/hub/src/helpers.rs | 18 ++++++++++++ contracts/hub/src/math.rs | 20 ++++++------- contracts/hub/src/queries.rs | 17 ++--------- contracts/token/Cargo.toml | 6 ++-- contracts/token/src/lib.rs | 7 +++-- init/archway_hub_init.json | 6 ++-- justfile | 12 -------- 12 files changed, 76 insertions(+), 118 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d0b890c..74f8027 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -719,7 +719,7 @@ dependencies = [ [[package]] name = "pfc-steak-token" -version = "2.0.2" +version = "3.0.20" dependencies = [ "cosmwasm-std", "cw20 0.13.4", diff --git a/contracts/hub-tf/src/execute.rs b/contracts/hub-tf/src/execute.rs index 9bd8a4c..7b24749 100644 --- a/contracts/hub-tf/src/execute.rs +++ b/contracts/hub-tf/src/execute.rs @@ -1,4 +1,4 @@ -use std::{collections::HashSet, iter::FromIterator, str::FromStr}; +use std::{collections::HashSet, str::FromStr}; use cosmwasm_std::{ Addr, BankMsg, Coin, CosmosMsg, Decimal, DepsMut, DistributionMsg, Env, Event, Order, ReplyOn, @@ -145,25 +145,11 @@ pub fn bond( validators.push(v?); } - let mut validators_wl: HashSet = Default::default(); - for v in VALIDATORS.items(deps.storage, None, None, Order::Ascending) { - validators_wl.insert(v?); - } - for v in validators.iter() { - validators_wl.remove(v); - } - let non_active_validator_list = Vec::from_iter(validators_wl); - // Query the current delegations made to validators, and find the validator with the smallest // delegated amount through a linear search // The code for linear search is a bit uglier than using `sort_by` but cheaper: O(n) vs O(n * // log(n)) - let delegations_non_active = query_delegations( - &deps.querier, - &non_active_validator_list, - &env.contract.address, - &denom, - )?; + let delegations_all = query_all_delegations(&deps.querier, &env.contract.address)?; let delegations = query_delegations(&deps.querier, &validators, &env.contract.address, &denom)?; let mut validator = &delegations[0].validator; @@ -189,12 +175,7 @@ pub fn bond( if mint_steak { // Query the current supply of Steak and compute the amount to mint // let usteak_supply = steak_minted; - let usteak_to_mint = compute_mint_amount( - steak_minted, - amount_to_bond, - &delegations, - &delegations_non_active, - ); + let usteak_to_mint = compute_mint_amount(steak_minted, amount_to_bond, &delegations_all); state.steak_minted.save(deps.storage, &(steak_minted + usteak_to_mint))?; // TODO deal with multiple token returns state.prev_denom.save( @@ -583,20 +564,20 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { for v in validators.iter() { validators_active.remove(v); } - let active_validator_list = Vec::from_iter(validators_active); + // let active_validator_list = Vec::from_iter(validators_active); // for unbonding we still need to look at // TODO verify denom let delegations = query_all_delegations(&deps.querier, &env.contract.address)?; - let delegations_active = - query_delegations(&deps.querier, &active_validator_list, &env.contract.address, &denom)?; + // let delegations_active = + // query_delegations(&deps.querier, &active_validator_list, &env.contract.address, &denom)?; // let usteak_supply = query_cw20_total_supply(&deps.querier, &steak_token)?; let amount_to_unbond = compute_unbond_amount( usteak_supply, pending_batch.usteak_to_burn, &delegations, - &delegations_active, + // &delegations_active, ); let new_undelegations = compute_undelegations(amount_to_unbond, &delegations, &denom); @@ -846,17 +827,14 @@ pub fn withdraw_unbonded( pub fn rebalance(deps: DepsMut, env: Env, minimum: Uint128) -> StdResult { let state = State::default(); let denom = state.denom.load(deps.storage)?; - let mut validators: Vec = Default::default(); - for v in VALIDATORS.items(deps.storage, None, None, Order::Ascending) { - validators.push(v?) - } + + let delegations = query_all_delegations(&deps.querier, &env.contract.address)?; + let mut validators_active: Vec = Default::default(); for v in VALIDATORS_ACTIVE.items(deps.storage, None, None, Order::Ascending) { validators_active.push(v?); } - let delegations = query_delegations(&deps.querier, &validators, &env.contract.address, &denom)?; - let new_redelegations = compute_redelegations_for_rebalancing(validators_active, &delegations, minimum); diff --git a/contracts/hub-tf/src/math.rs b/contracts/hub-tf/src/math.rs index 45bed74..1e28b97 100644 --- a/contracts/hub-tf/src/math.rs +++ b/contracts/hub-tf/src/math.rs @@ -15,12 +15,12 @@ use crate::types::{Delegation, Redelegation, Undelegation}; pub(crate) fn compute_mint_amount( usteak_supply: Uint128, native_to_bond: Uint128, - current_delegations: &[Delegation], - inactive_delegations: &[Delegation], + all_delegations: &[Delegation], + // inactive_delegations: &[Delegation], ) -> Uint128 { - let native_bonded_c: u128 = current_delegations.iter().map(|d| d.amount).sum(); - let native_bonded_inactive: u128 = inactive_delegations.iter().map(|d| d.amount).sum(); - let native_bonded = native_bonded_c + native_bonded_inactive; + let native_bonded: u128 = all_delegations.iter().map(|d| d.amount).sum(); + // let native_bonded_inactive: u128 = inactive_delegations.iter().map(|d| d.amount).sum(); + // let native_bonded = native_bonded_c + native_bonded_inactive; if native_bonded == 0 { native_to_bond } else { @@ -35,12 +35,12 @@ pub(crate) fn compute_mint_amount( pub(crate) fn compute_unbond_amount( usteak_supply: Uint128, usteak_to_burn: Uint128, - current_delegations: &[Delegation], - active_delegations: &[Delegation], + all_delegations: &[Delegation], + //active_delegations: &[Delegation], ) -> Uint128 { - let native_bonded_c: u128 = current_delegations.iter().map(|d| d.amount).sum(); - let native_bonded_a: u128 = active_delegations.iter().map(|d| d.amount).sum(); - let native_bonded = native_bonded_c + native_bonded_a; + let native_bonded: u128 = all_delegations.iter().map(|d| d.amount).sum(); + // let native_bonded_a: u128 = active_delegations.iter().map(|d| d.amount).sum(); + // let native_bonded = native_bonded_c + native_bonded_a; Uint128::new(native_bonded).multiply_ratio(usteak_to_burn, usteak_supply) } diff --git a/contracts/hub-tf/src/testing/tests.rs b/contracts/hub-tf/src/testing/tests.rs index c1eb643..4d35613 100644 --- a/contracts/hub-tf/src/testing/tests.rs +++ b/contracts/hub-tf/src/testing/tests.rs @@ -1827,7 +1827,7 @@ fn dust_return_denom() { ExecuteMsg::ReturnDenom {}, ) .unwrap(); - eprintln!("{:?}", res.messages); + //eprintln!("{:?}", res.messages); assert_eq!(res.messages.len(), 1); assert_eq!(res.messages[0], SubMsg { msg: Delegation::new("charlie", 12345, "uxyz").to_cosmos_msg(), diff --git a/contracts/hub/src/execute.rs b/contracts/hub/src/execute.rs index 7df505f..4b181a5 100644 --- a/contracts/hub/src/execute.rs +++ b/contracts/hub/src/execute.rs @@ -18,8 +18,8 @@ use pfc_steak::{ use crate::{ contract::{REPLY_INSTANTIATE_TOKEN, REPLY_REGISTER_RECEIVED_COINS}, helpers::{ - get_denom_balance, parse_received_fund, query_cw20_total_supply, query_delegation, - query_delegations, + get_denom_balance, parse_received_fund, query_all_delegations, query_cw20_total_supply, + query_delegation, query_delegations, }, math::{ compute_mint_amount, compute_redelegations_for_rebalancing, @@ -29,7 +29,6 @@ use crate::{ state::State, types::{Coins, Delegation, Redelegation}, }; - //-------------------------------------------------------------------------------------------------- // Instantiation //-------------------------------------------------------------------------------------------------- @@ -136,23 +135,11 @@ pub fn bond( let steak_token = state.steak_token.load(deps.storage)?; let validators = state.validators_active.load(deps.storage)?; - let mut validators_wl: HashSet = - HashSet::from_iter(state.validators.load(deps.storage)?); - for v in validators.iter() { - validators_wl.remove(v); - } - let non_active_validator_list = Vec::from_iter(validators_wl); - // Query the current delegations made to validators, and find the validator with the smallest // delegated amount through a linear search // The code for linear search is a bit uglier than using `sort_by` but cheaper: O(n) vs O(n * // log(n)) - let delegations_non_active = query_delegations( - &deps.querier, - &non_active_validator_list, - &env.contract.address, - &denom, - )?; + let all_delegations = query_all_delegations(&deps.querier, &env.contract.address)?; let delegations = query_delegations(&deps.querier, &validators, &env.contract.address, &denom)?; let mut validator = &delegations[0].validator; @@ -171,8 +158,7 @@ pub fn bond( // Query the current supply of Steak and compute the amount to mint let usteak_supply = query_cw20_total_supply(&deps.querier, &steak_token)?; - let usteak_to_mint = - compute_mint_amount(usteak_supply, amount_to_bond, &delegations, &delegations_non_active); + let usteak_to_mint = compute_mint_amount(usteak_supply, amount_to_bond, &all_delegations); state.prev_denom.save( deps.storage, &get_denom_balance(&deps.querier, env.contract.address.clone(), denom.clone())?, @@ -515,20 +501,16 @@ pub fn submit_batch(deps: DepsMut, env: Env) -> StdResult { for v in validators.iter() { validators_active.remove(v); } - let active_validator_list = Vec::from_iter(validators_active); + // let active_validator_list = Vec::from_iter(validators_active); // for unbonding we still need to look at - let delegations = query_delegations(&deps.querier, &validators, &env.contract.address, &denom)?; - let delegations_active = - query_delegations(&deps.querier, &active_validator_list, &env.contract.address, &denom)?; + let delegations = query_all_delegations(&deps.querier, &env.contract.address)?; + // let delegations_active = + // query_delegations(&deps.querier, &active_validator_list, &env.contract.address, &denom)?; let usteak_supply = query_cw20_total_supply(&deps.querier, &steak_token)?; - let amount_to_bond = compute_unbond_amount( - usteak_supply, - pending_batch.usteak_to_burn, - &delegations, - &delegations_active, - ); + let amount_to_bond = + compute_unbond_amount(usteak_supply, pending_batch.usteak_to_burn, &delegations); let new_undelegations = compute_undelegations(amount_to_bond, &delegations, &denom); // NOTE: Regarding the `amount_unclaimed` value @@ -743,10 +725,10 @@ pub fn withdraw_unbonded( pub fn rebalance(deps: DepsMut, env: Env, minimum: Uint128) -> StdResult { let state = State::default(); let denom = state.denom.load(deps.storage)?; - let validators = state.validators.load(deps.storage)?; + //let validators = state.validators.load(deps.storage)?; let validators_active = state.validators_active.load(deps.storage)?; - let delegations = query_delegations(&deps.querier, &validators, &env.contract.address, &denom)?; + let delegations = query_all_delegations(&deps.querier, &env.contract.address)?; let new_redelegations = compute_redelegations_for_rebalancing(validators_active, &delegations, minimum); diff --git a/contracts/hub/src/helpers.rs b/contracts/hub/src/helpers.rs index 3a1de78..9a24a2a 100644 --- a/contracts/hub/src/helpers.rs +++ b/contracts/hub/src/helpers.rs @@ -32,6 +32,7 @@ pub(crate) fn query_delegation( ) -> StdResult { Ok(Delegation { validator: validator.to_string(), + amount: querier .query_delegation(delegator_addr, validator)? .map(|fd| fd.amount.amount.u128()) @@ -52,6 +53,23 @@ pub(crate) fn query_delegations( .map(|validator| query_delegation(querier, validator, delegator_addr, denom)) .collect() } +/// Query the amounts of Luna a staker is delegating to each of the validators specified +pub(crate) fn query_all_delegations( + querier: &QuerierWrapper, + delegator_addr: &Addr, +) -> StdResult> { + let res = querier + .query_all_delegations(delegator_addr)? + .into_iter() + .map(|validator| Delegation { + validator: validator.validator.clone(), + amount: validator.amount.amount.u128(), + denom: validator.amount.denom.clone(), + }) + .collect::>(); + + Ok(res) +} /// `cosmwasm_std::Coin` does not implement `FromStr`, so we have do it ourselves /// diff --git a/contracts/hub/src/math.rs b/contracts/hub/src/math.rs index f26f4b6..681dcf1 100644 --- a/contracts/hub/src/math.rs +++ b/contracts/hub/src/math.rs @@ -15,12 +15,12 @@ use crate::types::{Delegation, Redelegation, Undelegation}; pub(crate) fn compute_mint_amount( usteak_supply: Uint128, native_to_bond: Uint128, - current_delegations: &[Delegation], - inactive_delegations: &[Delegation], + all_delegations: &[Delegation], + // inactive_delegations: &[Delegation], ) -> Uint128 { - let native_bonded_c: u128 = current_delegations.iter().map(|d| d.amount).sum(); - let native_bonded_inactive: u128 = inactive_delegations.iter().map(|d| d.amount).sum(); - let native_bonded = native_bonded_c + native_bonded_inactive; + let native_bonded: u128 = all_delegations.iter().map(|d| d.amount).sum(); + // let native_bonded_inactive: u128 = inactive_delegations.iter().map(|d| d.amount).sum(); + // let native_bonded = native_bonded_c + native_bonded_inactive; if native_bonded == 0 { native_to_bond } else { @@ -35,12 +35,12 @@ pub(crate) fn compute_mint_amount( pub(crate) fn compute_unbond_amount( usteak_supply: Uint128, usteak_to_burn: Uint128, - current_delegations: &[Delegation], - active_delegations: &[Delegation], + all_delegations: &[Delegation], + // active_delegations: &[Delegation], ) -> Uint128 { - let native_bonded_c: u128 = current_delegations.iter().map(|d| d.amount).sum(); - let native_bonded_a: u128 = active_delegations.iter().map(|d| d.amount).sum(); - let native_bonded = native_bonded_c + native_bonded_a; + let native_bonded: u128 = all_delegations.iter().map(|d| d.amount).sum(); + // let native_bonded_a: u128 = active_delegations.iter().map(|d| d.amount).sum(); + // let native_bonded = native_bonded_c + native_bonded_a; Uint128::new(native_bonded).multiply_ratio(usteak_to_burn, usteak_supply) } diff --git a/contracts/hub/src/queries.rs b/contracts/hub/src/queries.rs index 14e8d9f..4ba9591 100644 --- a/contracts/hub/src/queries.rs +++ b/contracts/hub/src/queries.rs @@ -1,7 +1,4 @@ -use std::{ - collections::{BTreeSet, HashSet}, - iter::FromIterator, -}; +use std::{collections::BTreeSet, iter::FromIterator}; use cosmwasm_std::{Addr, Decimal, Deps, Env, Order, StdResult, Uint128}; use cw_storage_plus::{Bound, CwIntKey}; @@ -11,7 +8,7 @@ use pfc_steak::hub::{ }; use crate::{ - helpers::{query_cw20_total_supply, query_delegations}, + helpers::{query_all_delegations, query_cw20_total_supply}, state::State, types::BooleanKey, }; @@ -53,18 +50,10 @@ pub fn config(deps: Deps) -> StdResult { pub fn state(deps: Deps, env: Env) -> StdResult { let state = State::default(); - let denom = state.denom.load(deps.storage)?; let steak_token = state.steak_token.load(deps.storage)?; let total_usteak = query_cw20_total_supply(&deps.querier, &steak_token)?; - let mut validators: HashSet = HashSet::from_iter(state.validators.load(deps.storage)?); - let validators_active: HashSet = - HashSet::from_iter(state.validators_active.load(deps.storage)?); - validators.extend(validators_active); - let validator_vec: Vec = Vec::from_iter(validators); - - let delegations = - query_delegations(&deps.querier, &validator_vec, &env.contract.address, &denom)?; + let delegations = query_all_delegations(&deps.querier, &env.contract.address)?; let total_native: u128 = delegations.iter().map(|d| d.amount).sum(); let exchange_rate = if total_usteak.is_zero() { diff --git a/contracts/token/Cargo.toml b/contracts/token/Cargo.toml index e2af341..4b30c4c 100644 --- a/contracts/token/Cargo.toml +++ b/contracts/token/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pfc-steak-token" -version = "2.0.2" +version = "3.0.20" authors = ["larry ", "PFC "] edition = "2018" license = "GPL-3.0-or-later" @@ -10,9 +10,9 @@ repository = "https://github.com/st4k3h0us3/steak-contracts" crate-type = ["cdylib", "rlib"] [features] -backtraces = ["cosmwasm-std/backtraces"] +# backtraces = ["cosmwasm-std/backtraces"] [dependencies] -cosmwasm-std = { version = "1.5.4" } +cosmwasm-std = { version = "1.5.8" } cw20 = "0.13" cw20-base = { version = "0.13", features = ["library"] } diff --git a/contracts/token/src/lib.rs b/contracts/token/src/lib.rs index f03b7e8..6b29040 100644 --- a/contracts/token/src/lib.rs +++ b/contracts/token/src/lib.rs @@ -1,5 +1,6 @@ use cosmwasm_std::{ Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdError, StdResult, Storage, + entry_point, }; use cw20_base::{ ContractError, @@ -8,7 +9,7 @@ use cw20_base::{ state::{MinterData, TOKEN_INFO}, }; -//#[cfg_attr(not(feature = "library"), entry_point)] +#[entry_point] pub fn instantiate( deps: DepsMut, env: Env, @@ -18,7 +19,7 @@ pub fn instantiate( cw20_instantiate(deps, env, info, msg) } -//#[cfg_attr(not(feature = "library"), entry_point)] +#[entry_point] pub fn execute( deps: DepsMut, env: Env, @@ -56,7 +57,7 @@ fn assert_minter(storage: &dyn Storage, sender: &Addr) -> Result<(), ContractErr Ok(()) } -//#[cfg_attr(not(feature = "library"), entry_point)] +#[entry_point] pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { cw20_query(deps, env, msg) } diff --git a/init/archway_hub_init.json b/init/archway_hub_init.json index ebcdddf..be09847 100644 --- a/init/archway_hub_init.json +++ b/init/archway_hub_init.json @@ -1,5 +1,5 @@ { - "cw20_code_id": 446, + "cw20_code_id": 701, "owner": "archway1lu92zj8q6cmrptu09rp3343x9969r9qrg72hzj", "name": "boneARCH Token", "symbol": "boneARCH", @@ -7,7 +7,9 @@ "epoch_period": 259200, "unbond_period": 1814400, "validators": [ - "archwayvaloper1cz6unq70w0sdm2gwghv8m4uqmhwxz5x4adam88" + "archwayvaloper1hgzw3udgagqylcvp9kyqq2w7ws8enucqpw094n", + "archwayvaloper140l6y2gp3gxvay6qtn70re7z2s0gn57zekxzpl", + "archwayvaloper1p4n7pw4rdfyn3rgc4cndcqedwmcu3dt6kfdwpt" ], "denom": "aarch", "fee_account": "archway1pzjprqczpn0fvn5kvqzs74mggg4539rt2vpmp07ffp3pufp6cc3qqmhdg3", diff --git a/justfile b/justfile index 16aa04d..d73be2f 100644 --- a/justfile +++ b/justfile @@ -11,18 +11,6 @@ test: cargo test optimize: - if [[ $(uname -m) =~ "arm64" ]]; then \ - just optimize-arm; else \ - just optimize-x86; fi - -optimize-arm: - docker run --rm -v "$(pwd)":/code \ - --mount type=volume,source="$(basename "$(pwd)")_cache",target=/target \ - --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ - --platform linux/arm64 \ - cosmwasm/optimizer-arm64:0.16.0 - -optimize-x86: docker run --rm -v "$(pwd)":/code \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \