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(&current_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<RawHash, RawCipherText>,
+        mut deps: DepsMut,
+        env: Env,
+        _info: MessageInfo,
+        setoffs_enc: BTreeMap<RawHash, SettleOff>,
     ) -> Result<Response, ContractError> {
         // 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)?;
 
+        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<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)]
-pub fn query(_deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
-    match msg {}
+pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
+    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 })
+    }
 }
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<K256Error> for ContractError {
@@ -33,3 +37,9 @@ impl From<K256Error> for ContractError {
         Self::K256(e)
     }
 }
+
+impl From<Cw20ContractError> 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<SubmitObligationMsg>);
+
+    #[cw_serde]
+    pub struct SubmitTenderMsg {
+        pub ciphertext: HexBinary,
+        pub digest: HexBinary,
+        // pub proof: π
+    }
+
     #[cw_serde]
     pub struct SubmitSetoffsMsg {
-        pub setoffs_enc: BTreeMap<RawHash, RawCipherText>,
+        pub setoffs_enc: BTreeMap<RawHash, SettleOff>,
         // 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<RawHash, RawCipherText>>;
-pub type SetoffsItem<'a> = Item<'a, BTreeMap<RawHash, RawCipherText>>;
+pub type SetoffsItem<'a> = Item<'a, BTreeMap<RawHash, SettleOff>>;
 
 #[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<RawCipherText>),
+    Transfer(Transfer),
+}
+
 pub const STATE: Item<State> = 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<String, StdError> {
     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))
+}