From d7a652e4d8cb9f680ed45d4b6300c2ca5dd52162 Mon Sep 17 00:00:00 2001 From: Abhishek-1857 Date: Fri, 28 Jun 2024 18:41:37 +0530 Subject: [PATCH] permit check added for key to be dao core addr --- contracts/dao-dao-core/src/contract.rs | 6 +- .../cw-fund-distributor/src/contract.rs | 81 +++-- .../external/cw-fund-distributor/src/state.rs | 1 + contracts/external/cw4-group/src/contract.rs | 6 +- contracts/external/cw4-group/src/state.rs | 1 - .../external/snip721-roles/src/contract.rs | 7 +- contracts/external/snip721-roles/src/state.rs | 1 + .../dao-proposal-multiple/src/contract.rs | 4 + .../dao-proposal-single/src/contract.rs | 4 + .../src/contract.rs | 128 ++++--- .../snip20-stake-external-rewards/src/msg.rs | 11 +- .../src/state.rs | 2 - .../staking/snip20-stake/src/contract.rs | 15 +- contracts/staking/snip20-stake/src/msg.rs | 4 +- contracts/staking/snip20-stake/src/state.rs | 1 - contracts/staking/snip20-stake/src/tests.rs | 123 ++++++- .../dao-voting-snip20-staked/src/tests.rs | 53 ++- .../dao-voting-snip721-staked/src/contract.rs | 2 +- .../dao-voting-token-staked/src/contract.rs | 14 +- .../voting/dao-voting-token-staked/src/msg.rs | 4 +- .../src/tests/multitest/tests.rs | 316 ++++++++++++++++-- packages/dao-hooks/src/stake.rs | 19 +- 22 files changed, 599 insertions(+), 204 deletions(-) diff --git a/contracts/dao-dao-core/src/contract.rs b/contracts/dao-dao-core/src/contract.rs index d3a406d..d75db90 100644 --- a/contracts/dao-dao-core/src/contract.rs +++ b/contracts/dao-dao-core/src/contract.rs @@ -1135,9 +1135,9 @@ pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result Result { set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + DAO.save(deps.storage, &info.sender.to_string())?; // store the height DISTRIBUTION_HEIGHT.save(deps.storage, &msg.distribution_height)?; @@ -107,9 +108,13 @@ pub fn execute( memo: _, }) => execute_fund_snip20(deps, env, info.sender, amount), ExecuteMsg::FundNative {} => execute_fund_native(deps, env, info), - ExecuteMsg::ClaimSnip20 { auth, tokens } => execute_claim_snip20s(deps, env, auth, tokens), - ExecuteMsg::ClaimNatives { auth, denoms } => execute_claim_natives(deps, env, auth, denoms), - ExecuteMsg::ClaimAll { auth } => execute_claim_all(deps, env, auth), + ExecuteMsg::ClaimSnip20 { auth, tokens } => { + execute_claim_snip20s(deps, env, info.sender, auth, tokens) + } + ExecuteMsg::ClaimNatives { auth, denoms } => { + execute_claim_natives(deps, env, auth, info.sender, denoms) + } + ExecuteMsg::ClaimAll { auth } => execute_claim_all(deps, env, info.sender, auth), ExecuteMsg::SetSnip20sCodeHash { token_info } => { if info.sender != CONFIG.load(deps.storage)?.owner { return Err(ContractError::Unauthorized {}); @@ -226,6 +231,7 @@ fn get_relative_share(deps: &Deps, auth: Auth) -> Result { pub fn execute_claim_snip20s( deps: DepsMut, env: Env, + sender: Addr, auth: Auth, tokens: Vec, ) -> Result { @@ -239,10 +245,6 @@ pub fn execute_claim_snip20s( } let relative_share = get_relative_share(&deps.as_ref(), auth.clone())?; - let mut sender = Addr::unchecked(""); - if let Auth::ViewingKey { address, .. } = auth { - sender = deps.api.addr_validate(&address)?; - } let messages = get_snip20_claim_wasm_messages(tokens, deps, sender.clone(), relative_share)?; Ok(Response::default() @@ -320,6 +322,7 @@ pub fn execute_claim_natives( deps: DepsMut, env: Env, auth: Auth, + sender: Addr, denoms: Vec, ) -> Result { let funding_deadline = FUNDING_PERIOD_EXPIRATION.load(deps.storage)?; @@ -334,10 +337,6 @@ pub fn execute_claim_natives( // find the relative share of the distributor pool for the user // and determine the native claim transfer amounts with it let relative_share = get_relative_share(&deps.as_ref(), auth.clone())?; - let mut sender = Addr::unchecked(""); - if let Auth::ViewingKey { address, .. } = auth { - sender = deps.api.addr_validate(&address)?; - } let messages = get_native_claim_bank_messages(denoms, deps, sender.clone(), relative_share)?; Ok(Response::default() @@ -399,6 +398,7 @@ fn get_native_claim_bank_messages( pub fn execute_claim_all( mut deps: DepsMut, env: Env, + sender: Addr, auth: Auth, ) -> Result { let funding_deadline = FUNDING_PERIOD_EXPIRATION.load(deps.storage)?; @@ -429,11 +429,6 @@ pub fn execute_claim_all( let relative_share = get_relative_share(&deps.as_ref(), auth.clone())?; - let mut sender = Addr::unchecked(""); - if let Auth::ViewingKey { address, .. } = auth { - sender = deps.api.addr_validate(&address)?; - } - // get the claim messages let cw20_claim_msgs = get_snip20_claim_wasm_messages(snip20_info, deps.branch(), sender.clone(), relative_share)?; @@ -455,13 +450,13 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { QueryMsg::Snip20Tokens {} => query_snip20_tokens(deps), QueryMsg::NativeEntitlement { auth, denom } => { let query_auth = CONFIG.load(deps.storage)?.query_auth; - authenticate(deps, auth.clone(), query_auth)?; - query_native_entitlement(deps, auth, denom) + let sender = authenticate(deps, auth.clone(), query_auth)?; + query_native_entitlement(deps, auth, sender, denom) } QueryMsg::Snip20Entitlement { auth, token } => { let query_auth = CONFIG.load(deps.storage)?.query_auth; - authenticate(deps, auth.clone(), query_auth)?; - query_snip20_entitlement(deps, auth, token) + let sender = authenticate(deps, auth.clone(), query_auth)?; + query_snip20_entitlement(deps, auth, sender, token) } QueryMsg::NativeEntitlements { auth, @@ -469,8 +464,8 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { limit, } => { let query_auth = CONFIG.load(deps.storage)?.query_auth; - authenticate(deps, auth.clone(), query_auth)?; - query_native_entitlements(deps, auth, start_at, limit) + let sender = authenticate(deps, auth.clone(), query_auth)?; + query_native_entitlements(deps, auth, sender, start_at, limit) } QueryMsg::Snip20Entitlements { auth, @@ -478,8 +473,8 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { limit, } => { let query_auth = CONFIG.load(deps.storage)?.query_auth; - authenticate(deps, auth.clone(), query_auth)?; - query_snip20_entitlements(deps, auth, start_at, limit) + let sender = authenticate(deps, auth.clone(), query_auth)?; + query_snip20_entitlements(deps, auth, sender, start_at, limit) } } } @@ -528,11 +523,12 @@ pub fn query_snip20_tokens(deps: Deps) -> StdResult { to_binary(&cw20_responses) } -pub fn query_native_entitlement(deps: Deps, auth: Auth, denom: String) -> StdResult { - let mut sender = Addr::unchecked(""); - if let Auth::ViewingKey { address, .. } = auth.clone() { - sender = deps.api.addr_validate(&address)?; - } +pub fn query_native_entitlement( + deps: Deps, + auth: Auth, + sender: Addr, + denom: String, +) -> StdResult { let prev_claim = NATIVE_CLAIMS .get(deps.storage, &(sender.clone(), denom.clone())) .unwrap_or_default(); @@ -551,11 +547,12 @@ pub fn query_native_entitlement(deps: Deps, auth: Auth, denom: String) -> StdRes }) } -pub fn query_snip20_entitlement(deps: Deps, auth: Auth, token: String) -> StdResult { - let mut sender = Addr::unchecked(""); - if let Auth::ViewingKey { address, .. } = auth.clone() { - sender = deps.api.addr_validate(&address)?; - } +pub fn query_snip20_entitlement( + deps: Deps, + auth: Auth, + sender: Addr, + token: String, +) -> StdResult { let token = Addr::unchecked(token); let prev_claim = SNIP20_CLAIMS @@ -579,13 +576,10 @@ pub fn query_snip20_entitlement(deps: Deps, auth: Auth, token: String) -> StdRes pub fn query_native_entitlements( deps: Deps, auth: Auth, + sender: Addr, start_at: Option, limit: Option, ) -> StdResult { - let mut sender = Addr::unchecked(""); - if let Auth::ViewingKey { address, .. } = auth.clone() { - sender = deps.api.addr_validate(&address)?; - } let relative_share = get_relative_share(&deps, auth)?; let mut start = start_at.clone(); // Clone start_after to mutate it if necessary let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize; @@ -634,13 +628,10 @@ const DEFAULT_LIMIT: u32 = 10; pub fn query_snip20_entitlements( deps: Deps, auth: Auth, + sender: Addr, start_at: Option, limit: Option, ) -> StdResult { - let mut sender = Addr::unchecked(""); - if let Auth::ViewingKey { address, .. } = auth.clone() { - sender = deps.api.addr_validate(&address)?; - } let relative_share = get_relative_share(&deps, auth)?; let mut start = start_at.map(|h| deps.api.addr_validate(&h)).transpose()?; let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize; @@ -764,6 +755,10 @@ pub fn authenticate(deps: Deps, auth: Auth, query_auth: Contract) -> StdResult { + let dao = DAO.load(deps.storage)?; + if permit.params.key != dao { + return Err(StdError::generic_err("Invalid permit Key")); + } let res: PermitAuthentication = authenticate_permit(permit, &deps.querier, query_auth)?; if res.revoked { diff --git a/contracts/external/cw-fund-distributor/src/state.rs b/contracts/external/cw-fund-distributor/src/state.rs index bcc3721..6f3b99a 100644 --- a/contracts/external/cw-fund-distributor/src/state.rs +++ b/contracts/external/cw-fund-distributor/src/state.rs @@ -40,3 +40,4 @@ pub static NATIVE_CLAIMS: Keymap<(Addr, String), Uint128> = Keymap::new(b"native pub static SNIP20S_CODE_HASH: Keymap = Keymap::new(b"snip20s_code_hash"); pub const CONFIG: Item = Item::new("config"); +pub const DAO: Item = Item::new("dao"); diff --git a/contracts/external/cw4-group/src/contract.rs b/contracts/external/cw4-group/src/contract.rs index c569c55..8f4bf2e 100644 --- a/contracts/external/cw4-group/src/contract.rs +++ b/contracts/external/cw4-group/src/contract.rs @@ -21,7 +21,9 @@ use shade_protocol::Contract; use crate::error::ContractError; use crate::helpers::validate_unique_members; use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; -use crate::state::{MembersStore, TotalStore, ADMIN, DAO, HOOKS, MEMBERS_PRIMARY, OWNER, QUERY_AUTH}; +use crate::state::{ + MembersStore, TotalStore, ADMIN, DAO, HOOKS, MEMBERS_PRIMARY, OWNER, QUERY_AUTH, +}; // version info for migration info const CONTRACT_NAME: &str = "crates.io:cw4-group"; @@ -288,7 +290,7 @@ pub fn authenticate(deps: Deps, auth: Auth, query_auth: Contract) -> StdResult { - if permit.params.key!=DAO.load(deps.storage)?{ + if permit.params.key != DAO.load(deps.storage)? { return Err(StdError::generic_err("Invalid permit Key")); } let res: PermitAuthentication = diff --git a/contracts/external/cw4-group/src/state.rs b/contracts/external/cw4-group/src/state.rs index 5c9a8f6..8e79def 100644 --- a/contracts/external/cw4-group/src/state.rs +++ b/contracts/external/cw4-group/src/state.rs @@ -14,7 +14,6 @@ pub const QUERY_AUTH: Item = Item::new("query_auth"); pub const OWNER: Item = Item::new("owner"); pub const DAO: Item = Item::new("dao"); - // pub const TOTAL: SnapshotItem = SnapshotItem::new( // TOTAL_KEY, // TOTAL_KEY_CHECKPOINTS, diff --git a/contracts/external/snip721-roles/src/contract.rs b/contracts/external/snip721-roles/src/contract.rs index aa96a7b..20caf5e 100644 --- a/contracts/external/snip721-roles/src/contract.rs +++ b/contracts/external/snip721-roles/src/contract.rs @@ -23,7 +23,7 @@ use snip721_roles_impl::{ use std::cmp::Ordering; use crate::msg::{ExecuteMsg, QueryMsg}; -use crate::state::{MembersStore, TotalStore, MEMBERS_PRIMARY}; +use crate::state::{MembersStore, TotalStore, DAO, MEMBERS_PRIMARY}; use crate::{error::RolesContractError as ContractError, state::HOOKS}; // Version info for migration @@ -44,6 +44,7 @@ pub fn instantiate( msg: Snip721BaseInstantiateMsg, ) -> Result { Snip721roles::default().instantiate(deps.branch(), env.clone(), info.clone(), msg)?; + DAO.save(deps.storage, &info.sender.to_string())?; // Initialize total weight to zero TotalStore::save(deps.storage, env.block.height, 0)?; @@ -683,6 +684,10 @@ pub fn authenticate(deps: Deps, auth: Auth, query_auth: Contract) -> StdResult { + let dao = DAO.load(deps.storage)?; + if permit.params.key != dao { + return Err(StdError::generic_err("Invalid permit Key")); + } let res: PermitAuthentication = authenticate_permit(permit, &deps.querier, query_auth)?; if res.revoked { diff --git a/contracts/external/snip721-roles/src/state.rs b/contracts/external/snip721-roles/src/state.rs index 59ba888..492f938 100644 --- a/contracts/external/snip721-roles/src/state.rs +++ b/contracts/external/snip721-roles/src/state.rs @@ -14,6 +14,7 @@ pub struct Config { // Hooks to contracts that will receive staking and unstaking messages. pub const HOOKS: Hooks = Hooks::new("hooks"); pub const SNIP721_INFO: Item = Item::new("si"); +pub const DAO: Item = Item::new("dao"); // /// A historic snapshot of total weight over time // pub const TOTAL: SnapshotItem = SnapshotItem::new( diff --git a/contracts/proposal/dao-proposal-multiple/src/contract.rs b/contracts/proposal/dao-proposal-multiple/src/contract.rs index 5e7935d..a9416e8 100644 --- a/contracts/proposal/dao-proposal-multiple/src/contract.rs +++ b/contracts/proposal/dao-proposal-multiple/src/contract.rs @@ -950,6 +950,10 @@ pub fn authenticate(deps: Deps, auth: Auth, query_auth: Contract) -> StdResult { + let dao = DAO.load(deps.storage)?.addr.to_string(); + if permit.params.key != dao { + return Err(StdError::generic_err("Invalid permit Key")); + } let res: PermitAuthentication = authenticate_permit(permit, &deps.querier, query_auth)?; if res.revoked { diff --git a/contracts/proposal/dao-proposal-single/src/contract.rs b/contracts/proposal/dao-proposal-single/src/contract.rs index 666bc32..fb80874 100644 --- a/contracts/proposal/dao-proposal-single/src/contract.rs +++ b/contracts/proposal/dao-proposal-single/src/contract.rs @@ -941,6 +941,10 @@ pub fn authenticate(deps: Deps, auth: Auth, query_auth: Contract) -> StdResult { + let dao = DAO.load(deps.storage)?.addr.to_string(); + if permit.params.key != dao { + return Err(StdError::generic_err("Invalid permit Key")); + } let res: PermitAuthentication = authenticate_permit(permit, &deps.querier, query_auth)?; if res.revoked { diff --git a/contracts/staking/snip20-stake-external-rewards/src/contract.rs b/contracts/staking/snip20-stake-external-rewards/src/contract.rs index b398ef4..2866d2d 100644 --- a/contracts/staking/snip20-stake-external-rewards/src/contract.rs +++ b/contracts/staking/snip20-stake-external-rewards/src/contract.rs @@ -4,7 +4,7 @@ use crate::msg::{ }; use crate::state::{ Config, Denom, RewardConfig, CONFIG, LAST_UPDATE_BLOCK, PENDING_REWARDS, REWARD_CONFIG, - REWARD_PER_TOKEN, USER_REWARD_PER_TOKEN, VIEWING_KEY_INFO, + REWARD_PER_TOKEN, USER_REWARD_PER_TOKEN, }; use crate::ContractError; use crate::ContractError::{ @@ -121,7 +121,6 @@ pub fn execute( execute_update_reward_duration(deps, env, info, new_duration) } ExecuteMsg::UpdateOwnership(action) => execute_update_owner(deps, info, env, action), - ExecuteMsg::SetViewingKey { key } => try_set_viewing_key(deps, info, key), } } @@ -138,14 +137,7 @@ pub fn execute_receive( return Err(InvalidSnip20 {}); }; match msg { - ReceiveMsg::Fund {} => { - let key = VIEWING_KEY_INFO.load(deps.storage, sender.clone())?; - let auth = Auth::ViewingKey { - key, - address: sender.to_string(), - }; - execute_fund(deps, env, auth, wrapper.amount) - } + ReceiveMsg::Fund { auth } => execute_fund(deps, env, auth, sender, wrapper.amount), } } @@ -160,7 +152,7 @@ pub fn execute_fund_native( match config.reward_token { Denom::Native(denom) => { let amount = secret_utils::must_pay(&info, &denom).map_err(|_| InvalidFunds {})?; - execute_fund(deps, env, auth, amount) + execute_fund(deps, env, auth, info.sender, amount) } Snip20(_) => Err(InvalidFunds {}), } @@ -170,16 +162,12 @@ pub fn execute_fund( mut deps: DepsMut, env: Env, auth: Auth, + sender: Addr, amount: Uint128, ) -> Result, ContractError> { - let mut user = String::new(); - if let Auth::ViewingKey { address, .. } = auth.clone() { - user = address; - } - let sender = deps.api.addr_validate(&user)?; cw_ownable::assert_owner(deps.storage, &sender)?; - update_rewards(&mut deps, &env, auth)?; + update_rewards(&mut deps, &env, auth, &sender)?; let reward_config = REWARD_CONFIG.load(deps.storage)?; if reward_config.period_finish > env.block.height { return Err(RewardPeriodNotFinished {}); @@ -219,22 +207,8 @@ pub fn execute_stake_changed( return Err(ContractError::InvalidHookSender {}); }; match msg { - StakeChangedHookMsg::Stake { addr, .. } => { - let key = VIEWING_KEY_INFO.load(deps.storage, addr.clone())?; - let auth = Auth::ViewingKey { - key, - address: addr.to_string(), - }; - execute_stake(deps, env, auth) - } - StakeChangedHookMsg::Unstake { addr, .. } => { - let key = VIEWING_KEY_INFO.load(deps.storage, addr.clone())?; - let auth = Auth::ViewingKey { - key, - address: addr.to_string(), - }; - execute_unstake(deps, env, auth) - } + StakeChangedHookMsg::Stake { addr, auth, .. } => execute_stake(deps, env, auth, addr), + StakeChangedHookMsg::Unstake { addr, auth, .. } => execute_unstake(deps, env, auth, addr), } } @@ -242,8 +216,9 @@ pub fn execute_stake( mut deps: DepsMut, env: Env, auth: Auth, + addr: Addr, ) -> Result, ContractError> { - update_rewards(&mut deps, &env, auth)?; + update_rewards(&mut deps, &env, auth, &addr)?; Ok(Response::new().add_attribute("action", "stake")) } @@ -251,8 +226,9 @@ pub fn execute_unstake( mut deps: DepsMut, env: Env, auth: Auth, + addr: Addr, ) -> Result, ContractError> { - update_rewards(&mut deps, &env, auth)?; + update_rewards(&mut deps, &env, auth, &addr)?; Ok(Response::new().add_attribute("action", "unstake")) } @@ -262,7 +238,7 @@ pub fn execute_claim( info: MessageInfo, auth: Auth, ) -> Result, ContractError> { - update_rewards(&mut deps, &env, auth)?; + update_rewards(&mut deps, &env, auth, &info.sender)?; let rewards = PENDING_REWARDS .load(deps.storage, info.sender.clone()) .map_err(|_| NoRewardsClaimable {})?; @@ -293,15 +269,6 @@ pub fn execute_update_owner( Ok(Response::default().add_attributes(ownership.into_attributes())) } -pub fn try_set_viewing_key( - deps: DepsMut, - info: MessageInfo, - key: String, -) -> Result { - VIEWING_KEY_INFO.save(deps.storage, info.sender, &key)?; - Ok(Response::default().add_attribute("action", "set_viewing_key")) -} - pub fn get_transfer_msg( recipient: Addr, amount: Uint128, @@ -332,7 +299,7 @@ pub fn get_transfer_msg( } } -pub fn update_rewards(deps: &mut DepsMut, env: &Env, auth: Auth) -> StdResult<()> { +pub fn update_rewards(deps: &mut DepsMut, env: &Env, auth: Auth, addr: &Addr) -> StdResult<()> { let config = CONFIG.load(deps.storage)?; let reward_per_token = get_reward_per_token( deps.as_ref(), @@ -347,14 +314,9 @@ pub fn update_rewards(deps: &mut DepsMut, env: &Env, auth: Auth) -> StdResult<() reward_per_token, &config.staking_contract, auth.clone(), + addr, )?; - let mut user = String::new(); - if let Auth::ViewingKey { address, .. } = auth { - user = address - } - let addr = deps.api.addr_validate(&user)?; - PENDING_REWARDS.update::<_, StdError>(deps.storage, addr.clone(), |r| { Ok(r.unwrap_or_default() + earned_rewards) })?; @@ -399,6 +361,7 @@ pub fn get_rewards_earned( reward_per_token: Uint256, staking_contract: &Addr, auth: Auth, + addr: &Addr, ) -> StdResult { let config = CONFIG.load(deps.storage)?; let staked_balance = Uint256::from(get_staked_balance( @@ -407,11 +370,6 @@ pub fn get_rewards_earned( config.staking_contract_code_hash, auth.clone(), )?); - let mut user = String::new(); - if let Auth::ViewingKey { address, .. } = auth { - user = address - } - let addr = deps.api.addr_validate(&user)?; let user_reward_per_token = USER_REWARD_PER_TOKEN .load(deps.storage, addr.clone()) .unwrap_or_default(); @@ -489,8 +447,8 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { QueryMsg::Info {} => Ok(to_binary(&query_info(deps, env)?)?), QueryMsg::Ownership {} => to_binary(&cw_ownable::get_ownership(deps.storage)?), - QueryMsg::GetPendingRewards { auth } => { - Ok(to_binary(&query_pending_rewards(deps, env, *auth)?)?) + QueryMsg::GetPendingRewards { auth, addr } => { + Ok(to_binary(&query_pending_rewards(deps, env, *auth, addr)?)?) } } } @@ -505,6 +463,7 @@ pub fn query_pending_rewards( deps: Deps, env: Env, auth: Auth, + addr: Addr, ) -> StdResult { let config = CONFIG.load(deps.storage)?; let reward_per_token = get_reward_per_token( @@ -518,14 +477,9 @@ pub fn query_pending_rewards( reward_per_token, &config.staking_contract, auth.clone(), + &addr, )?; - let mut user = String::new(); - if let Auth::ViewingKey { address, .. } = auth { - user = address - } - let addr = deps.api.addr_validate(&user)?; - let existing_rewards = PENDING_REWARDS .load(deps.storage, addr.clone()) .unwrap_or_default(); @@ -684,12 +638,13 @@ mod tests { snip20_code_hash: String, sender: T, amount: u128, + auth: Auth, ) { let msg = Snip20ExecuteMsg::Send { recipient: staking_addr.to_string(), recipient_code_hash: Some(staking_code_hash), amount: Uint128::new(amount), - msg: Some(to_binary(&snip20_stake::msg::ReceiveMsg::Stake {}).unwrap()), + msg: Some(to_binary(&snip20_stake::msg::ReceiveMsg::Stake { auth }).unwrap()), memo: None, decoys: None, entropy: None, @@ -748,8 +703,15 @@ mod tests { viewing_key } - fn unstake_tokens(app: &mut App, staking_info: &ContractInfo, address: &str, amount: u128) { + fn unstake_tokens( + app: &mut App, + staking_info: &ContractInfo, + address: &str, + amount: u128, + auth: Auth, + ) { let msg = snip20_stake::msg::ExecuteMsg::Unstake { + auth, amount: Uint128::new(amount), }; app.execute_contract(Addr::unchecked(address), staking_info, &msg, &[]) @@ -778,14 +740,20 @@ mod tests { ); app.update_block(next_block); for coin in initial_balances { + let info = mock_info(&coin.address, &[]); + let viewing_key = create_viewing_key(app, query_auth_info.clone(), info.clone()); stake_tokens( app, &staking_info.clone().address, staking_info.clone().code_hash, &snip20_info.clone().address, snip20_info.clone().code_hash, - coin.address, + coin.address.clone(), coin.amount.u128(), + Auth::ViewingKey { + key: viewing_key.clone(), + address: coin.address, + }, ); } (staking_info, snip20_info, query_auth_info) @@ -860,6 +828,7 @@ mod tests { app: &mut App, reward_contract_info: ContractInfo, auth: Auth, + addr: Addr, expected: u128, ) { let res: PendingRewardsResponse = app @@ -870,6 +839,7 @@ mod tests { reward_contract_info.address.to_string(), &QueryMsg::GetPendingRewards { auth: Box::new(auth), + addr, }, ) .unwrap(); @@ -893,8 +863,9 @@ mod tests { snip20_info: ContractInfo, reward_contract_info: ContractInfo, amount: u128, + auth: Auth, ) { - let fund_sub_msg = to_binary(&ReceiveMsg::Fund {}).unwrap(); + let fund_sub_msg = to_binary(&ReceiveMsg::Fund { auth }).unwrap(); let fund_msg = Snip20ExecuteMsg::Send { recipient: reward_contract_info.address.clone().into_string(), recipient_code_hash: Some(reward_contract_info.clone().code_hash), @@ -1548,7 +1519,13 @@ mod tests { amount: Uint128::new(500000000), }], ); - let fund_sub_msg = to_binary(&ReceiveMsg::Fund {}).unwrap(); + let fund_sub_msg = to_binary(&ReceiveMsg::Fund { + auth: Auth::ViewingKey { + key: viewing_key_admin.clone(), + address: OWNER.to_string(), + }, + }) + .unwrap(); let fund_msg = snip20_reference_impl::msg::ExecuteMsg::Send { recipient: reward_contract_info.clone().address.into_string(), recipient_code_hash: Some(reward_contract_info.clone().code_hash), @@ -1612,7 +1589,13 @@ mod tests { app.borrow_mut().update_block(|b| b.height = 1000); // Test with invalid token - let fund_sub_msg = to_binary(&ReceiveMsg::Fund {}).unwrap(); + let fund_sub_msg = to_binary(&ReceiveMsg::Fund { + auth: Auth::ViewingKey { + key: viewing_key_admin.clone(), + address: OWNER.to_string(), + }, + }) + .unwrap(); let fund_msg = snip20_reference_impl::msg::ExecuteMsg::Send { recipient: reward_contract_info.clone().address.into_string(), recipient_code_hash: Some(reward_contract_info.clone().code_hash), @@ -1751,6 +1734,7 @@ mod tests { key: viewing_key_addr1, address: ADDR1.to_string(), }, + Addr::unchecked(ADDR1), 5, ); assert_pending_rewards( @@ -1760,6 +1744,7 @@ mod tests { key: viewing_key_addr2, address: ADDR2.to_string(), }, + Addr::unchecked(ADDR2), 2, ); assert_pending_rewards( @@ -1769,6 +1754,7 @@ mod tests { key: viewing_key_addr3, address: ADDR3.to_string(), }, + Addr::unchecked(ADDR3), 2, ); } diff --git a/contracts/staking/snip20-stake-external-rewards/src/msg.rs b/contracts/staking/snip20-stake-external-rewards/src/msg.rs index 03b7a2e..f106592 100644 --- a/contracts/staking/snip20-stake-external-rewards/src/msg.rs +++ b/contracts/staking/snip20-stake-external-rewards/src/msg.rs @@ -43,11 +43,6 @@ pub enum ExecuteMsg { Receive(Snip20ReceiveMsg), Fund { auth: Auth }, UpdateRewardDuration { new_duration: u64 }, - // User viewing key for snip20 token. - // user need to create viewing key for snip20 token in token - // contract and set that viewing key here for further use - // like checking user token balance etc. - SetViewingKey { key: String }, } #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, JsonSchema, Debug)] @@ -60,9 +55,9 @@ pub enum MigrateMsg { FromV1 {}, } -#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, JsonSchema, Debug)] +#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] pub enum ReceiveMsg { - Fund {}, + Fund { auth: Auth }, } #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug, QueryResponses)] @@ -71,7 +66,7 @@ pub enum QueryMsg { #[returns(InfoResponse)] Info {}, #[returns(PendingRewardsResponse)] - GetPendingRewards { auth: Box }, + GetPendingRewards { auth: Box, addr: Addr }, #[returns(::cw_ownable::Ownership<::cosmwasm_std::Addr>)] Ownership {}, } diff --git a/contracts/staking/snip20-stake-external-rewards/src/state.rs b/contracts/staking/snip20-stake-external-rewards/src/state.rs index 80df6e2..9af3b18 100644 --- a/contracts/staking/snip20-stake-external-rewards/src/state.rs +++ b/contracts/staking/snip20-stake-external-rewards/src/state.rs @@ -35,5 +35,3 @@ pub const LAST_UPDATE_BLOCK: Item = Item::new("last_update_block"); pub const PENDING_REWARDS: Map = Map::new("pending_rewards"); pub const USER_REWARD_PER_TOKEN: Map = Map::new("user_reward_per_token"); - -pub const VIEWING_KEY_INFO: Map = Map::new("viewing_key_info"); diff --git a/contracts/staking/snip20-stake/src/contract.rs b/contracts/staking/snip20-stake/src/contract.rs index c747dec..416b754 100644 --- a/contracts/staking/snip20-stake/src/contract.rs +++ b/contracts/staking/snip20-stake/src/contract.rs @@ -6,7 +6,8 @@ use crate::msg::{ TotalStakedAtHeightResponse, TotalValueResponse, }; use crate::state::{ - Config, StakedBalancesStore, StakedTotalStore, BALANCE, CLAIMS, CONFIG, DAO, HOOKS, MAX_CLAIMS, STAKED_BALANCES_PRIMARY + Config, StakedBalancesStore, StakedTotalStore, BALANCE, CLAIMS, CONFIG, DAO, HOOKS, MAX_CLAIMS, + STAKED_BALANCES_PRIMARY, }; use crate::ContractError; use cosmwasm_std::{ @@ -95,7 +96,7 @@ pub fn execute( ) -> Result, ContractError> { match msg { ExecuteMsg::Receive(msg) => execute_receive(deps, env, info, msg), - ExecuteMsg::Unstake { amount } => execute_unstake(deps, env, info, amount), + ExecuteMsg::Unstake { amount, auth } => execute_unstake(deps, env, info, amount, auth), ExecuteMsg::Claim {} => execute_claim(deps, env, info), ExecuteMsg::UpdateConfig { duration } => execute_update_config(info, deps, duration), ExecuteMsg::AddHook { addr, code_hash } => { @@ -147,7 +148,7 @@ pub fn execute_receive( let msg: ReceiveMsg = from_binary(&wrapper.msg.unwrap())?; let sender: Addr = deps.api.addr_validate(wrapper.sender.as_ref())?; match msg { - ReceiveMsg::Stake {} => execute_stake(deps, env, sender, wrapper.amount), + ReceiveMsg::Stake { auth } => execute_stake(deps, env, sender, wrapper.amount, auth), ReceiveMsg::Fund {} => execute_fund(deps, env, &sender, wrapper.amount), } } @@ -157,6 +158,7 @@ pub fn execute_stake( env: Env, sender: Addr, amount: Uint128, + auth: Auth, ) -> Result { let balance = BALANCE.load(deps.storage).unwrap_or_default(); let staked_total = StakedTotalStore::load(deps.storage); @@ -182,7 +184,7 @@ pub fn execute_stake( .checked_add(amount_to_stake) .map_err(StdError::overflow)?, )?; - let hook_msgs = stake_hook_msgs(HOOKS, deps.storage, sender.clone(), amount_to_stake)?; + let hook_msgs = stake_hook_msgs(HOOKS, deps.storage, sender.clone(), amount_to_stake, auth)?; Ok(Response::new() .add_submessages(hook_msgs) .add_attribute("action", "stake") @@ -195,6 +197,7 @@ pub fn execute_unstake( env: Env, info: MessageInfo, amount: Uint128, + auth: Auth, ) -> Result { let config = CONFIG.load(deps.storage)?; let balance = BALANCE.load(deps.storage).unwrap_or_default(); @@ -233,7 +236,7 @@ pub fn execute_unstake( .checked_sub(amount_to_claim) .map_err(StdError::overflow)?, )?; - let hook_msgs = unstake_hook_msgs(HOOKS, deps.storage, info.sender.clone(), amount)?; + let hook_msgs = unstake_hook_msgs(HOOKS, deps.storage, info.sender.clone(), amount, auth)?; match config.unstaking_duration { None => { let snip_send_msg = secret_toolkit::snip20::HandleMsg::Transfer { @@ -535,7 +538,7 @@ pub fn authenticate(deps: Deps, auth: Auth, query_auth: Contract) -> StdResult { - if permit.params.key!=DAO.load(deps.storage)?{ + if permit.params.key != DAO.load(deps.storage)? { return Err(StdError::generic_err("Invalid permit Key")); } let res: PermitAuthentication = diff --git a/contracts/staking/snip20-stake/src/msg.rs b/contracts/staking/snip20-stake/src/msg.rs index ed4551e..7982bd3 100644 --- a/contracts/staking/snip20-stake/src/msg.rs +++ b/contracts/staking/snip20-stake/src/msg.rs @@ -38,7 +38,7 @@ pub struct Snip20ReceiveMsg { #[serde(rename_all = "snake_case")] pub enum ExecuteMsg { Receive(Snip20ReceiveMsg), - Unstake { amount: Uint128 }, + Unstake { auth: Auth, amount: Uint128 }, Claim {}, UpdateConfig { duration: Option }, AddHook { addr: String, code_hash: String }, @@ -65,7 +65,7 @@ pub enum ExecuteAnswer { #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] pub enum ReceiveMsg { - Stake {}, + Stake { auth: Auth }, Fund {}, } diff --git a/contracts/staking/snip20-stake/src/state.rs b/contracts/staking/snip20-stake/src/state.rs index c931426..d83455f 100644 --- a/contracts/staking/snip20-stake/src/state.rs +++ b/contracts/staking/snip20-stake/src/state.rs @@ -21,7 +21,6 @@ pub const CONFIG: Item = Item::new("config"); pub const BALANCE: Item = Item::new("balance"); pub const DAO: Item = Item::new("dao"); - // Hooks to contracts that will receive staking and unstaking messages pub const HOOKS: Hooks = Hooks::new("hooks"); diff --git a/contracts/staking/snip20-stake/src/tests.rs b/contracts/staking/snip20-stake/src/tests.rs index 61356d8..48912ae 100644 --- a/contracts/staking/snip20-stake/src/tests.rs +++ b/contracts/staking/snip20-stake/src/tests.rs @@ -241,10 +241,11 @@ fn stake_tokens( snip20_code_hash: String, info: MessageInfo, amount: Uint128, + auth: Auth, ) -> AnyResult { let msg = secret_toolkit::snip20::HandleMsg::Send { amount, - msg: Some(to_binary(&ReceiveMsg::Stake {}).unwrap()), + msg: Some(to_binary(&ReceiveMsg::Stake { auth }).unwrap()), recipient: staking_addr.to_string(), recipient_code_hash: Some(staking_code_hash), memo: None, @@ -286,8 +287,9 @@ fn unstake_tokens( staking_code_hash: String, info: MessageInfo, amount: Uint128, + auth: Auth, ) -> AnyResult { - let msg = ExecuteMsg::Unstake { amount }; + let msg = ExecuteMsg::Unstake { auth, amount }; app.execute_contract( info.sender, &ContractInfo { @@ -439,6 +441,7 @@ fn test_staking() { setup_test_case(&mut app, initial_balances, None); let info = mock_info(ADDR1, &[]); + let viewing_key_user1 = create_viewing_key(&mut app, query_auth_info.clone(), info.clone()); let _env = mock_env(); // Successful bond @@ -451,6 +454,10 @@ fn test_staking() { snip20_info.code_hash.clone(), info.clone(), amount, + Auth::ViewingKey { + key: viewing_key_user1.clone(), + address: info.clone().sender.into_string(), + }, ) .unwrap(); @@ -505,6 +512,8 @@ fn test_staking() { // Addr 2 successful bond let info = mock_info(ADDR2, &[]); + let viewing_key_user2 = create_viewing_key(&mut app, query_auth_info.clone(), info.clone()); + stake_tokens( &mut app, &staking_info.address.clone(), @@ -513,11 +522,13 @@ fn test_staking() { snip20_info.code_hash.clone(), info.clone(), Uint128::new(20), + Auth::ViewingKey { + key: viewing_key_user2.clone(), + address: info.clone().sender.into_string(), + }, ) .unwrap(); - let viewing_key_user2 = create_viewing_key(&mut app, query_auth_info.clone(), info.clone()); - app.update_block(next_block); assert_eq!( @@ -549,6 +560,10 @@ fn test_staking() { staking_info.code_hash.clone(), info.clone(), Uint128::new(100), + Auth::ViewingKey { + key: viewing_key_user2.clone(), + address: info.clone().sender.into_string(), + }, ) .unwrap_err(); @@ -559,6 +574,10 @@ fn test_staking() { staking_info.code_hash.clone(), info.clone(), Uint128::new(10), + Auth::ViewingKey { + key: viewing_key_user2.clone(), + address: info.clone().sender.into_string(), + }, ) .unwrap(); app.update_block(next_block); @@ -608,13 +627,14 @@ fn text_max_claims() { address: ADDR1.to_string(), amount: amount1, }]; - let (staking_info, snip20_info, _query_auth) = setup_test_case( + let (staking_info, snip20_info, query_auth) = setup_test_case( &mut app, initial_balances, Some(Duration::Height(unstaking_blocks)), ); let info = mock_info(ADDR1, &[]); + let viewing_key_user1 = create_viewing_key(&mut app, query_auth.clone(), info.clone()); stake_tokens( &mut app, &staking_info.address.clone(), @@ -623,6 +643,10 @@ fn text_max_claims() { snip20_info.code_hash.clone(), info.clone(), amount1, + Auth::ViewingKey { + key: viewing_key_user1.clone(), + address: info.clone().sender.into_string(), + }, ) .unwrap(); @@ -634,6 +658,10 @@ fn text_max_claims() { staking_info.code_hash.clone(), info.clone(), Uint128::new(1), + Auth::ViewingKey { + key: viewing_key_user1.clone(), + address: info.clone().sender.into_string(), + }, ) .unwrap(); } @@ -645,6 +673,10 @@ fn text_max_claims() { staking_info.code_hash.clone(), info.clone(), Uint128::new(1), + Auth::ViewingKey { + key: viewing_key_user1.clone(), + address: info.clone().sender.into_string(), + }, ) .unwrap_err(); @@ -665,6 +697,10 @@ fn text_max_claims() { staking_info.code_hash.clone(), info.clone(), Uint128::new(1), + Auth::ViewingKey { + key: viewing_key_user1.clone(), + address: info.clone().sender.into_string(), + }, ) .unwrap(); app.update_block(next_block); @@ -705,8 +741,12 @@ fn test_unstaking_with_claims() { staking_info.code_hash.clone(), &snip20_info.address.clone(), snip20_info.code_hash.clone(), - info, + info.clone(), Uint128::new(50), + Auth::ViewingKey { + key: viewing_key_user1.clone(), + address: info.clone().sender.into_string(), + }, ) .unwrap(); app.update_block(next_block); @@ -738,8 +778,12 @@ fn test_unstaking_with_claims() { &mut app, &staking_info.address.clone(), staking_info.code_hash.clone(), - info, + info.clone(), Uint128::new(10), + Auth::ViewingKey { + key: viewing_key_user1.clone(), + address: info.clone().sender.into_string(), + }, ) .unwrap(); app.update_block(next_block); @@ -816,8 +860,12 @@ fn test_unstaking_with_claims() { &mut app, &staking_info.address.clone(), staking_info.code_hash.clone(), - info, + info.clone(), Uint128::new(5), + Auth::ViewingKey { + key: viewing_key_user1.clone(), + address: info.clone().sender.into_string(), + }, ) .unwrap(); app.update_block(next_block); @@ -828,8 +876,12 @@ fn test_unstaking_with_claims() { &mut app, &staking_info.address.clone(), staking_info.code_hash.clone(), - info, + info.clone(), Uint128::new(5), + Auth::ViewingKey { + key: viewing_key_user1.clone(), + address: info.clone().sender.into_string(), + }, ) .unwrap(); app.update_block(next_block); @@ -918,6 +970,8 @@ fn multiple_address_staking() { ); let info = mock_info(ADDR1, &[]); + let viewing_key_user1 = create_viewing_key(&mut app, query_auth_info.clone(), info.clone()); + // Successful bond let _res = stake_tokens( &mut app, @@ -927,13 +981,17 @@ fn multiple_address_staking() { snip20_info.code_hash.clone(), info.clone(), amount1, + Auth::ViewingKey { + key: viewing_key_user1.clone(), + address: ADDR1.to_string().clone(), + }, ) .unwrap(); app.update_block(next_block); - let viewing_key_user1 = create_viewing_key(&mut app, query_auth_info.clone(), info.clone()); - let info = mock_info(ADDR2, &[]); + let viewing_key_user2 = create_viewing_key(&mut app, query_auth_info.clone(), info.clone()); + // Successful bond let _res = stake_tokens( &mut app, @@ -943,13 +1001,17 @@ fn multiple_address_staking() { snip20_info.code_hash.clone(), info.clone(), amount1, + Auth::ViewingKey { + key: viewing_key_user2.clone(), + address: ADDR1.to_string().clone(), + }, ) .unwrap(); app.update_block(next_block); - let viewing_key_user2 = create_viewing_key(&mut app, query_auth_info.clone(), info.clone()); - let info = mock_info(ADDR3, &[]); + let viewing_key_user3 = create_viewing_key(&mut app, query_auth_info.clone(), info.clone()); + // Successful bond let _res = stake_tokens( &mut app, @@ -959,12 +1021,17 @@ fn multiple_address_staking() { snip20_info.code_hash.clone(), info.clone(), amount1, + Auth::ViewingKey { + key: viewing_key_user3.clone(), + address: ADDR1.to_string().clone(), + }, ) .unwrap(); app.update_block(next_block); - let viewing_key_user3 = create_viewing_key(&mut app, query_auth_info.clone(), info.clone()); let info = mock_info(ADDR4, &[]); + let viewing_key_user4 = create_viewing_key(&mut app, query_auth_info.clone(), info.clone()); + // Successful bond let _res = stake_tokens( &mut app, @@ -974,11 +1041,14 @@ fn multiple_address_staking() { snip20_info.code_hash.clone(), info.clone(), amount1, + Auth::ViewingKey { + key: viewing_key_user4.clone(), + address: ADDR1.to_string().clone(), + }, ) .unwrap(); app.update_block(next_block); - let viewing_key_user4 = create_viewing_key(&mut app, query_auth_info.clone(), info.clone()); assert_eq!( query_staked_balance( &app, @@ -1060,6 +1130,8 @@ fn test_simple_unstaking_with_duration() { // Bond Address 1 let info = mock_info(ADDR1, &[]); + let viewing_key_user1 = create_viewing_key(&mut app, query_auth_info.clone(), info.clone()); + let _env = mock_env(); let amount = Uint128::new(100); stake_tokens( @@ -1070,13 +1142,17 @@ fn test_simple_unstaking_with_duration() { snip20_info.code_hash.clone(), info.clone(), amount, + Auth::ViewingKey { + key: viewing_key_user1.clone(), + address: ADDR1.to_string().clone(), + }, ) .unwrap(); - let viewing_key_user1 = create_viewing_key(&mut app, query_auth_info.clone(), info.clone()); - // Bond Address 2 let info = mock_info(ADDR2, &[]); + let viewing_key_user2 = create_viewing_key(&mut app, query_auth_info.clone(), info.clone()); + let _env = mock_env(); let amount = Uint128::new(100); let _res = stake_tokens( @@ -1087,9 +1163,12 @@ fn test_simple_unstaking_with_duration() { snip20_info.code_hash.clone(), info.clone(), amount, + Auth::ViewingKey { + key: viewing_key_user2.clone(), + address: ADDR1.to_string().clone(), + }, ) .unwrap(); - let viewing_key_user2 = create_viewing_key(&mut app, query_auth_info.clone(), info.clone()); app.update_block(next_block); assert_eq!( query_staked_balance( @@ -1127,6 +1206,10 @@ fn test_simple_unstaking_with_duration() { staking_info.code_hash.clone(), info, amount, + Auth::ViewingKey { + key: viewing_key_user1.clone(), + address: ADDR1.to_string().clone(), + }, ) .unwrap(); // Unstake Addr2 @@ -1139,6 +1222,10 @@ fn test_simple_unstaking_with_duration() { staking_info.code_hash.clone(), info, amount, + Auth::ViewingKey { + key: viewing_key_user2.clone(), + address: ADDR1.to_string().clone(), + }, ) .unwrap(); app.update_block(next_block); diff --git a/contracts/voting/dao-voting-snip20-staked/src/tests.rs b/contracts/voting/dao-voting-snip20-staked/src/tests.rs index 6d5779c..f31ead6 100644 --- a/contracts/voting/dao-voting-snip20-staked/src/tests.rs +++ b/contracts/voting/dao-voting-snip20-staked/src/tests.rs @@ -189,12 +189,13 @@ fn stake_tokens( snip20_contract_info: ContractInfo, sender: &str, amount: u128, + auth: Auth, ) { let msg = snip20_reference_impl::msg::ExecuteMsg::Send { recipient: staking_addr.to_string(), recipient_code_hash: Some(staking_code_hash), amount: Uint128::new(amount), - msg: Some(to_binary(&snip20_stake::msg::ReceiveMsg::Stake {}).unwrap()), + msg: Some(to_binary(&snip20_stake::msg::ReceiveMsg::Stake { auth }).unwrap()), memo: None, decoys: None, entropy: None, @@ -542,6 +543,10 @@ fn test_existing_snip20() { snip20_info, CREATOR_ADDR, 1, + Auth::ViewingKey { + key: creator_viewing_key_snip20_stake.clone(), + address: CREATOR_ADDR.to_string(), + }, ); app.update_block(next_block); @@ -697,6 +702,10 @@ fn test_existing_cw20_existing_staking() { snip20_info.clone(), CREATOR_ADDR, 1, + Auth::ViewingKey { + key: creator_viewing_key_snip20_stake.clone(), + address: CREATOR_ADDR.to_string(), + }, ); // Expect 1 as creator has now staked 1 @@ -851,6 +860,10 @@ fn test_different_heights() { snip20_info.clone(), CREATOR_ADDR, 1, + Auth::ViewingKey { + key: creator_viewing_key_snip20_stake.clone(), + address: CREATOR_ADDR.to_string(), + }, ); // Expect 1 as creator has now staked 1 @@ -904,6 +917,10 @@ fn test_different_heights() { snip20_info.clone(), CREATOR_ADDR, 1, + Auth::ViewingKey { + key: creator_viewing_key_snip20_stake.clone(), + address: CREATOR_ADDR.to_string(), + }, ); // Expect 2 as creator has now staked 2 @@ -1048,8 +1065,8 @@ fn test_active_threshold_absolute_count() { }), dao_code_hash: "dao_code_hash".to_string(), query_auth: Some(RawContract { - address: query_auth.address.to_string(), - code_hash: query_auth.code_hash, + address: query_auth.clone().address.to_string(), + code_hash: query_auth.clone().code_hash, }), }, ); @@ -1067,6 +1084,8 @@ fn test_active_threshold_absolute_count() { // Stake 100 token as creator app.update_block(next_block); + let creator_viewing_key_snip20_stake = + create_viewing_key(&mut app, query_auth.clone(), mock_info(CREATOR_ADDR, &[])); stake_tokens( &mut app, staking_contract_info.clone().address, @@ -1074,6 +1093,10 @@ fn test_active_threshold_absolute_count() { snip20_info.clone(), CREATOR_ADDR, 100, + Auth::ViewingKey { + key: creator_viewing_key_snip20_stake.clone(), + address: CREATOR_ADDR.to_string(), + }, ); // Active as enough staked @@ -1143,8 +1166,8 @@ fn test_active_threshold_percent() { }), dao_code_hash: "dao_code_hash".to_string(), query_auth: Some(RawContract { - address: query_auth.address.to_string(), - code_hash: query_auth.code_hash, + address: query_auth.clone().address.to_string(), + code_hash: query_auth.clone().code_hash, }), }, ); @@ -1162,6 +1185,8 @@ fn test_active_threshold_percent() { // Stake 60 token as creator, now active app.update_block(next_block); + let creator_viewing_key_snip20_stake = + create_viewing_key(&mut app, query_auth.clone(), mock_info(CREATOR_ADDR, &[])); stake_tokens( &mut app, staking_contract_info.clone().address, @@ -1169,6 +1194,10 @@ fn test_active_threshold_percent() { snip20_info.clone(), CREATOR_ADDR, 60, + Auth::ViewingKey { + key: creator_viewing_key_snip20_stake.clone(), + address: CREATOR_ADDR.to_string(), + }, ); // Active as enough staked let is_active: IsActiveResponse = app @@ -1237,8 +1266,8 @@ fn test_active_threshold_percent_rounds_up() { }), dao_code_hash: "dao_code_hash".to_string(), query_auth: Some(RawContract { - address: query_auth.address.to_string(), - code_hash: query_auth.code_hash, + address: query_auth.clone().address.to_string(), + code_hash: query_auth.clone().code_hash, }), }, ); @@ -1256,6 +1285,8 @@ fn test_active_threshold_percent_rounds_up() { // Stake 2 token as creator, should not be active app.update_block(next_block); + let creator_viewing_key_snip20_stake = + create_viewing_key(&mut app, query_auth.clone(), mock_info(CREATOR_ADDR, &[])); stake_tokens( &mut app, staking_contract_info.clone().address, @@ -1263,6 +1294,10 @@ fn test_active_threshold_percent_rounds_up() { snip20_info.clone(), CREATOR_ADDR, 2, + Auth::ViewingKey { + key: creator_viewing_key_snip20_stake.clone(), + address: CREATOR_ADDR.to_string(), + }, ); let is_active: IsActiveResponse = app @@ -1284,6 +1319,10 @@ fn test_active_threshold_percent_rounds_up() { snip20_info.clone(), CREATOR_ADDR, 1, + Auth::ViewingKey { + key: creator_viewing_key_snip20_stake.clone(), + address: CREATOR_ADDR.to_string(), + }, ); let is_active: IsActiveResponse = app diff --git a/contracts/voting/dao-voting-snip721-staked/src/contract.rs b/contracts/voting/dao-voting-snip721-staked/src/contract.rs index aeefd0a..b743bab 100644 --- a/contracts/voting/dao-voting-snip721-staked/src/contract.rs +++ b/contracts/voting/dao-voting-snip721-staked/src/contract.rs @@ -756,7 +756,7 @@ pub fn authenticate(deps: Deps, auth: Auth, query_auth: Contract) -> StdResult { let dao = DAO.load(deps.storage)?.addr.to_string(); - if permit.params.key!=dao{ + if permit.params.key != dao { return Err(StdError::generic_err("Invalid permit Key")); } let res: PermitAuthentication = diff --git a/contracts/voting/dao-voting-token-staked/src/contract.rs b/contracts/voting/dao-voting-token-staked/src/contract.rs index 86e1cd6..d97f209 100644 --- a/contracts/voting/dao-voting-token-staked/src/contract.rs +++ b/contracts/voting/dao-voting-token-staked/src/contract.rs @@ -191,8 +191,8 @@ pub fn execute( msg: ExecuteMsg, ) -> Result { match msg { - ExecuteMsg::Stake {} => execute_stake(deps, env, info), - ExecuteMsg::Unstake { amount } => execute_unstake(deps, env, info, amount), + ExecuteMsg::Stake { auth } => execute_stake(deps, env, info, auth), + ExecuteMsg::Unstake { auth, amount } => execute_unstake(deps, env, info, amount, auth), ExecuteMsg::UpdateConfig { duration } => execute_update_config(deps, info, duration), ExecuteMsg::Claim {} => execute_claim(deps, env, info), ExecuteMsg::UpdateActiveThreshold { new_threshold } => { @@ -211,6 +211,7 @@ pub fn execute_stake( deps: DepsMut, env: Env, info: MessageInfo, + auth: Auth, ) -> Result { let denom = DENOM.load(deps.storage)?; let amount = must_pay(&info, &denom)?; @@ -246,7 +247,7 @@ pub fn execute_stake( )?; // Add stake hook messages - let hook_msgs = stake_hook_msgs(HOOKS, deps.storage, info.sender.clone(), amount)?; + let hook_msgs = stake_hook_msgs(HOOKS, deps.storage, info.sender.clone(), amount, auth)?; Ok(Response::new() .add_submessages(hook_msgs) @@ -260,6 +261,7 @@ pub fn execute_unstake( env: Env, info: MessageInfo, amount: Uint128, + auth: Auth, ) -> Result { if amount.is_zero() { return Err(ContractError::ZeroUnstake {}); @@ -289,7 +291,7 @@ pub fn execute_unstake( )?; // Add unstake hook messages - let hook_msgs = unstake_hook_msgs(HOOKS, deps.storage, info.sender.clone(), amount)?; + let hook_msgs = unstake_hook_msgs(HOOKS, deps.storage, info.sender.clone(), amount, auth)?; let config = CONFIG.load(deps.storage)?; let denom = DENOM.load(deps.storage)?; @@ -651,6 +653,10 @@ pub fn authenticate(deps: Deps, auth: Auth, query_auth: Contract) -> StdResult { + let dao = DAO.load(deps.storage)?.addr.to_string(); + if permit.params.key != dao { + return Err(StdError::generic_err("Invalid permit Key")); + } let res: PermitAuthentication = authenticate_permit(permit, &deps.querier, query_auth)?; if res.revoked { diff --git a/contracts/voting/dao-voting-token-staked/src/msg.rs b/contracts/voting/dao-voting-token-staked/src/msg.rs index e11b349..a24ae7f 100644 --- a/contracts/voting/dao-voting-token-staked/src/msg.rs +++ b/contracts/voting/dao-voting-token-staked/src/msg.rs @@ -45,9 +45,9 @@ pub struct InstantiateMsg { #[cw_serde] pub enum ExecuteMsg { /// Stakes tokens with the contract to get voting power in the DAO - Stake {}, + Stake { auth: Auth }, /// Unstakes tokens so that they begin unbonding - Unstake { amount: Uint128 }, + Unstake { auth: Auth, amount: Uint128 }, /// Updates the contract configuration UpdateConfig { duration: Option }, /// Claims unstaked tokens that have completed the unbonding period diff --git a/contracts/voting/dao-voting-token-staked/src/tests/multitest/tests.rs b/contracts/voting/dao-voting-token-staked/src/tests/multitest/tests.rs index d52efcf..6d9320b 100644 --- a/contracts/voting/dao-voting-token-staked/src/tests/multitest/tests.rs +++ b/contracts/voting/dao-voting-token-staked/src/tests/multitest/tests.rs @@ -168,12 +168,13 @@ fn stake_tokens( staking_info: ContractInfo, sender: &str, amount: u128, + auth: Auth, denom: &str, ) -> anyhow::Result { app.execute_contract( Addr::unchecked(sender), &staking_info, - &ExecuteMsg::Stake {}, + &ExecuteMsg::Stake { auth }, &coins(amount, denom), ) } @@ -183,11 +184,13 @@ fn unstake_tokens( staking_info: ContractInfo, sender: &str, amount: u128, + auth: Auth, ) -> anyhow::Result { app.execute_contract( Addr::unchecked(sender), &staking_info, &ExecuteMsg::Unstake { + auth, amount: Uint128::new(amount), }, &[], @@ -387,6 +390,8 @@ fn test_stake_invalid_denom() { let staking_contract_instantiate_info = app.store_code(staking_contract()); let query_auth = instantiate_query_auth(&mut app); + let info = mock_info(ADDR1, &[]); + let viewing_key = create_viewing_key(&mut app, query_auth.clone(), info.clone()); // Populated fields let staking_info = instantiate_staking( &mut app, @@ -406,7 +411,18 @@ fn test_stake_invalid_denom() { ); // Try and stake an invalid denom - stake_tokens(&mut app, staking_info, ADDR1, 100, INVALID_DENOM).unwrap(); + stake_tokens( + &mut app, + staking_info, + ADDR1, + 100, + Auth::ViewingKey { + key: viewing_key, + address: info.sender.to_string(), + }, + INVALID_DENOM, + ) + .unwrap(); } #[test] @@ -415,6 +431,8 @@ fn test_stake_valid_denom() { let staking_contract_instantiate_info = app.store_code(staking_contract()); let query_auth = instantiate_query_auth(&mut app); + let info = mock_info(ADDR1, &[]); + let viewing_key = create_viewing_key(&mut app, query_auth.clone(), info.clone()); // Populated fields let staking_info = instantiate_staking( &mut app, @@ -433,7 +451,18 @@ fn test_stake_valid_denom() { }, ); // Try and stake an valid denom - stake_tokens(&mut app, staking_info, ADDR1, 100, DENOM).unwrap(); + stake_tokens( + &mut app, + staking_info, + ADDR1, + 100, + Auth::ViewingKey { + key: viewing_key, + address: info.sender.to_string(), + }, + DENOM, + ) + .unwrap(); app.update_block(next_block); } @@ -444,6 +473,8 @@ fn test_unstake_none_staked() { let staking_contract_instantiate_info = app.store_code(staking_contract()); let query_auth = instantiate_query_auth(&mut app); + let info = mock_info(ADDR1, &[]); + let viewing_key = create_viewing_key(&mut app, query_auth.clone(), info.clone()); // Populated fields let staking_info = instantiate_staking( &mut app, @@ -462,7 +493,17 @@ fn test_unstake_none_staked() { }, ); - unstake_tokens(&mut app, staking_info, ADDR1, 100).unwrap(); + unstake_tokens( + &mut app, + staking_info, + ADDR1, + 100, + Auth::ViewingKey { + key: viewing_key, + address: info.sender.to_string(), + }, + ) + .unwrap(); } #[test] @@ -472,6 +513,8 @@ fn test_unstake_zero_tokens() { let staking_contract_instantiate_info = app.store_code(staking_contract()); let query_auth = instantiate_query_auth(&mut app); + let info = mock_info(ADDR1, &[]); + let viewing_key = create_viewing_key(&mut app, query_auth.clone(), info.clone()); // Populated fields let staking_info = instantiate_staking( &mut app, @@ -490,7 +533,17 @@ fn test_unstake_zero_tokens() { }, ); - unstake_tokens(&mut app, staking_info, ADDR1, 0).unwrap(); + unstake_tokens( + &mut app, + staking_info, + ADDR1, + 0, + Auth::ViewingKey { + key: viewing_key, + address: info.sender.to_string(), + }, + ) + .unwrap(); } #[test] @@ -500,6 +553,8 @@ fn test_unstake_invalid_balance() { let staking_contract_instantiate_info = app.store_code(staking_contract()); let query_auth = instantiate_query_auth(&mut app); + let info = mock_info(ADDR1, &[]); + let viewing_key = create_viewing_key(&mut app, query_auth.clone(), info.clone()); // Populated fields let staking_info = instantiate_staking( &mut app, @@ -519,11 +574,32 @@ fn test_unstake_invalid_balance() { ); // Stake some tokens - stake_tokens(&mut app, staking_info.clone(), ADDR1, 100, DENOM).unwrap(); + stake_tokens( + &mut app, + staking_info.clone(), + ADDR1, + 100, + Auth::ViewingKey { + key: viewing_key.clone(), + address: info.sender.to_string(), + }, + DENOM, + ) + .unwrap(); app.update_block(next_block); // Try and unstake too many - unstake_tokens(&mut app, staking_info, ADDR1, 200).unwrap(); + unstake_tokens( + &mut app, + staking_info, + ADDR1, + 200, + Auth::ViewingKey { + key: viewing_key, + address: info.sender.to_string(), + }, + ) + .unwrap(); } #[test] @@ -532,6 +608,8 @@ fn test_unstake() { let staking_contract_instantiate_info = app.store_code(staking_contract()); let query_auth = instantiate_query_auth(&mut app); + let info = mock_info(ADDR1, &[]); + let viewing_key = create_viewing_key(&mut app, query_auth.clone(), info.clone()); // Populated fields let staking_info = instantiate_staking( &mut app, @@ -550,14 +628,33 @@ fn test_unstake() { }, ); - let viewing_key = create_viewing_key(&mut app, query_auth, mock_info(ADDR1, &[])); - // Stake some tokens - stake_tokens(&mut app, staking_info.clone(), ADDR1, 100, DENOM).unwrap(); + stake_tokens( + &mut app, + staking_info.clone(), + ADDR1, + 100, + Auth::ViewingKey { + key: viewing_key.clone(), + address: info.sender.to_string(), + }, + DENOM, + ) + .unwrap(); app.update_block(next_block); // Unstake some - unstake_tokens(&mut app, staking_info.clone(), ADDR1, 75).unwrap(); + unstake_tokens( + &mut app, + staking_info.clone(), + ADDR1, + 75, + Auth::ViewingKey { + key: viewing_key.clone(), + address: info.sender.to_string(), + }, + ) + .unwrap(); // Query claims let claims = get_claims( @@ -572,7 +669,17 @@ fn test_unstake() { app.update_block(next_block); // Unstake the rest - unstake_tokens(&mut app, staking_info.clone(), ADDR1, 25).unwrap(); + unstake_tokens( + &mut app, + staking_info.clone(), + ADDR1, + 25, + Auth::ViewingKey { + key: viewing_key.clone(), + address: info.sender.to_string(), + }, + ) + .unwrap(); // Query claims let claims = get_claims( @@ -591,6 +698,8 @@ fn test_unstake_no_unstaking_duration() { let mut app = mock_app(); let staking_contract_instantiate_info = app.store_code(staking_contract()); let query_auth = instantiate_query_auth(&mut app); + let info = mock_info(ADDR1, &[]); + let viewing_key = create_viewing_key(&mut app, query_auth.clone(), info.clone()); // Populated fields let staking_info = instantiate_staking( &mut app, @@ -610,11 +719,32 @@ fn test_unstake_no_unstaking_duration() { ); // Stake some tokens - stake_tokens(&mut app, staking_info.clone(), ADDR1, 100, DENOM).unwrap(); + stake_tokens( + &mut app, + staking_info.clone(), + ADDR1, + 100, + Auth::ViewingKey { + key: viewing_key.clone(), + address: info.sender.to_string(), + }, + DENOM, + ) + .unwrap(); app.update_block(next_block); // Unstake some tokens - unstake_tokens(&mut app, staking_info.clone(), ADDR1, 75).unwrap(); + unstake_tokens( + &mut app, + staking_info.clone(), + ADDR1, + 75, + Auth::ViewingKey { + key: viewing_key.clone(), + address: info.sender.to_string(), + }, + ) + .unwrap(); app.update_block(next_block); @@ -623,7 +753,17 @@ fn test_unstake_no_unstaking_duration() { assert_eq!(balance, Uint128::new(9975)); // Unstake the rest - unstake_tokens(&mut app, staking_info, ADDR1, 25).unwrap(); + unstake_tokens( + &mut app, + staking_info, + ADDR1, + 25, + Auth::ViewingKey { + key: viewing_key, + address: info.sender.to_string(), + }, + ) + .unwrap(); let balance = get_balance(&mut app, ADDR1, DENOM); // 10000 (initial bal) - 100 (staked) + 75 (unstaked 1) + 25 (unstaked 2) = 10000 @@ -665,6 +805,8 @@ fn test_claim_claim_not_reached() { let staking_contract_instantiate_info = app.store_code(staking_contract()); let query_auth = instantiate_query_auth(&mut app); + let info = mock_info(ADDR1, &[]); + let viewing_key = create_viewing_key(&mut app, query_auth.clone(), info.clone()); // Populated fields let staking_info = instantiate_staking( &mut app, @@ -684,11 +826,32 @@ fn test_claim_claim_not_reached() { ); // Stake some tokens - stake_tokens(&mut app, staking_info.clone(), ADDR1, 100, DENOM).unwrap(); + stake_tokens( + &mut app, + staking_info.clone(), + ADDR1, + 100, + Auth::ViewingKey { + key: viewing_key.clone(), + address: info.sender.to_string(), + }, + DENOM, + ) + .unwrap(); app.update_block(next_block); // Unstake them to create the claims - unstake_tokens(&mut app, staking_info.clone(), ADDR1, 100).unwrap(); + unstake_tokens( + &mut app, + staking_info.clone(), + ADDR1, + 100, + Auth::ViewingKey { + key: viewing_key, + address: info.sender.to_string(), + }, + ) + .unwrap(); app.update_block(next_block); // We have a claim but it isnt reached yet so this will still fail @@ -701,6 +864,8 @@ fn test_claim() { let staking_contract_instantiate_info = app.store_code(staking_contract()); let query_auth = instantiate_query_auth(&mut app); + let info = mock_info(ADDR1, &[]); + let viewing_key = create_viewing_key(&mut app, query_auth.clone(), info.clone()); // Populated fields let staking_info = instantiate_staking( &mut app, @@ -720,11 +885,32 @@ fn test_claim() { ); // Stake some tokens - stake_tokens(&mut app, staking_info.clone(), ADDR1, 100, DENOM).unwrap(); + stake_tokens( + &mut app, + staking_info.clone(), + ADDR1, + 100, + Auth::ViewingKey { + key: viewing_key.clone(), + address: info.sender.to_string(), + }, + DENOM, + ) + .unwrap(); app.update_block(next_block); // Unstake some to create the claims - unstake_tokens(&mut app, staking_info.clone(), ADDR1, 75).unwrap(); + unstake_tokens( + &mut app, + staking_info.clone(), + ADDR1, + 75, + Auth::ViewingKey { + key: viewing_key.clone(), + address: info.sender.to_string(), + }, + ) + .unwrap(); app.update_block(|b| { b.height += 5; b.time = b.time.plus_seconds(25); @@ -739,7 +925,17 @@ fn test_claim() { assert_eq!(balance, Uint128::new(9975)); // Unstake the rest - unstake_tokens(&mut app, staking_info.clone(), ADDR1, 25).unwrap(); + unstake_tokens( + &mut app, + staking_info.clone(), + ADDR1, + 25, + Auth::ViewingKey { + key: viewing_key.clone(), + address: info.sender.to_string(), + }, + ) + .unwrap(); app.update_block(|b| { b.height += 10; b.time = b.time.plus_seconds(50); @@ -939,6 +1135,8 @@ fn test_query_claims() { let staking_contract_instantiate_info = app.store_code(staking_contract()); let query_auth = instantiate_query_auth(&mut app); + let info = mock_info(ADDR1, &[]); + let viewing_key = create_viewing_key(&mut app, query_auth.clone(), info.clone()); // Populated fields let staking_info = instantiate_staking( &mut app, @@ -957,8 +1155,6 @@ fn test_query_claims() { }, ); - let viewing_key = create_viewing_key(&mut app, query_auth, mock_info(ADDR1, &[])); - let claims = get_claims( &mut app, staking_info.clone(), @@ -970,11 +1166,32 @@ fn test_query_claims() { assert_eq!(claims.claims.len(), 0); // Stake some tokens - stake_tokens(&mut app, staking_info.clone(), ADDR1, 100, DENOM).unwrap(); + stake_tokens( + &mut app, + staking_info.clone(), + ADDR1, + 100, + Auth::ViewingKey { + key: viewing_key.clone(), + address: info.sender.to_string(), + }, + DENOM, + ) + .unwrap(); app.update_block(next_block); // Unstake some tokens - unstake_tokens(&mut app, staking_info.clone(), ADDR1, 25).unwrap(); + unstake_tokens( + &mut app, + staking_info.clone(), + ADDR1, + 25, + Auth::ViewingKey { + key: viewing_key.clone(), + address: info.sender.to_string(), + }, + ) + .unwrap(); app.update_block(next_block); let claims = get_claims( @@ -987,7 +1204,17 @@ fn test_query_claims() { ); assert_eq!(claims.claims.len(), 1); - unstake_tokens(&mut app, staking_info.clone(), ADDR1, 25).unwrap(); + unstake_tokens( + &mut app, + staking_info.clone(), + ADDR1, + 25, + Auth::ViewingKey { + key: viewing_key.clone(), + address: info.sender.to_string(), + }, + ) + .unwrap(); app.update_block(next_block); let claims = get_claims( @@ -1041,6 +1268,7 @@ fn test_voting_power_queries() { let staking_contract_instantiate_info = app.store_code(staking_contract()); let query_auth = instantiate_query_auth(&mut app); + // Populated fields let staking_info = instantiate_staking( &mut app, @@ -1080,7 +1308,18 @@ fn test_voting_power_queries() { assert!(resp.power.is_zero()); // ADDR1 stakes - stake_tokens(&mut app, staking_info.clone(), ADDR1, 100, DENOM).unwrap(); + stake_tokens( + &mut app, + staking_info.clone(), + ADDR1, + 100, + Auth::ViewingKey { + key: viewing_key_addr1.clone(), + address: ADDR1.to_string(), + }, + DENOM, + ) + .unwrap(); app.update_block(next_block); // Total power is 100 @@ -1112,7 +1351,18 @@ fn test_voting_power_queries() { assert!(resp.power.is_zero()); // ADDR2 stakes - stake_tokens(&mut app, staking_info.clone(), ADDR2, 50, DENOM).unwrap(); + stake_tokens( + &mut app, + staking_info.clone(), + ADDR2, + 50, + Auth::ViewingKey { + key: viewing_key_addr2.clone(), + address: ADDR2.to_string(), + }, + DENOM, + ) + .unwrap(); app.update_block(next_block); let prev_height = app.block_info().height - 2; @@ -1163,7 +1413,17 @@ fn test_voting_power_queries() { assert_eq!(resp.power, Uint128::new(50)); // ADDR1 unstakes half - unstake_tokens(&mut app, staking_info.clone(), ADDR1, 50).unwrap(); + unstake_tokens( + &mut app, + staking_info.clone(), + ADDR1, + 50, + Auth::ViewingKey { + key: viewing_key_addr1.clone(), + address: ADDR1.to_string(), + }, + ) + .unwrap(); app.update_block(next_block); let prev_height = app.block_info().height - 2; diff --git a/packages/dao-hooks/src/stake.rs b/packages/dao-hooks/src/stake.rs index a8c8fa6..ac07843 100644 --- a/packages/dao-hooks/src/stake.rs +++ b/packages/dao-hooks/src/stake.rs @@ -2,13 +2,22 @@ use cosmwasm_std::{to_binary, Addr, StdResult, Storage, SubMsg, Uint128, WasmMsg use cw_hooks::Hooks; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use shade_protocol::basic_staking::Auth; /// An enum representing staking hooks. #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] #[serde(rename_all = "snake_case")] pub enum StakeChangedHookMsg { - Stake { addr: Addr, amount: Uint128 }, - Unstake { addr: Addr, amount: Uint128 }, + Stake { + addr: Addr, + amount: Uint128, + auth: Auth, + }, + Unstake { + addr: Addr, + amount: Uint128, + auth: Auth, + }, } /// Prepares StakeChangedHookMsg::Stake hook SubMsgs, @@ -18,9 +27,10 @@ pub fn stake_hook_msgs( storage: &dyn Storage, addr: Addr, amount: Uint128, + auth: Auth, ) -> StdResult> { let msg = to_binary(&StakeChangedExecuteMsg::StakeChangeHook( - StakeChangedHookMsg::Stake { addr, amount }, + StakeChangedHookMsg::Stake { addr, amount, auth }, ))?; hooks.prepare_hooks(storage, |hook_item| { let execute = WasmMsg::Execute { @@ -40,9 +50,10 @@ pub fn unstake_hook_msgs( storage: &dyn Storage, addr: Addr, amount: Uint128, + auth: Auth, ) -> StdResult> { let msg = to_binary(&StakeChangedExecuteMsg::StakeChangeHook( - StakeChangedHookMsg::Unstake { addr, amount }, + StakeChangedHookMsg::Unstake { addr, amount, auth }, ))?; hooks.prepare_hooks(storage, |hook_item| { let execute = WasmMsg::Execute {