diff --git a/bisenzone-cw-mvp/src/contract.rs b/bisenzone-cw-mvp/src/contract.rs index 2a460b9..9109826 100644 --- a/bisenzone-cw-mvp/src/contract.rs +++ b/bisenzone-cw-mvp/src/contract.rs @@ -1,10 +1,11 @@ -#[cfg(not(feature = "library"))] -use cosmwasm_std::entry_point; -use cosmwasm_std::{to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; +use cosmwasm_std::{ + entry_point, to_json_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult, +}; use cw2::set_contract_version; +use itertools::Itertools; use crate::error::ContractError; -use crate::msg::{ExecuteMsg, GetCountResponse, InstantiateMsg, QueryMsg}; +use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; use crate::state::{State, STATE}; // version info for migration info @@ -16,10 +17,10 @@ pub fn instantiate( deps: DepsMut, _env: Env, info: MessageInfo, - msg: InstantiateMsg, + _msg: InstantiateMsg, ) -> Result { let state = State { - count: msg.count, + utilization: Default::default(), owner: info.sender.clone(), }; set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; @@ -27,8 +28,7 @@ pub fn instantiate( Ok(Response::new() .add_attribute("method", "instantiate") - .add_attribute("owner", info.sender) - .add_attribute("count", msg.count.to_string())) + .add_attribute("owner", info.sender)) } #[cfg_attr(not(feature = "library"), entry_point)] @@ -39,48 +39,106 @@ pub fn execute( msg: ExecuteMsg, ) -> Result { match msg { - ExecuteMsg::Increment {} => execute::increment(deps), - ExecuteMsg::Reset { count } => execute::reset(deps, info, count), + ExecuteMsg::UploadObligation { + creditor, + amount, + memo, + } => execute::upload_obligation(deps, info, creditor, amount, memo), + ExecuteMsg::ApplyCycle { path, amount } => execute::apply_cycle(deps, path, amount), } } pub mod execute { use super::*; - pub fn increment(deps: DepsMut) -> Result { + pub fn upload_obligation( + deps: DepsMut, + info: MessageInfo, + creditor: Addr, + amount: u64, + memo: String, + ) -> Result { STATE.update(deps.storage, |mut state| -> Result<_, ContractError> { - state.count += 1; + // Uncomment if we want to only allow ourselves to add obligations + // if info.sender != state.owner { + // return Err(ContractError::Unauthorized); + // } + + *state + .utilization + .get_mut(&creditor) + .unwrap() + .get_mut(&info.sender) + .unwrap() += amount; Ok(state) })?; - Ok(Response::new().add_attribute("action", "increment")) + Ok(Response::new() + .add_attribute("action", "upload_obligation") + .add_attribute("memo", memo)) } - pub fn reset(deps: DepsMut, info: MessageInfo, count: i32) -> Result { + pub fn apply_cycle( + deps: DepsMut, + path: Vec, + amount: u64, + ) -> Result { + let mut volume_cleared = 0; + STATE.update(deps.storage, |mut state| -> Result<_, ContractError> { - if info.sender != state.owner { - return Err(ContractError::Unauthorized {}); + validate_cycle(&path, amount, &state)?; + + for (from, to) in path.into_iter().tuples() { + *state + .utilization + .get_mut(&to) + .unwrap() + .get_mut(&from) + .unwrap() -= amount; + volume_cleared += amount; } - state.count = count; + Ok(state) })?; - Ok(Response::new().add_attribute("action", "reset")) + Ok(Response::new() + .add_attribute("action", "apply_cycle") + .add_attribute("volume_cleared", format!("{}", volume_cleared))) + } + + fn validate_cycle(path: &[Addr], amount: u64, state: &State) -> Result<(), ContractError> { + if path.first() != path.last() { + return Err(ContractError::PathNotCycle); + } + + for (from, to) in path.iter().tuples() { + if amount > state.utilization[to][from] { + return Err(ContractError::ClearingTooMuch); + } + } + + Ok(()) } } #[cfg_attr(not(feature = "library"), entry_point)] pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { match msg { - QueryMsg::GetCount {} => to_json_binary(&query::count(deps)?), + QueryMsg::GetObligations { creditor } => { + to_json_binary(&query::get_obligations(deps, creditor)?) + } } } pub mod query { use super::*; - pub fn count(deps: Deps) -> StdResult { + use crate::msg::GetObligationsResponse; + + pub fn get_obligations(deps: Deps, creditor: Addr) -> StdResult { let state = STATE.load(deps.storage)?; - Ok(GetCountResponse { count: state.count }) + Ok(GetObligationsResponse { + obligations: state.utilization[&creditor].clone(), + }) } } diff --git a/bisenzone-cw-mvp/src/error.rs b/bisenzone-cw-mvp/src/error.rs index 4a69d8f..6989d01 100644 --- a/bisenzone-cw-mvp/src/error.rs +++ b/bisenzone-cw-mvp/src/error.rs @@ -7,7 +7,11 @@ pub enum ContractError { Std(#[from] StdError), #[error("Unauthorized")] - Unauthorized {}, - // Add any other custom errors you like here. - // Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details. + Unauthorized, + + #[error("Specified path does not form a cycle")] + PathNotCycle, + + #[error("Amount is greater than utilization")] + ClearingTooMuch, } diff --git a/bisenzone-cw-mvp/src/lib.rs b/bisenzone-cw-mvp/src/lib.rs index d6185c4..e3f498e 100644 --- a/bisenzone-cw-mvp/src/lib.rs +++ b/bisenzone-cw-mvp/src/lib.rs @@ -1,6 +1,14 @@ +#![deny( + warnings, + trivial_casts, + trivial_numeric_casts, + unused_import_braces, + unused_qualifications +)] +#![forbid(unsafe_code)] + pub mod contract; mod error; -pub mod helpers; pub mod integration_tests; pub mod msg; pub mod state; diff --git a/bisenzone-cw-mvp/src/msg.rs b/bisenzone-cw-mvp/src/msg.rs index 0edfa32..261f5b7 100644 --- a/bisenzone-cw-mvp/src/msg.rs +++ b/bisenzone-cw-mvp/src/msg.rs @@ -1,26 +1,34 @@ +use std::collections::HashMap; + use cosmwasm_schema::{cw_serde, QueryResponses}; +use cosmwasm_std::Addr; #[cw_serde] -pub struct InstantiateMsg { - pub count: i32, -} +pub struct InstantiateMsg; #[cw_serde] pub enum ExecuteMsg { - Increment {}, - Reset { count: i32 }, + UploadObligation { + creditor: Addr, + amount: u64, + memo: String, + }, + ApplyCycle { + path: Vec, + amount: u64, + }, } #[cw_serde] #[derive(QueryResponses)] pub enum QueryMsg { // GetCount returns the current count as a json-encoded number - #[returns(GetCountResponse)] - GetCount {}, + #[returns(GetObligationsResponse)] + GetObligations { creditor: Addr }, } // We define a custom struct for each query response #[cw_serde] -pub struct GetCountResponse { - pub count: i32, +pub struct GetObligationsResponse { + pub obligations: HashMap, } diff --git a/bisenzone-cw-mvp/src/state.rs b/bisenzone-cw-mvp/src/state.rs index bad9202..2aede68 100644 --- a/bisenzone-cw-mvp/src/state.rs +++ b/bisenzone-cw-mvp/src/state.rs @@ -1,12 +1,13 @@ -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; +use std::collections::HashMap; use cosmwasm_std::Addr; use cw_storage_plus::Item; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] pub struct State { - pub count: i32, + pub utilization: HashMap>, pub owner: Addr, }