Implement core logic

This commit is contained in:
hu55a1n1 2023-11-15 00:54:43 -08:00
parent 855c955ffe
commit 4a916357a6
5 changed files with 116 additions and 37 deletions

View file

@ -1,10 +1,11 @@
#[cfg(not(feature = "library"))] use cosmwasm_std::{
use cosmwasm_std::entry_point; entry_point, to_json_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult,
use cosmwasm_std::{to_json_binary, 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, GetCountResponse, InstantiateMsg, QueryMsg}; use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg};
use crate::state::{State, STATE}; use crate::state::{State, STATE};
// version info for migration info // version info for migration info
@ -16,10 +17,10 @@ pub fn instantiate(
deps: DepsMut, deps: DepsMut,
_env: Env, _env: Env,
info: MessageInfo, info: MessageInfo,
msg: InstantiateMsg, _msg: InstantiateMsg,
) -> Result<Response, ContractError> { ) -> Result<Response, ContractError> {
let state = State { let state = State {
count: msg.count, utilization: Default::default(),
owner: info.sender.clone(), owner: info.sender.clone(),
}; };
set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
@ -27,8 +28,7 @@ pub fn instantiate(
Ok(Response::new() Ok(Response::new()
.add_attribute("method", "instantiate") .add_attribute("method", "instantiate")
.add_attribute("owner", info.sender) .add_attribute("owner", info.sender))
.add_attribute("count", msg.count.to_string()))
} }
#[cfg_attr(not(feature = "library"), entry_point)] #[cfg_attr(not(feature = "library"), entry_point)]
@ -39,48 +39,106 @@ pub fn execute(
msg: ExecuteMsg, msg: ExecuteMsg,
) -> Result<Response, ContractError> { ) -> Result<Response, ContractError> {
match msg { match msg {
ExecuteMsg::Increment {} => execute::increment(deps), ExecuteMsg::UploadObligation {
ExecuteMsg::Reset { count } => execute::reset(deps, info, count), creditor,
amount,
memo,
} => execute::upload_obligation(deps, info, creditor, amount, memo),
ExecuteMsg::ApplyCycle { path, amount } => execute::apply_cycle(deps, path, amount),
} }
} }
pub mod execute { pub mod execute {
use super::*; use super::*;
pub fn increment(deps: DepsMut) -> Result<Response, ContractError> { pub fn upload_obligation(
deps: DepsMut,
info: MessageInfo,
creditor: Addr,
amount: u64,
memo: String,
) -> Result<Response, ContractError> {
STATE.update(deps.storage, |mut state| -> Result<_, ContractError> { 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(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<Response, ContractError> { pub fn apply_cycle(
deps: DepsMut,
path: Vec<Addr>,
amount: u64,
) -> Result<Response, ContractError> {
let mut volume_cleared = 0;
STATE.update(deps.storage, |mut state| -> Result<_, ContractError> { STATE.update(deps.storage, |mut state| -> Result<_, ContractError> {
if info.sender != state.owner { validate_cycle(&path, amount, &state)?;
return Err(ContractError::Unauthorized {});
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(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)] #[cfg_attr(not(feature = "library"), entry_point)]
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> { pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
match msg { match msg {
QueryMsg::GetCount {} => to_json_binary(&query::count(deps)?), QueryMsg::GetObligations { creditor } => {
to_json_binary(&query::get_obligations(deps, creditor)?)
}
} }
} }
pub mod query { pub mod query {
use super::*; use super::*;
pub fn count(deps: Deps) -> StdResult<GetCountResponse> { use crate::msg::GetObligationsResponse;
pub fn get_obligations(deps: Deps, creditor: Addr) -> StdResult<GetObligationsResponse> {
let state = STATE.load(deps.storage)?; let state = STATE.load(deps.storage)?;
Ok(GetCountResponse { count: state.count }) Ok(GetObligationsResponse {
obligations: state.utilization[&creditor].clone(),
})
} }
} }

View file

@ -7,7 +7,11 @@ pub enum ContractError {
Std(#[from] StdError), Std(#[from] StdError),
#[error("Unauthorized")] #[error("Unauthorized")]
Unauthorized {}, Unauthorized,
// Add any other custom errors you like here.
// Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details. #[error("Specified path does not form a cycle")]
PathNotCycle,
#[error("Amount is greater than utilization")]
ClearingTooMuch,
} }

View file

@ -1,6 +1,14 @@
#![deny(
warnings,
trivial_casts,
trivial_numeric_casts,
unused_import_braces,
unused_qualifications
)]
#![forbid(unsafe_code)]
pub mod contract; pub mod contract;
mod error; mod error;
pub mod helpers;
pub mod integration_tests; pub mod integration_tests;
pub mod msg; pub mod msg;
pub mod state; pub mod state;

View file

@ -1,26 +1,34 @@
use std::collections::HashMap;
use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_schema::{cw_serde, QueryResponses};
use cosmwasm_std::Addr;
#[cw_serde] #[cw_serde]
pub struct InstantiateMsg { pub struct InstantiateMsg;
pub count: i32,
}
#[cw_serde] #[cw_serde]
pub enum ExecuteMsg { pub enum ExecuteMsg {
Increment {}, UploadObligation {
Reset { count: i32 }, creditor: Addr,
amount: u64,
memo: String,
},
ApplyCycle {
path: Vec<Addr>,
amount: u64,
},
} }
#[cw_serde] #[cw_serde]
#[derive(QueryResponses)] #[derive(QueryResponses)]
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(GetCountResponse)] #[returns(GetObligationsResponse)]
GetCount {}, GetObligations { creditor: Addr },
} }
// We define a custom struct for each query response // We define a custom struct for each query response
#[cw_serde] #[cw_serde]
pub struct GetCountResponse { pub struct GetObligationsResponse {
pub count: i32, pub obligations: HashMap<Addr, u64>,
} }

View file

@ -1,12 +1,13 @@
use schemars::JsonSchema; use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use cosmwasm_std::Addr; use cosmwasm_std::Addr;
use cw_storage_plus::Item; use cw_storage_plus::Item;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct State { pub struct State {
pub count: i32, pub utilization: HashMap<Addr, HashMap<Addr, u64>>,
pub owner: Addr, pub owner: Addr,
} }