Skip to content

Commit

Permalink
Add listener contract to listen to voice callbacks (#44)
Browse files Browse the repository at this point in the history
  • Loading branch information
NoahSaso authored Jun 11, 2023
1 parent 63cbae8 commit c91eaf6
Show file tree
Hide file tree
Showing 10 changed files with 315 additions and 0 deletions.
15 changes: 15 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions contracts/accessories/listener/.cargo/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[alias]
wasm = "build --release --lib --target wasm32-unknown-unknown"
schema = "run --bin schema"
29 changes: 29 additions & 0 deletions contracts/accessories/listener/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
[package]
name = "polytone-listener"
authors = ["noah <[email protected]>"]
edition = { workspace = true }
license = { workspace = true }
rust-version = { workspace = true }
version = { workspace = true }

[lib]
crate-type = ["cdylib", "rlib"]

[features]
# for more explicit tests, cargo test --features=backtraces
backtraces = ["cosmwasm-std/backtraces"]
# disables #[entry_point] (i.e. instantiate/execute/query) export
library = []

[dependencies]
cosmwasm-schema = { workspace = true }
cosmwasm-std = { workspace = true }
cw-storage-plus = { workspace = true }
cw2 = { workspace = true }
thiserror = { workspace = true }
polytone = { workspace = true }

[dev-dependencies]
cw-multi-test = { workspace = true }
anyhow = { workspace = true }
polytone-note = { workspace = true }
11 changes: 11 additions & 0 deletions contracts/accessories/listener/src/bin/schema.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use cosmwasm_schema::write_api;

use polytone_listener::msg::{ExecuteMsg, InstantiateMsg, QueryMsg};

fn main() {
write_api! {
instantiate: InstantiateMsg,
execute: ExecuteMsg,
query: QueryMsg,
}
}
71 changes: 71 additions & 0 deletions contracts/accessories/listener/src/contract.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#[cfg(not(feature = "library"))]
use cosmwasm_std::entry_point;
use cosmwasm_std::{to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult};
use cw2::set_contract_version;

use crate::error::ContractError;
use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg, ResultResponse};
use crate::state::{NOTE, RESULTS};

const CONTRACT_NAME: &str = "crates.io:polytone-listener";
const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(
deps: DepsMut,
_env: Env,
_info: MessageInfo,
msg: InstantiateMsg,
) -> Result<Response, ContractError> {
set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;

let note = deps.api.addr_validate(&msg.note)?;
NOTE.save(deps.storage, &note)?;

Ok(Response::default()
.add_attribute("method", "instantiate")
.add_attribute("note", msg.note))
}

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
deps: DepsMut,
_env: Env,
info: MessageInfo,
msg: ExecuteMsg,
) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::Callback(callback) => {
// Only the note can execute the callback on this contract.
if info.sender != NOTE.load(deps.storage)? {
return Err(ContractError::Unauthorized {});
}

RESULTS.save(
deps.storage,
(
callback.initiator.to_string(),
callback.initiator_msg.to_string(),
),
&callback,
)?;
Ok(Response::default()
.add_attribute("method", "callback")
.add_attribute("initiator", callback.initiator.to_string())
.add_attribute("initiator_msg", callback.initiator_msg.to_string()))
}
}
}

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
match msg {
QueryMsg::Note {} => to_binary(&NOTE.load(deps.storage)?),
QueryMsg::Result {
initiator,
initiator_msg,
} => to_binary(&ResultResponse {
callback: RESULTS.load(deps.storage, (initiator, initiator_msg))?,
}),
}
}
11 changes: 11 additions & 0 deletions contracts/accessories/listener/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use cosmwasm_std::StdError;
use thiserror::Error;

#[derive(Error, Debug, PartialEq)]
pub enum ContractError {
#[error(transparent)]
Std(#[from] StdError),

#[error("Unauthorized")]
Unauthorized {},
}
7 changes: 7 additions & 0 deletions contracts/accessories/listener/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pub mod contract;
pub mod error;
pub mod msg;
pub mod state;

#[cfg(test)]
mod tests;
33 changes: 33 additions & 0 deletions contracts/accessories/listener/src/msg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use cosmwasm_schema::{cw_serde, QueryResponses};
use polytone::callbacks::CallbackMessage;

#[cw_serde]
pub struct InstantiateMsg {
/// The polytone note contract that can call this contract.
pub note: String,
}

#[cw_serde]
pub enum ExecuteMsg {
/// Stores the callback in state and makes it queryable.
Callback(CallbackMessage),
}

#[cw_serde]
#[derive(QueryResponses)]
pub enum QueryMsg {
/// Gets note that can call this contract.
#[returns(String)]
Note {},
/// Gets callback result.
#[returns(ResultResponse)]
Result {
initiator: String,
initiator_msg: String,
},
}

#[cw_serde]
pub struct ResultResponse {
pub callback: CallbackMessage,
}
9 changes: 9 additions & 0 deletions contracts/accessories/listener/src/state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use cosmwasm_std::Addr;
use cw_storage_plus::{Item, Map};
use polytone::callbacks::CallbackMessage;

/// The note that can call this contract.
pub(crate) const NOTE: Item<Addr> = Item::new("note");

/// (initiator, initiator_msg) -> callback
pub(crate) const RESULTS: Map<(String, String), CallbackMessage> = Map::new("results");
126 changes: 126 additions & 0 deletions contracts/accessories/listener/src/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
use cosmwasm_std::{to_binary, Addr, Empty, Uint64};

use cw_multi_test::{App, Contract, ContractWrapper, Executor};
use polytone::callbacks::{Callback, CallbackMessage};

use crate::{
error::ContractError,
msg::{ExecuteMsg, InstantiateMsg, QueryMsg, ResultResponse},
};

pub const CREATOR_ADDR: &str = "creator";
pub const INITIATOR_ADDR: &str = "initiator";
pub const INITIATOR_MSG: &str = "initiator_msg";

fn note_contract() -> Box<dyn Contract<Empty>> {
let contract = ContractWrapper::new(
polytone_note::contract::execute,
polytone_note::contract::instantiate,
polytone_note::contract::query,
);
Box::new(contract)
}

fn listener_contract() -> Box<dyn Contract<Empty>> {
let contract = ContractWrapper::new(
crate::contract::execute,
crate::contract::instantiate,
crate::contract::query,
);
Box::new(contract)
}

#[test]
fn test() {
let mut app = App::default();

let note_code = app.store_code(note_contract());
let listener_code = app.store_code(listener_contract());

let note1 = app
.instantiate_contract(
note_code,
Addr::unchecked(CREATOR_ADDR),
&polytone_note::msg::InstantiateMsg {
pair: None,
block_max_gas: Uint64::new(110_000),
},
&[],
"note1",
Some(CREATOR_ADDR.to_string()),
)
.unwrap();
let note2 = app
.instantiate_contract(
note_code,
Addr::unchecked(CREATOR_ADDR),
&polytone_note::msg::InstantiateMsg {
pair: None,
block_max_gas: Uint64::new(110_000),
},
&[],
"note2",
Some(CREATOR_ADDR.to_string()),
)
.unwrap();

let listener = app
.instantiate_contract(
listener_code,
Addr::unchecked(CREATOR_ADDR),
&InstantiateMsg {
note: note1.to_string(),
},
&[],
"listener",
Some(CREATOR_ADDR.to_string()),
)
.unwrap();

// Returns correct note.
let queried_note: String = app
.wrap()
.query_wasm_smart(listener.clone(), &QueryMsg::Note {})
.unwrap();
assert_eq!(queried_note, note1.to_string());

// Allows note to execute callback.
let callback = CallbackMessage {
initiator: Addr::unchecked(INITIATOR_ADDR),
initiator_msg: to_binary(INITIATOR_MSG).unwrap(),
result: Callback::Execute(Result::Err("ERROR".to_string())),
};
app.execute_contract(
note1,
listener.clone(),
&ExecuteMsg::Callback(callback.clone()),
&[],
)
.unwrap();

// Prevents different note from executing callback.
let err: ContractError = app
.execute_contract(
note2,
listener.clone(),
&ExecuteMsg::Callback(callback.clone()),
&[],
)
.unwrap_err()
.downcast()
.unwrap();
assert_eq!(err, ContractError::Unauthorized {});

// Returns the correct callback.
let response: ResultResponse = app
.wrap()
.query_wasm_smart(
listener,
&QueryMsg::Result {
initiator: INITIATOR_ADDR.to_string(),
initiator_msg: to_binary(INITIATOR_MSG).unwrap().to_string(),
},
)
.unwrap();
assert_eq!(response.callback, callback);
}

0 comments on commit c91eaf6

Please sign in to comment.