Use Map to store utilization

This commit is contained in:
hu55a1n1 2023-11-15 07:16:42 -08:00
parent 4b343ea6ff
commit cdb09737a2
3 changed files with 76 additions and 64 deletions

View file

@ -2,11 +2,10 @@ use cosmwasm_std::{
entry_point, to_json_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult, entry_point, to_json_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult,
}; };
use cw2::set_contract_version; use cw2::set_contract_version;
use itertools::Itertools;
use crate::error::ContractError; use crate::error::ContractError;
use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg};
use crate::state::{State, STATE}; use crate::state::{State, STATE, UTILIZATION};
// version info for migration info // version info for migration info
const CONTRACT_NAME: &str = "crates.io:cw-mtcs"; const CONTRACT_NAME: &str = "crates.io:cw-mtcs";
@ -20,8 +19,7 @@ pub fn instantiate(
_msg: InstantiateMsg, _msg: InstantiateMsg,
) -> Result<Response, ContractError> { ) -> Result<Response, ContractError> {
let state = State { let state = State {
utilization: Default::default(), owner: info.sender.to_string(),
owner: info.sender.clone(),
}; };
set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
STATE.save(deps.storage, &state)?; STATE.save(deps.storage, &state)?;
@ -49,29 +47,32 @@ pub fn execute(
} }
pub mod execute { pub mod execute {
use cosmwasm_std::Uint128;
use super::*; use super::*;
pub fn upload_obligation( pub fn upload_obligation(
deps: DepsMut, deps: DepsMut,
info: MessageInfo, info: MessageInfo,
creditor: Addr, creditor: String,
amount: u64, amount: Uint128,
memo: String, memo: String,
) -> Result<Response, ContractError> { ) -> Result<Response, ContractError> {
STATE.update(deps.storage, |mut state| -> Result<_, ContractError> { let creditor = deps.api.addr_validate(&creditor)?;
// Uncomment if we want to only allow ourselves to add obligations
// if info.sender != state.owner {
// return Err(ContractError::Unauthorized);
// }
*state UTILIZATION.update(
.utilization deps.storage,
.entry(creditor) (&creditor, &info.sender),
.or_default() |utilization| -> Result<_, ContractError> {
.entry(info.sender) // Uncomment if we want to only allow ourselves to add obligations
.or_default() += amount; // if info.sender != state.owner {
Ok(state) // return Err(ContractError::Unauthorized);
})?; // }
let utilization = utilization.unwrap_or_default() + amount;
Ok(utilization)
},
)?;
Ok(Response::new() Ok(Response::new()
.add_attribute("action", "upload_obligation") .add_attribute("action", "upload_obligation")
@ -80,38 +81,46 @@ pub mod execute {
pub fn apply_cycle( pub fn apply_cycle(
deps: DepsMut, deps: DepsMut,
path: Vec<Addr>, path: Vec<String>,
amount: u64, amount: Uint128,
) -> Result<Response, ContractError> { ) -> Result<Response, ContractError> {
let mut volume_cleared = 0; let mut volume_cleared = Uint128::zero();
STATE.update(deps.storage, |mut state| -> Result<_, ContractError> { let path = path
validate_cycle(&path, amount, &state)?; .into_iter()
.map(|addr| deps.api.addr_validate(&addr))
.collect::<StdResult<Vec<Addr>>>()?;
for (from, to) in path.into_iter().tuples() { validate_cycle(&path, amount, &deps)?;
*state
.utilization
.get_mut(&to)
.unwrap()
.get_mut(&from)
.unwrap() -= amount;
volume_cleared += amount;
}
Ok(state) for from_to in path.windows(2) {
})?; let (from, to) = (&from_to[0], &from_to[1]);
UTILIZATION.update(
deps.storage,
(&to, &from),
|utilization| -> Result<_, ContractError> {
let utilization = utilization.unwrap_or_default() - amount;
volume_cleared += amount;
Ok(utilization)
},
)?;
}
Ok(Response::new() Ok(Response::new()
.add_attribute("action", "apply_cycle") .add_attribute("action", "apply_cycle")
.add_attribute("volume_cleared", format!("{}", volume_cleared))) .add_attribute("volume_cleared", format!("{}", volume_cleared)))
} }
fn validate_cycle(path: &[Addr], amount: u64, state: &State) -> Result<(), ContractError> { fn validate_cycle(path: &[Addr], amount: Uint128, deps: &DepsMut) -> Result<(), ContractError> {
if path.first() != path.last() { if path.first() != path.last() {
return Err(ContractError::PathNotCycle); return Err(ContractError::PathNotCycle);
} }
for (from, to) in path.iter().tuples() { for from_to in path.windows(2) {
if amount > state.utilization[to][from] { let (from, to) = (&from_to[0], &from_to[1]);
if amount > UTILIZATION.load(deps.storage, (to, from))? {
return Err(ContractError::ClearingTooMuch); return Err(ContractError::ClearingTooMuch);
} }
} }
@ -131,18 +140,26 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
pub mod query { pub mod query {
use super::*; use super::*;
use std::collections::HashMap; use cosmwasm_std::Order;
use crate::msg::GetObligationsResponse; use crate::msg::GetObligationsResponse;
use crate::state::UTILIZATION;
pub fn get_obligations(deps: Deps, creditor: Addr) -> StdResult<GetObligationsResponse> { pub fn get_obligations(deps: Deps, creditor: String) -> StdResult<GetObligationsResponse> {
let state = STATE.load(deps.storage)?; let creditor = deps.api.addr_validate(&creditor)?;
let keys = UTILIZATION
.keys(deps.storage, None, None, Order::Ascending)
.collect::<StdResult<Vec<(Addr, Addr)>>>()?
.into_iter()
.filter(|(from, _)| from == creditor);
Ok(GetObligationsResponse { Ok(GetObligationsResponse {
obligations: state obligations: keys
.utilization .map(|(from, to)| {
.get(&creditor) let utilization = UTILIZATION.load(deps.storage, (&from, &to)).unwrap();
.unwrap_or(&HashMap::new()) (to.to_string(), utilization)
.clone(), })
.collect(),
}) })
} }
} }

View file

@ -1,7 +1,5 @@
use std::collections::HashMap;
use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_schema::{cw_serde, QueryResponses};
use cosmwasm_std::Addr; use cosmwasm_std::Uint128;
#[cw_serde] #[cw_serde]
pub struct InstantiateMsg; pub struct InstantiateMsg;
@ -9,13 +7,13 @@ pub struct InstantiateMsg;
#[cw_serde] #[cw_serde]
pub enum ExecuteMsg { pub enum ExecuteMsg {
UploadObligation { UploadObligation {
creditor: Addr, creditor: String,
amount: u64, amount: Uint128,
memo: String, memo: String,
}, },
ApplyCycle { ApplyCycle {
path: Vec<Addr>, path: Vec<String>,
amount: u64, amount: Uint128,
}, },
} }
@ -24,11 +22,11 @@ pub enum ExecuteMsg {
pub enum QueryMsg { pub enum QueryMsg {
// GetCount returns the current count as a json-encoded number // GetCount returns the current count as a json-encoded number
#[returns(GetObligationsResponse)] #[returns(GetObligationsResponse)]
GetObligations { creditor: Addr }, GetObligations { creditor: String },
} }
// We define a custom struct for each query response // We define a custom struct for each query response
#[cw_serde] #[cw_serde]
pub struct GetObligationsResponse { pub struct GetObligationsResponse {
pub obligations: HashMap<Addr, u64>, pub obligations: Vec<(String, Uint128)>,
} }

View file

@ -1,14 +1,11 @@
use std::collections::HashMap; use cosmwasm_schema::cw_serde;
use cosmwasm_std::{Addr, Uint128};
use cw_storage_plus::{Item, Map};
use cosmwasm_std::Addr; #[cw_serde]
use cw_storage_plus::Item;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct State { pub struct State {
pub utilization: HashMap<Addr, HashMap<Addr, u64>>, pub owner: String,
pub owner: Addr,
} }
pub const STATE: Item<State> = Item::new("state"); pub const STATE: Item<State> = Item::new("state");
pub const UTILIZATION: Map<(&Addr, &Addr), Uint128> = Map::new("utilization");