From aa438c0d621d57ff75e3d1d86b04000a76820c26 Mon Sep 17 00:00:00 2001 From: Shoaib Ahmed Date: Tue, 7 May 2024 15:06:09 -0700 Subject: [PATCH] Obligato liquidity prototype (#21) --- .../contracts/cw-tee-mtcs/Cargo.toml | 2 + .../contracts/cw-tee-mtcs/src/contract.rs | 143 ++++++++++++++++-- .../contracts/cw-tee-mtcs/src/error.rs | 10 ++ .../contracts/cw-tee-mtcs/src/msg.rs | 35 ++++- .../contracts/cw-tee-mtcs/src/state.rs | 20 ++- 5 files changed, 190 insertions(+), 20 deletions(-) diff --git a/bisenzone-cw-mvp/contracts/cw-tee-mtcs/Cargo.toml b/bisenzone-cw-mvp/contracts/cw-tee-mtcs/Cargo.toml index 0e33e50..13529be 100644 --- a/bisenzone-cw-mvp/contracts/cw-tee-mtcs/Cargo.toml +++ b/bisenzone-cw-mvp/contracts/cw-tee-mtcs/Cargo.toml @@ -47,6 +47,8 @@ cosmwasm-std = { version = "1.5.0", features = [ # "cosmwasm_1_4", ] } cw-storage-plus = "1.1.0" +cw20-base = { version = "1.1.1", features = ["library"] } +cw20 = "1.1.1" cw2 = "1.1.1" hex = { version = "0.4.3", default-features = false } k256 = { version = "0.13.2", default-features = false, features = ["ecdsa"] } diff --git a/bisenzone-cw-mvp/contracts/cw-tee-mtcs/src/contract.rs b/bisenzone-cw-mvp/contracts/cw-tee-mtcs/src/contract.rs index 46b9ba8..a6049f6 100644 --- a/bisenzone-cw-mvp/contracts/cw-tee-mtcs/src/contract.rs +++ b/bisenzone-cw-mvp/contracts/cw-tee-mtcs/src/contract.rs @@ -1,9 +1,16 @@ -use cosmwasm_std::{entry_point, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; +use cosmwasm_std::{ + entry_point, to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult, + Uint128, +}; use cw2::set_contract_version; +use cw20_base::contract::execute_mint; +use cw20_base::contract::query_balance as cw20_query_balance; +use cw20_base::state::{MinterData, TokenInfo, TOKEN_INFO}; use quartz_cw::handler::RawHandler; +use quartz_cw::state::EPOCH_COUNTER; use crate::error::ContractError; -use crate::msg::execute::{SubmitObligationMsg, SubmitSetoffsMsg}; +use crate::msg::execute::{SubmitObligationMsg, SubmitObligationsMsg, SubmitSetoffsMsg}; use crate::msg::QueryMsg; use crate::msg::{ExecuteMsg, InstantiateMsg}; use crate::state::{current_epoch_key, ObligationsItem, State, OBLIGATIONS_KEY, STATE}; @@ -28,9 +35,54 @@ pub fn instantiate( set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; STATE.save(deps.storage, &state)?; + EPOCH_COUNTER.save(deps.storage, &1)?; + ObligationsItem::new(¤t_epoch_key(OBLIGATIONS_KEY, deps.storage)?) .save(deps.storage, &Default::default())?; + // store token info using cw20-base format + let data = TokenInfo { + name: "USD".to_string(), + symbol: "!$".to_string(), + decimals: 0, + total_supply: Uint128::zero(), + // set self as minter, so we can properly execute mint and burn + mint: Some(MinterData { + minter: env.contract.address.clone(), + cap: None, + }), + }; + TOKEN_INFO.save(deps.storage, &data)?; + + let info = MessageInfo { + sender: env.contract.address.clone(), + funds: vec![], + }; + + execute_mint( + deps.branch(), + env.clone(), + info.clone(), + "wasm1qv9nel6lwtrq5jmwruxfndqw7ejskn5ysz53hp".to_owned(), + Uint128::new(1000), + )?; + + execute_mint( + deps.branch(), + env.clone(), + info.clone(), + "wasm1tfxrdcj5kk6rewzmmkku4d9htpjqr0kk6lcftv".to_owned(), + Uint128::new(1000), + )?; + + execute_mint( + deps.branch(), + env.clone(), + info.clone(), + "wasm1gjg72awjl7jvtmq4kjqp3al9p6crstpar8wgn5".to_owned(), + Uint128::new(1000), + )?; + Ok(Response::new() .add_attribute("method", "instantiate") .add_attribute("owner", info.sender)) @@ -38,7 +90,7 @@ pub fn instantiate( #[cfg_attr(not(feature = "library"), entry_point)] pub fn execute( - deps: DepsMut, + mut deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg, @@ -48,21 +100,30 @@ pub fn execute( ExecuteMsg::SubmitObligation(SubmitObligationMsg { ciphertext, digest }) => { execute::submit_obligation(deps, ciphertext, digest) } - ExecuteMsg::SubmitSetoffs(SubmitSetoffsMsg { setoffs_enc }) => { - execute::submit_setoffs(deps, setoffs_enc) + ExecuteMsg::SubmitObligations(SubmitObligationsMsg(obligations)) => { + for o in obligations { + execute::submit_obligation(deps.branch(), o.ciphertext, o.digest)?; + } + Ok(Response::new()) } + ExecuteMsg::SubmitSetoffs(SubmitSetoffsMsg { setoffs_enc }) => { + execute::submit_setoffs(deps, env, info, setoffs_enc) + } + ExecuteMsg::InitClearing => execute::init_clearing(deps), } } pub mod execute { use std::collections::BTreeMap; - use cosmwasm_std::{DepsMut, HexBinary, Response}; + use cosmwasm_std::{DepsMut, Env, HexBinary, MessageInfo, Response, StdResult}; + use cw20_base::contract::{execute_burn, execute_mint}; use quartz_cw::state::Hash; + use quartz_cw::state::EPOCH_COUNTER; use crate::state::{ - current_epoch_key, ObligationsItem, RawCipherText, RawHash, SetoffsItem, OBLIGATIONS_KEY, - SETOFFS_KEY, + current_epoch_key, previous_epoch_key, ObligationsItem, RawHash, SetoffsItem, SettleOff, + OBLIGATIONS_KEY, SETOFFS_KEY, }; use crate::ContractError; @@ -91,18 +152,74 @@ pub mod execute { } pub fn submit_setoffs( - deps: DepsMut, - setoffs_enc: BTreeMap, + mut deps: DepsMut, + env: Env, + _info: MessageInfo, + setoffs_enc: BTreeMap, ) -> Result { // store the `BTreeMap` - SetoffsItem::new(¤t_epoch_key(SETOFFS_KEY, deps.storage)?) + SetoffsItem::new(&previous_epoch_key(SETOFFS_KEY, deps.storage)?) .save(deps.storage, &setoffs_enc)?; + for (_, so) in setoffs_enc { + if let SettleOff::Transfer(t) = so { + let info = MessageInfo { + sender: env.contract.address.clone(), + funds: vec![], + }; + + execute_mint( + deps.branch(), + env.clone(), + info.clone(), + t.payee.to_string(), + t.amount.into(), + )?; + + let payer = deps.api.addr_validate(&t.payer.to_string())?; + let info = MessageInfo { + sender: payer, + funds: vec![], + }; + + execute_burn(deps.branch(), env.clone(), info, t.amount.into())?; + } + } + Ok(Response::new().add_attribute("action", "submit_setoffs")) } + + pub fn init_clearing(deps: DepsMut) -> Result { + EPOCH_COUNTER.update(deps.storage, |mut counter| -> StdResult<_> { + counter += 1; + Ok(counter) + })?; + Ok(Response::new().add_attribute("action", "init_clearing")) + } } #[cfg_attr(not(feature = "library"), entry_point)] -pub fn query(_deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { - match msg {} +pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { + match msg { + QueryMsg::GetAllSetoffs => to_json_binary(&query::get_all_setoffs(deps)?), + QueryMsg::Balance { address } => to_json_binary(&cw20_query_balance(deps, address)?), + } +} + +pub mod query { + use cosmwasm_std::Deps; + use cosmwasm_std::StdResult; + + use crate::msg::GetAllSetoffsResponse; + use crate::state::previous_epoch_key; + use crate::state::SetoffsItem; + use crate::state::SETOFFS_KEY; + + pub fn get_all_setoffs(deps: Deps) -> StdResult { + let setoffs = SetoffsItem::new(&previous_epoch_key(SETOFFS_KEY, deps.storage)?) + .load(deps.storage)? + .into_iter() + .collect(); + Ok(GetAllSetoffsResponse { setoffs }) + } } diff --git a/bisenzone-cw-mvp/contracts/cw-tee-mtcs/src/error.rs b/bisenzone-cw-mvp/contracts/cw-tee-mtcs/src/error.rs index 9683c23..ff82711 100644 --- a/bisenzone-cw-mvp/contracts/cw-tee-mtcs/src/error.rs +++ b/bisenzone-cw-mvp/contracts/cw-tee-mtcs/src/error.rs @@ -1,4 +1,5 @@ use cosmwasm_std::StdError; +use cw20_base::ContractError as Cw20ContractError; use hex::FromHexError; use k256::ecdsa::Error as K256Error; use quartz_cw::error::Error as QuartzError; @@ -26,6 +27,9 @@ pub enum ContractError { #[error("Invalid length")] BadLength, + + #[error("Cw20 error: {0}")] + Cw20(Cw20ContractError), } impl From for ContractError { @@ -33,3 +37,9 @@ impl From for ContractError { Self::K256(e) } } + +impl From for ContractError { + fn from(e: Cw20ContractError) -> Self { + Self::Cw20(e) + } +} diff --git a/bisenzone-cw-mvp/contracts/cw-tee-mtcs/src/msg.rs b/bisenzone-cw-mvp/contracts/cw-tee-mtcs/src/msg.rs index fff496e..b2a356a 100644 --- a/bisenzone-cw-mvp/contracts/cw-tee-mtcs/src/msg.rs +++ b/bisenzone-cw-mvp/contracts/cw-tee-mtcs/src/msg.rs @@ -1,12 +1,13 @@ use std::collections::BTreeMap; use cosmwasm_schema::{cw_serde, QueryResponses}; +use cosmwasm_std::HexBinary; use quartz_cw::prelude::*; -use crate::state::{RawCipherText, RawHash}; +use crate::state::RawHash; +use crate::state::SettleOff; #[cw_serde] -#[serde(transparent)] pub struct InstantiateMsg(pub QuartzInstantiateMsg); #[cw_serde] @@ -14,12 +15,12 @@ pub struct InstantiateMsg(pub QuartzInstantiateMsg); pub enum ExecuteMsg { Quartz(QuartzExecuteMsg), SubmitObligation(execute::SubmitObligationMsg), + SubmitObligations(execute::SubmitObligationsMsg), SubmitSetoffs(execute::SubmitSetoffsMsg), + InitClearing, } pub mod execute { - use cosmwasm_std::HexBinary; - use super::*; #[cw_serde] @@ -30,15 +31,37 @@ pub mod execute { // pub proof: π } + #[cw_serde] + #[serde(transparent)] + pub struct SubmitObligationsMsg(pub Vec); + + #[cw_serde] + pub struct SubmitTenderMsg { + pub ciphertext: HexBinary, + pub digest: HexBinary, + // pub proof: π + } + #[cw_serde] pub struct SubmitSetoffsMsg { - pub setoffs_enc: BTreeMap, + pub setoffs_enc: BTreeMap, // pub proof: π, } } #[cw_serde] #[derive(QueryResponses)] -pub enum QueryMsg {} +pub enum QueryMsg { + #[returns(GetAllSetoffsResponse)] + GetAllSetoffs, + #[returns(cw20::BalanceResponse)] + Balance { address: String }, +} + +// We define a custom struct for each query response +#[cw_serde] +pub struct GetAllSetoffsResponse { + pub setoffs: Vec<(HexBinary, SettleOff)>, +} #[cfg(test)] mod tests { diff --git a/bisenzone-cw-mvp/contracts/cw-tee-mtcs/src/state.rs b/bisenzone-cw-mvp/contracts/cw-tee-mtcs/src/state.rs index f49ac64..c8a62a5 100644 --- a/bisenzone-cw-mvp/contracts/cw-tee-mtcs/src/state.rs +++ b/bisenzone-cw-mvp/contracts/cw-tee-mtcs/src/state.rs @@ -9,13 +9,27 @@ pub type RawHash = HexBinary; pub type RawCipherText = HexBinary; pub type ObligationsItem<'a> = Item<'a, BTreeMap>; -pub type SetoffsItem<'a> = Item<'a, BTreeMap>; +pub type SetoffsItem<'a> = Item<'a, BTreeMap>; #[cw_serde] pub struct State { pub owner: String, } +#[cw_serde] +pub struct Transfer { + pub payer: String, + pub payee: String, + pub amount: u64, +} + +#[cw_serde] +#[serde(untagged)] +pub enum SettleOff { + SetOff(Vec), + Transfer(Transfer), +} + pub const STATE: Item = Item::new("state"); pub const OBLIGATIONS_KEY: &str = "obligations"; pub const SETOFFS_KEY: &str = "setoffs"; @@ -23,3 +37,7 @@ pub const SETOFFS_KEY: &str = "setoffs"; pub fn current_epoch_key(key: &str, storage: &dyn Storage) -> Result { Ok(format!("{}/{key}", EPOCH_COUNTER.load(storage)?)) } + +pub fn previous_epoch_key(key: &str, storage: &dyn Storage) -> Result { + Ok(format!("{}/{key}", EPOCH_COUNTER.load(storage)? - 1)) +}