Obligato liquidity prototype (#21)

This commit is contained in:
Shoaib Ahmed 2024-05-07 15:06:09 -07:00 committed by GitHub
parent 57b2a050ff
commit aa438c0d62
5 changed files with 190 additions and 20 deletions

View file

@ -47,6 +47,8 @@ cosmwasm-std = { version = "1.5.0", features = [
# "cosmwasm_1_4", # "cosmwasm_1_4",
] } ] }
cw-storage-plus = "1.1.0" cw-storage-plus = "1.1.0"
cw20-base = { version = "1.1.1", features = ["library"] }
cw20 = "1.1.1"
cw2 = "1.1.1" cw2 = "1.1.1"
hex = { version = "0.4.3", default-features = false } hex = { version = "0.4.3", default-features = false }
k256 = { version = "0.13.2", default-features = false, features = ["ecdsa"] } k256 = { version = "0.13.2", default-features = false, features = ["ecdsa"] }

View file

@ -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 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::handler::RawHandler;
use quartz_cw::state::EPOCH_COUNTER;
use crate::error::ContractError; use crate::error::ContractError;
use crate::msg::execute::{SubmitObligationMsg, SubmitSetoffsMsg}; use crate::msg::execute::{SubmitObligationMsg, SubmitObligationsMsg, SubmitSetoffsMsg};
use crate::msg::QueryMsg; use crate::msg::QueryMsg;
use crate::msg::{ExecuteMsg, InstantiateMsg}; use crate::msg::{ExecuteMsg, InstantiateMsg};
use crate::state::{current_epoch_key, ObligationsItem, State, OBLIGATIONS_KEY, STATE}; 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)?; set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
STATE.save(deps.storage, &state)?; STATE.save(deps.storage, &state)?;
EPOCH_COUNTER.save(deps.storage, &1)?;
ObligationsItem::new(&current_epoch_key(OBLIGATIONS_KEY, deps.storage)?) ObligationsItem::new(&current_epoch_key(OBLIGATIONS_KEY, deps.storage)?)
.save(deps.storage, &Default::default())?; .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() Ok(Response::new()
.add_attribute("method", "instantiate") .add_attribute("method", "instantiate")
.add_attribute("owner", info.sender)) .add_attribute("owner", info.sender))
@ -38,7 +90,7 @@ pub fn instantiate(
#[cfg_attr(not(feature = "library"), entry_point)] #[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute( pub fn execute(
deps: DepsMut, mut deps: DepsMut,
env: Env, env: Env,
info: MessageInfo, info: MessageInfo,
msg: ExecuteMsg, msg: ExecuteMsg,
@ -48,21 +100,30 @@ pub fn execute(
ExecuteMsg::SubmitObligation(SubmitObligationMsg { ciphertext, digest }) => { ExecuteMsg::SubmitObligation(SubmitObligationMsg { ciphertext, digest }) => {
execute::submit_obligation(deps, ciphertext, digest) execute::submit_obligation(deps, ciphertext, digest)
} }
ExecuteMsg::SubmitSetoffs(SubmitSetoffsMsg { setoffs_enc }) => { ExecuteMsg::SubmitObligations(SubmitObligationsMsg(obligations)) => {
execute::submit_setoffs(deps, setoffs_enc) 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 { pub mod execute {
use std::collections::BTreeMap; 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::Hash;
use quartz_cw::state::EPOCH_COUNTER;
use crate::state::{ use crate::state::{
current_epoch_key, ObligationsItem, RawCipherText, RawHash, SetoffsItem, OBLIGATIONS_KEY, current_epoch_key, previous_epoch_key, ObligationsItem, RawHash, SetoffsItem, SettleOff,
SETOFFS_KEY, OBLIGATIONS_KEY, SETOFFS_KEY,
}; };
use crate::ContractError; use crate::ContractError;
@ -91,18 +152,74 @@ pub mod execute {
} }
pub fn submit_setoffs( pub fn submit_setoffs(
deps: DepsMut, mut deps: DepsMut,
setoffs_enc: BTreeMap<RawHash, RawCipherText>, env: Env,
_info: MessageInfo,
setoffs_enc: BTreeMap<RawHash, SettleOff>,
) -> Result<Response, ContractError> { ) -> Result<Response, ContractError> {
// store the `BTreeMap<RawHash, RawCipherText>` // store the `BTreeMap<RawHash, RawCipherText>`
SetoffsItem::new(&current_epoch_key(SETOFFS_KEY, deps.storage)?) SetoffsItem::new(&previous_epoch_key(SETOFFS_KEY, deps.storage)?)
.save(deps.storage, &setoffs_enc)?; .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")) Ok(Response::new().add_attribute("action", "submit_setoffs"))
} }
pub fn init_clearing(deps: DepsMut) -> Result<Response, ContractError> {
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)] #[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::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<GetAllSetoffsResponse> {
let setoffs = SetoffsItem::new(&previous_epoch_key(SETOFFS_KEY, deps.storage)?)
.load(deps.storage)?
.into_iter()
.collect();
Ok(GetAllSetoffsResponse { setoffs })
}
} }

View file

@ -1,4 +1,5 @@
use cosmwasm_std::StdError; use cosmwasm_std::StdError;
use cw20_base::ContractError as Cw20ContractError;
use hex::FromHexError; use hex::FromHexError;
use k256::ecdsa::Error as K256Error; use k256::ecdsa::Error as K256Error;
use quartz_cw::error::Error as QuartzError; use quartz_cw::error::Error as QuartzError;
@ -26,6 +27,9 @@ pub enum ContractError {
#[error("Invalid length")] #[error("Invalid length")]
BadLength, BadLength,
#[error("Cw20 error: {0}")]
Cw20(Cw20ContractError),
} }
impl From<K256Error> for ContractError { impl From<K256Error> for ContractError {
@ -33,3 +37,9 @@ impl From<K256Error> for ContractError {
Self::K256(e) Self::K256(e)
} }
} }
impl From<Cw20ContractError> for ContractError {
fn from(e: Cw20ContractError) -> Self {
Self::Cw20(e)
}
}

View file

@ -1,12 +1,13 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_schema::{cw_serde, QueryResponses};
use cosmwasm_std::HexBinary;
use quartz_cw::prelude::*; use quartz_cw::prelude::*;
use crate::state::{RawCipherText, RawHash}; use crate::state::RawHash;
use crate::state::SettleOff;
#[cw_serde] #[cw_serde]
#[serde(transparent)]
pub struct InstantiateMsg(pub QuartzInstantiateMsg); pub struct InstantiateMsg(pub QuartzInstantiateMsg);
#[cw_serde] #[cw_serde]
@ -14,12 +15,12 @@ pub struct InstantiateMsg(pub QuartzInstantiateMsg);
pub enum ExecuteMsg { pub enum ExecuteMsg {
Quartz(QuartzExecuteMsg), Quartz(QuartzExecuteMsg),
SubmitObligation(execute::SubmitObligationMsg), SubmitObligation(execute::SubmitObligationMsg),
SubmitObligations(execute::SubmitObligationsMsg),
SubmitSetoffs(execute::SubmitSetoffsMsg), SubmitSetoffs(execute::SubmitSetoffsMsg),
InitClearing,
} }
pub mod execute { pub mod execute {
use cosmwasm_std::HexBinary;
use super::*; use super::*;
#[cw_serde] #[cw_serde]
@ -30,15 +31,37 @@ pub mod execute {
// pub proof: π // pub proof: π
} }
#[cw_serde]
#[serde(transparent)]
pub struct SubmitObligationsMsg(pub Vec<SubmitObligationMsg>);
#[cw_serde]
pub struct SubmitTenderMsg {
pub ciphertext: HexBinary,
pub digest: HexBinary,
// pub proof: π
}
#[cw_serde] #[cw_serde]
pub struct SubmitSetoffsMsg { pub struct SubmitSetoffsMsg {
pub setoffs_enc: BTreeMap<RawHash, RawCipherText>, pub setoffs_enc: BTreeMap<RawHash, SettleOff>,
// pub proof: π, // pub proof: π,
} }
} }
#[cw_serde] #[cw_serde]
#[derive(QueryResponses)] #[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)] #[cfg(test)]
mod tests { mod tests {

View file

@ -9,13 +9,27 @@ pub type RawHash = HexBinary;
pub type RawCipherText = HexBinary; pub type RawCipherText = HexBinary;
pub type ObligationsItem<'a> = Item<'a, BTreeMap<RawHash, RawCipherText>>; pub type ObligationsItem<'a> = Item<'a, BTreeMap<RawHash, RawCipherText>>;
pub type SetoffsItem<'a> = Item<'a, BTreeMap<RawHash, RawCipherText>>; pub type SetoffsItem<'a> = Item<'a, BTreeMap<RawHash, SettleOff>>;
#[cw_serde] #[cw_serde]
pub struct State { pub struct State {
pub owner: String, 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<RawCipherText>),
Transfer(Transfer),
}
pub const STATE: Item<State> = Item::new("state"); pub const STATE: Item<State> = Item::new("state");
pub const OBLIGATIONS_KEY: &str = "obligations"; pub const OBLIGATIONS_KEY: &str = "obligations";
pub const SETOFFS_KEY: &str = "setoffs"; 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<String, StdError> { pub fn current_epoch_key(key: &str, storage: &dyn Storage) -> Result<String, StdError> {
Ok(format!("{}/{key}", EPOCH_COUNTER.load(storage)?)) Ok(format!("{}/{key}", EPOCH_COUNTER.load(storage)?))
} }
pub fn previous_epoch_key(key: &str, storage: &dyn Storage) -> Result<String, StdError> {
Ok(format!("{}/{key}", EPOCH_COUNTER.load(storage)? - 1))
}