diff --git a/contracts/vesting/src/contract.rs b/contracts/vesting/src/contract.rs index f95e2d2e7f..a19c571966 100644 --- a/contracts/vesting/src/contract.rs +++ b/contracts/vesting/src/contract.rs @@ -1,14 +1,16 @@ use crate::errors::ContractError; use crate::messages::{ExecuteMsg, InitMsg, QueryMsg}; -use crate::vesting::VestingPeriod; +use crate::storage::{get_account, set_account}; +use crate::vesting::{DelegationAccount, VestingAccount, VestingPeriod}; use cosmwasm_std::{ - entry_point, to_binary, Addr, Coin, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, + entry_point, to_binary, Coin, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, Timestamp, }; use mixnet_contract::IdentityKey; pub const NUM_VESTING_PERIODS: u64 = 8; pub const VESTING_PERIOD: u64 = 3 * 30 * 86400; +pub const ADMIN_ADDRESS: &str = "admin"; /// Instantiate the contract. /// @@ -17,9 +19,9 @@ pub const VESTING_PERIOD: u64 = 3 * 30 * 86400; /// `msg` is the contract initialization message, sort of like a constructor call. #[entry_point] pub fn instantiate( - deps: DepsMut, - env: Env, - info: MessageInfo, + _deps: DepsMut, + _env: Env, + _info: MessageInfo, _msg: InitMsg, ) -> Result { Ok(Response::default()) @@ -34,44 +36,70 @@ pub fn execute( msg: ExecuteMsg, ) -> Result { match msg { - ExecuteMsg::DelegateToMixnode { mix_identity } => { - try_delegate_to_mixnode(mix_identity, env, deps) - } - ExecuteMsg::UndelegateFromMixnode { mix_identity } => { - try_undelegate_from_mixnode(mix_identity, env, deps) - } + ExecuteMsg::DelegateToMixnode { + mix_identity, + delegate_addr, + amount, + } => try_delegate_to_mixnode(mix_identity, delegate_addr, amount, info, env, deps), + ExecuteMsg::UndelegateFromMixnode { + mix_identity, + delegate_addr, + } => try_undelegate_from_mixnode(mix_identity, delegate_addr, info, deps), ExecuteMsg::CreatePeriodicVestingAccount { address, coin, start_time, - periods, - } => try_create_periodic_vesting_account(address, coin, start_time, env, deps), + } => try_create_periodic_vesting_account(address, coin, start_time, info, env, deps), } } fn try_delegate_to_mixnode( mix_identity: IdentityKey, + delegate_addr: String, + amount: Coin, + info: MessageInfo, env: Env, deps: DepsMut, ) -> Result { - unimplemented!() + if info.sender != ADMIN_ADDRESS { + return Err(ContractError::NotAdmin(info.sender.as_str().to_string())); + } + let address = deps.api.addr_validate(&delegate_addr)?; + if let Some(account) = get_account(deps.storage, &address) { + account.try_delegate_to_mixnode(mix_identity, amount, env, deps)?; + } + Ok(Response::default()) } fn try_undelegate_from_mixnode( mix_identity: IdentityKey, - env: Env, + delegate_addr: String, + info: MessageInfo, deps: DepsMut, ) -> Result { - unimplemented!() + if info.sender != ADMIN_ADDRESS { + return Err(ContractError::NotAdmin(info.sender.as_str().to_string())); + } + let address = deps.api.addr_validate(&delegate_addr)?; + if let Some(account) = get_account(deps.storage, &address) { + account.try_undelegate_from_mixnode(mix_identity, deps)?; + } + Ok(Response::default()) } fn try_create_periodic_vesting_account( - address: Addr, + address: String, coin: Coin, start_time: Option, + info: MessageInfo, env: Env, deps: DepsMut, ) -> Result { + let mut deps = deps; + if info.sender != ADMIN_ADDRESS { + return Err(ContractError::NotAdmin(info.sender.as_str().to_string())); + } + let address = deps.api.addr_validate(&address)?; let start_time = start_time.unwrap_or_else(|| env.block.time.seconds()); let mut periods = Vec::new(); // There are eight 3 month periods in two years @@ -86,87 +114,200 @@ fn try_create_periodic_vesting_account( coin, Timestamp::from_seconds(start_time), periods, - ); - unimplemented!() + &mut deps, + )?; + set_account(deps.storage, account)?; + Ok(Response::default()) } #[entry_point] pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result { let query_res = match msg { - QueryMsg::LockedCoins { block_time } => { - to_binary(&try_get_locked_coins(block_time, env, deps)?) - } - QueryMsg::SpendableCoins { block_time } => { - to_binary(&try_get_spendable_coins(block_time, env, deps)?) - } - QueryMsg::GetVestedCoins { block_time } => { - to_binary(&try_get_vested_coins(block_time, env, deps)?) - } - QueryMsg::GetVestingCoins { block_time } => { - to_binary(&try_get_vesting_coins(block_time, env, deps)?) - } - QueryMsg::GetStartTime {} => to_binary(&try_get_start_time(env, deps)?), - QueryMsg::GetEndTime {} => to_binary(&try_get_end_time(env, deps)?), - QueryMsg::GetOriginalVesting {} => to_binary(&try_get_original_vesting(env, deps)?), - QueryMsg::GetDelegatedFree {} => to_binary(&try_get_delegated_free(env, deps)?), - QueryMsg::GetDelegatedVesting {} => to_binary(&try_get_delegated_vesting(env, deps)?), + QueryMsg::LockedCoins { + vesting_account_address, + block_time, + } => to_binary(&try_get_locked_coins( + vesting_account_address, + block_time, + env, + deps, + )?), + QueryMsg::SpendableCoins { + vesting_account_address, + block_time, + } => to_binary(&try_get_spendable_coins( + vesting_account_address, + block_time, + env, + deps, + )?), + QueryMsg::GetVestedCoins { + vesting_account_address, + block_time, + } => to_binary(&try_get_vested_coins( + vesting_account_address, + block_time, + env, + deps, + )?), + QueryMsg::GetVestingCoins { + vesting_account_address, + block_time, + } => to_binary(&try_get_vesting_coins( + vesting_account_address, + block_time, + env, + deps, + )?), + QueryMsg::GetStartTime { + vesting_account_address, + } => to_binary(&try_get_start_time(vesting_account_address, env, deps)?), + QueryMsg::GetEndTime { + vesting_account_address, + } => to_binary(&try_get_end_time(vesting_account_address, env, deps)?), + QueryMsg::GetOriginalVesting { + vesting_account_address, + } => to_binary(&try_get_original_vesting( + vesting_account_address, + env, + deps, + )?), + QueryMsg::GetDelegatedFree { + vesting_account_address, + } => to_binary(&try_get_delegated_free(vesting_account_address, env, deps)?), + QueryMsg::GetDelegatedVesting { + vesting_account_address, + } => to_binary(&try_get_delegated_vesting( + vesting_account_address, + env, + deps, + )?), }; Ok(query_res?) } fn try_get_locked_coins( + vesting_account_address: String, block_time: Option, env: Env, deps: Deps, -) -> Result, ContractError> { +) -> Result { let block_time = block_time.unwrap_or(env.block.time); - unimplemented!() + let address = deps.api.addr_validate(&vesting_account_address)?; + if let Some(account) = get_account(deps.storage, &address) { + Ok(account.locked_coins(block_time, env, deps)) + } else { + Err(ContractError::NoSuchAccount(vesting_account_address)) + } } fn try_get_spendable_coins( + vesting_account_address: String, block_time: Option, env: Env, deps: Deps, -) -> Result, ContractError> { +) -> Result { let block_time = block_time.unwrap_or(env.block.time); - unimplemented!() + let address = deps.api.addr_validate(&vesting_account_address)?; + if let Some(account) = get_account(deps.storage, &address) { + Ok(account.spendable_coins(block_time, env, deps)) + } else { + Err(ContractError::NoSuchAccount(vesting_account_address)) + } } fn try_get_vested_coins( + vesting_account_address: String, block_time: Option, env: Env, deps: Deps, -) -> Result, ContractError> { +) -> Result { let block_time = block_time.unwrap_or(env.block.time); - unimplemented!() + let address = deps.api.addr_validate(&vesting_account_address)?; + if let Some(account) = get_account(deps.storage, &address) { + Ok(account.get_vested_coins(block_time)) + } else { + Err(ContractError::NoSuchAccount(vesting_account_address)) + } } fn try_get_vesting_coins( + vesting_account_address: String, block_time: Option, env: Env, deps: Deps, -) -> Result, ContractError> { +) -> Result { let block_time = block_time.unwrap_or(env.block.time); - unimplemented!() + let address = deps.api.addr_validate(&vesting_account_address)?; + if let Some(account) = get_account(deps.storage, &address) { + Ok(account.get_vesting_coins(block_time)) + } else { + Err(ContractError::NoSuchAccount(vesting_account_address)) + } } -fn try_get_start_time(env: Env, deps: Deps) -> Result, ContractError> { - unimplemented!() +fn try_get_start_time( + vesting_account_address: String, + env: Env, + deps: Deps, +) -> Result { + let address = deps.api.addr_validate(&vesting_account_address)?; + if let Some(account) = get_account(deps.storage, &address) { + Ok(account.get_start_time()) + } else { + Err(ContractError::NoSuchAccount(vesting_account_address)) + } } -fn try_get_end_time(env: Env, deps: Deps) -> Result, ContractError> { - unimplemented!() +fn try_get_end_time( + vesting_account_address: String, + env: Env, + deps: Deps, +) -> Result { + let address = deps.api.addr_validate(&vesting_account_address)?; + if let Some(account) = get_account(deps.storage, &address) { + Ok(account.get_end_time()) + } else { + Err(ContractError::NoSuchAccount(vesting_account_address)) + } } -fn try_get_original_vesting(env: Env, deps: Deps) -> Result, ContractError> { - unimplemented!() +fn try_get_original_vesting( + vesting_account_address: String, + env: Env, + deps: Deps, +) -> Result { + let address = deps.api.addr_validate(&vesting_account_address)?; + if let Some(account) = get_account(deps.storage, &address) { + Ok(account.get_original_vesting()) + } else { + Err(ContractError::NoSuchAccount(vesting_account_address)) + } } -fn try_get_delegated_free(env: Env, deps: Deps) -> Result, ContractError> { - unimplemented!() +fn try_get_delegated_free( + vesting_account_address: String, + env: Env, + deps: Deps, +) -> Result { + let address = deps.api.addr_validate(&vesting_account_address)?; + if let Some(account) = get_account(deps.storage, &address) { + Ok(account.get_delegated_free(env, deps)) + } else { + Err(ContractError::NoSuchAccount(vesting_account_address)) + } } -fn try_get_delegated_vesting(env: Env, deps: Deps) -> Result, ContractError> { - unimplemented!() +fn try_get_delegated_vesting( + vesting_account_address: String, + env: Env, + deps: Deps, +) -> Result { + let address = deps.api.addr_validate(&vesting_account_address)?; + if let Some(account) = get_account(deps.storage, &address) { + Ok(account.get_delegated_vesting(env, deps)) + } else { + Err(ContractError::NoSuchAccount(vesting_account_address)) + } } diff --git a/contracts/vesting/src/errors.rs b/contracts/vesting/src/errors.rs index 7155f59277..2129c7f3a8 100644 --- a/contracts/vesting/src/errors.rs +++ b/contracts/vesting/src/errors.rs @@ -5,4 +5,8 @@ use thiserror::Error; pub enum ContractError { #[error("{0}")] Std(#[from] StdError), + #[error("Account does not exist - {0}")] + NoSuchAccount(String), + #[error("Only admin can perform this action, {0} is not admin")] + NotAdmin(String), } diff --git a/contracts/vesting/src/messages.rs b/contracts/vesting/src/messages.rs index b6f94bc6b1..f665921ca7 100644 --- a/contracts/vesting/src/messages.rs +++ b/contracts/vesting/src/messages.rs @@ -1,4 +1,4 @@ -use crate::vesting::VestingPeriod; +use crate::vesting::{self, VestingPeriod}; use cosmwasm_std::{Addr, Coin, Timestamp}; use mixnet_contract::IdentityKey; use schemars::JsonSchema; @@ -8,30 +8,56 @@ use serde::{Deserialize, Serialize}; pub struct InitMsg {} #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[serde(rename_all = "snake_case")] pub enum ExecuteMsg { DelegateToMixnode { mix_identity: IdentityKey, + delegate_addr: String, + amount: Coin, }, UndelegateFromMixnode { mix_identity: IdentityKey, + delegate_addr: String, }, CreatePeriodicVestingAccount { - address: Addr, + address: String, coin: Coin, start_time: Option, - periods: Option>, }, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[serde(rename_all = "snake_case")] pub enum QueryMsg { - LockedCoins { block_time: Option }, - SpendableCoins { block_time: Option }, - GetVestedCoins { block_time: Option }, - GetVestingCoins { block_time: Option }, - GetStartTime, - GetEndTime, - GetOriginalVesting, - GetDelegatedFree, - GetDelegatedVesting, + LockedCoins { + vesting_account_address: String, + block_time: Option, + }, + SpendableCoins { + vesting_account_address: String, + block_time: Option, + }, + GetVestedCoins { + vesting_account_address: String, + block_time: Option, + }, + GetVestingCoins { + vesting_account_address: String, + block_time: Option, + }, + GetStartTime { + vesting_account_address: String, + }, + GetEndTime { + vesting_account_address: String, + }, + GetOriginalVesting { + vesting_account_address: String, + }, + GetDelegatedFree { + vesting_account_address: String, + }, + GetDelegatedVesting { + vesting_account_address: String, + }, } diff --git a/contracts/vesting/src/storage.rs b/contracts/vesting/src/storage.rs index 7b7f0cd347..d75318f647 100644 --- a/contracts/vesting/src/storage.rs +++ b/contracts/vesting/src/storage.rs @@ -1,51 +1,65 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use cosmwasm_std::{Order, StdResult, Storage, Uint128}; -use cosmwasm_storage::{ - bucket, bucket_read, singleton, singleton_read, Bucket, ReadonlyBucket, ReadonlySingleton, - Singleton, -}; +use cosmwasm_std::{Addr, StdResult, Storage, Uint128}; +use cosmwasm_storage::{bucket, bucket_read, Bucket, ReadonlyBucket}; use mixnet_contract::IdentityKey; use std::collections::HashMap; -use crate::vesting::{DelegationData, PeriodicVestingAccount}; +use crate::{ + errors::ContractError, + vesting::{DelegationData, PeriodicVestingAccount}, +}; // storage prefixes // all of them must be unique and presumably not be a prefix of a different one // keeping them as short as possible is also desirable as they are part of each stored key // it's not as important for singletons, but is a nice optimisation for buckets -// singletons - // buckets const PREFIX_ACCOUNTS: &[u8] = b"ac"; const PREFIX_ACCOUNT_DELEGATIONS: &[u8] = b"ad"; +const PREFIX_ACCOUNT_BALANCE: &[u8] = b"ab"; // Contract-level stuff -pub fn accounts_mut(storage: &mut dyn Storage) -> Bucket { +fn accounts_mut(storage: &mut dyn Storage) -> Bucket { bucket(storage, PREFIX_ACCOUNTS) } -pub fn accounts(storage: &dyn Storage) -> ReadonlyBucket { +fn accounts(storage: &dyn Storage) -> ReadonlyBucket { bucket_read(storage, PREFIX_ACCOUNTS) } -pub fn account_delegations_mut( +fn account_delegations_mut( storage: &mut dyn Storage, ) -> Bucket>> { bucket(storage, PREFIX_ACCOUNT_DELEGATIONS) } -pub fn account_delegations( +fn account_delegations( storage: &dyn Storage, ) -> ReadonlyBucket>> { bucket_read(storage, PREFIX_ACCOUNT_DELEGATIONS) } -pub fn get_account(storage: &dyn Storage, address: &str) -> Option { +fn account_balance(storage: &dyn Storage) -> ReadonlyBucket { + bucket_read(storage, PREFIX_ACCOUNT_BALANCE) +} + +fn account_balance_mut(storage: &mut dyn Storage) -> Bucket { + bucket(storage, PREFIX_ACCOUNT_BALANCE) +} + +pub fn get_account(storage: &dyn Storage, address: &Addr) -> Option { // Due to using may_load this should be safe to unwrap accounts(storage).may_load(address.as_bytes()).unwrap() } +pub fn set_account( + storage: &mut dyn Storage, + account: PeriodicVestingAccount, +) -> Result<(), ContractError> { + Ok(accounts_mut(storage).save(account.address().as_bytes(), &account)?) +} + pub fn get_account_delegations( storage: &dyn Storage, address: &str, @@ -58,12 +72,26 @@ pub fn get_account_delegations( pub fn set_account_delegations( storage: &mut dyn Storage, - address: &str, + address: &Addr, delegations: HashMap>, ) -> StdResult<()> { account_delegations_mut(storage).save(address.as_bytes(), &delegations) } +pub fn get_account_balance(storage: &dyn Storage, address: &Addr) -> Option { + // Due to using may_load this should be safe to unwrap + account_balance(storage) + .may_load(address.as_bytes()) + .unwrap() +} + +pub fn set_account_balance( + storage: &mut dyn Storage, + address: &Addr, + balance: Uint128, +) -> StdResult<()> { + account_balance_mut(storage).save(address.as_bytes(), &balance) +} #[cfg(test)] mod tests { use super::*; diff --git a/contracts/vesting/src/vesting.rs b/contracts/vesting/src/vesting.rs index 9a0aead897..68e5930e7c 100644 --- a/contracts/vesting/src/vesting.rs +++ b/contracts/vesting/src/vesting.rs @@ -1,25 +1,27 @@ use crate::contract::{NUM_VESTING_PERIODS, VESTING_PERIOD}; use crate::errors::ContractError; -use crate::storage::{get_account_delegations, set_account_delegations}; +use crate::storage::{ + get_account_balance, get_account_delegations, set_account_balance, set_account_delegations, +}; use config::defaults::{DEFAULT_MIXNET_CONTRACT_ADDRESS, DENOM}; -use cosmwasm_std::{Addr, Coin, DepsMut, Env, Timestamp, Uint128}; +use cosmwasm_std::{Addr, Coin, Deps, DepsMut, Env, Timestamp, Uint128}; use mixnet_contract::ExecuteMsg as MixnetExecuteMsg; use mixnet_contract::IdentityKey; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::collections::HashMap; -trait VestingAccount { +pub trait VestingAccount { // locked_coins returns the set of coins that are not spendable (i.e. locked), // defined as the vesting coins that are not delegated. // // To get spendable coins of a vesting account, first the total balance must // be retrieved and the locked tokens can be subtracted from the total balance. // Note, the spendable balance can be negative. - fn locked_coins(&self, block_time: Timestamp, env: Env, deps: DepsMut) -> Coin; + fn locked_coins(&self, block_time: Timestamp, env: Env, deps: Deps) -> Coin; // Calculates the total spendable balance that can be sent to other accounts. - fn spendable_coins(&self, block_time: Timestamp, env: Env, deps: DepsMut) -> Coin; + fn spendable_coins(&self, block_time: Timestamp, env: Env, deps: Deps) -> Coin; fn get_vested_coins(&self, block_time: Timestamp) -> Coin; fn get_vesting_coins(&self, block_time: Timestamp) -> Coin; @@ -28,17 +30,16 @@ trait VestingAccount { fn get_end_time(&self) -> Timestamp; fn get_original_vesting(&self) -> Coin; - fn get_delegated_free(&self, env: Env, deps: DepsMut) -> Coin; - fn get_delegated_vesting(&self, env: Env, deps: DepsMut) -> Coin; + fn get_delegated_free(&self, env: Env, deps: Deps) -> Coin; + fn get_delegated_vesting(&self, env: Env, deps: Deps) -> Coin; } -trait DelegationAccount { - // TODO: Add delegate on behalf messages in the mixnet contract. - // As the vesting account is delegating on behalf of the token holders/vesters +pub trait DelegationAccount { fn try_delegate_to_mixnode( &self, mix_identity: IdentityKey, amount: Coin, + env: Env, deps: DepsMut, ) -> Result<(), ContractError>; @@ -61,7 +62,11 @@ trait DelegationAccount { ) -> Result<(), ContractError>; // track_undelegation performs internal vesting accounting necessary when a // vesting account performs an undelegation. - fn track_undelegation(&self, mix_identity: IdentityKey, deps: DepsMut); + fn track_undelegation( + &self, + mix_identity: IdentityKey, + deps: DepsMut, + ) -> Result<(), ContractError>; } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] @@ -95,14 +100,21 @@ impl PeriodicVestingAccount { coin: Coin, start_time: Timestamp, periods: Vec, - // TODO: Store coins into contract to keep track of current balance. - ) -> Self { - PeriodicVestingAccount { + deps: &mut DepsMut, + ) -> Result { + let amount = coin.amount; + let account = PeriodicVestingAccount { address, start_time, periods, coin, - } + }; + set_account_balance(deps.storage, &account.address, amount)?; + Ok(account) + } + + pub fn address(&self) -> Addr { + self.address.clone() } pub fn tokens_per_period(&self) -> u128 { @@ -129,12 +141,7 @@ impl PeriodicVestingAccount { } } - fn get_delegated_with_op( - &self, - op: &dyn Fn(u64, u64) -> bool, - env: Env, - deps: DepsMut, - ) -> Coin { + fn get_delegated_with_op(&self, op: &dyn Fn(u64, u64) -> bool, env: Env, deps: Deps) -> Coin { let period = self.get_current_vesting_period(env.block.time); if period == 0 { return Coin { @@ -170,8 +177,8 @@ impl PeriodicVestingAccount { } } - fn get_balance(&self) -> Coin { - unimplemented!() + fn get_balance(&self, deps: Deps) -> Uint128 { + get_account_balance(deps.storage, &self.address).unwrap_or(Uint128(0)) } } @@ -180,15 +187,17 @@ impl DelegationAccount for PeriodicVestingAccount { &self, mix_identity: IdentityKey, coin: Coin, + env: Env, deps: DepsMut, ) -> Result<(), ContractError> { let querier = deps.querier; let msg = MixnetExecuteMsg::DelegateToMixnodeOnBehalf { - mix_identity, + mix_identity: mix_identity.clone(), delegate_addr: self.address.clone(), - coin, + coin: coin.clone(), }; querier.query_wasm_smart(DEFAULT_MIXNET_CONTRACT_ADDRESS, &msg)?; + self.track_delegation(env.block.time, mix_identity, coin, deps)?; Ok(()) } @@ -199,10 +208,11 @@ impl DelegationAccount for PeriodicVestingAccount { ) -> Result<(), ContractError> { let querier = deps.querier; let msg = MixnetExecuteMsg::UnDelegateFromMixnodeOnBehalf { - mix_identity, + mix_identity: mix_identity.clone(), delegate_addr: self.address.clone(), }; querier.query_wasm_smart(DEFAULT_MIXNET_CONTRACT_ADDRESS, &msg)?; + self.track_undelegation(mix_identity, deps)?; Ok(()) } @@ -220,25 +230,36 @@ impl DelegationAccount for PeriodicVestingAccount { } else { HashMap::new() }; - let delegation = delegations.entry(mix_identity).or_insert_with(Vec::new); + let delegation = delegations + .entry(mix_identity) + .or_insert_with(Vec::new); delegation.push(DelegationData { amount: delegation_amount.amount, block_time, }); - set_account_delegations(deps.storage, self.address.as_str(), delegations)?; + set_account_delegations(deps.storage, &self.address, delegations)?; Ok(()) } - fn track_undelegation(&self, mix_identity: IdentityKey, deps: DepsMut) { + fn track_undelegation( + &self, + mix_identity: IdentityKey, + deps: DepsMut, + ) -> Result<(), ContractError> { // This has to exist in storage at this point. let mut delegations = get_account_delegations(deps.storage, self.address.as_str()).unwrap(); // Since we're always removing the entire delegation we can just drop the key delegations.remove(&mix_identity); + Ok(set_account_delegations( + deps.storage, + &self.address, + delegations, + )?) } } impl VestingAccount for PeriodicVestingAccount { - fn locked_coins(&self, block_time: Timestamp, env: Env, deps: DepsMut) -> Coin { + fn locked_coins(&self, block_time: Timestamp, env: Env, deps: Deps) -> Coin { // Returns 0 in case of underflow. Coin { amount: Uint128( @@ -251,11 +272,10 @@ impl VestingAccount for PeriodicVestingAccount { } } - fn spendable_coins(&self, block_time: Timestamp, env: Env, deps: DepsMut) -> Coin { + fn spendable_coins(&self, block_time: Timestamp, env: Env, deps: Deps) -> Coin { Coin { amount: Uint128( - self.get_balance() - .amount + self.get_balance(deps) .u128() .saturating_sub(self.locked_coins(block_time, env, deps).amount.u128()), ), @@ -305,11 +325,11 @@ impl VestingAccount for PeriodicVestingAccount { self.coin.clone() } - fn get_delegated_free(&self, env: Env, deps: DepsMut) -> Coin { + fn get_delegated_free(&self, env: Env, deps: Deps) -> Coin { self.get_delegated_with_op(<, env, deps) } - fn get_delegated_vesting(&self, env: Env, deps: DepsMut) -> Coin { + fn get_delegated_vesting(&self, env: Env, deps: Deps) -> Coin { self.get_delegated_with_op(&ge, env, deps) } }