Obligato liquidity prototype (#21)
This commit is contained in:
parent
57b2a050ff
commit
aa438c0d62
5 changed files with 190 additions and 20 deletions
|
@ -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"] }
|
||||||
|
|
|
@ -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(¤t_epoch_key(OBLIGATIONS_KEY, deps.storage)?)
|
ObligationsItem::new(¤t_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(¤t_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 })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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))
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue