Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add mock app docs #607

Merged
merged 5 commits into from
Aug 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 175 additions & 0 deletions contracts/cosmwasm-vm/cw-mock-ibc-dapp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
# Getting Started with IBC Dapp for Contracts based IBC Host.

This guide will walk you through the process of getting started with a CosmWasm contract compatible with IBC Core Smart Contracts. We'll cover the basic structure of the contract code, the different entrypoints, and how to handle different types of messages.

## Prerequisites

Before you begin, make sure you have the following:

- A working development environment with Rust and the necessary tools installed.
- Familiarity with Rust programming language.
- Knowledge of smart contracts and blockchain concepts.

## Setting Up Your Project

1. Create a new Rust project using `cargo`:

```bash
cargo new my_cosmwasm_contract
cd my_cosmwasm_contract
```

2. Edit the `Cargo.toml` file to add the required dependencies:

```toml
[dependencies]
cosmwasm-std = "0.16"
cosmwasm-derive = "0.16"
```

## Writing the Contract

Create a new Rust file in the `src` directory of your project, e.g., `contract.rs`. This is where you will define your contract logic.On contracts file add the entrypints to handle channel handshake from IBC Core Contract.
```rust
// src/contract.rs

use cosmwasm_std::{
DepsMut, Env, MessageInfo, Response, ContractError,
log, to_binary, StdResult,
};

// Import message types
use crate::msg::{ExecuteMsg, CwIbcConnection};

// Import storage functions
use crate::state::{add_admin, send_message, ensure_ibc_handler, on_channel_open, /* ... */};

pub fn execute(
deps: DepsMut,
env: Env,
info: MessageInfo,
msg: ExecuteMsg,
) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::SetAdmin { address } => {
let validated_address = CwIbcConnection::validate_address(deps.api, address.as_str())?;
add_admin(deps.storage, info, validated_address.to_string())
},
ExecuteMsg::SendMessage { msg } => {
log!("Received Payload From App");
send_message(deps, info, env, msg)
}
// Handle IBC-related messages (if not using native IBC)
#[cfg(not(feature = "native_ibc"))]
ExecuteMsg::IbcChannelOpen { msg } => {
ensure_ibc_handler(deps.as_ref().storage, info.sender)?;
Ok(on_channel_open(deps.storage, msg)?)
}
// Add other IBC-related cases...
#[cfg(feature = "native_ibc")]
_ => Err(ContractError::DecodeFailed {
error: "InvalidMessage Variant".to_string(),
}),
}
}
```

## Message Types and Storage

Create two new files in the `src` directory: `msg.rs` and `state.rs`. In `msg.rs`, define the message types that your contract will handle:

```rust
// src/msg.rs

use cosmwasm_std::{Addr, Binary, CanonicalAddr, Env, MessageInfo, StdError, StdResult, Uint128};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[cw_serde]
pub enum ExecuteMsg {

SendMessage {
msg: Vec<u8>,
},

IbcChannelOpen {
msg: CwChannelOpenMsg,
},


IbcChannelConnect {
msg: CwChannelConnectMsg,
},

IbcChannelClose {
msg: CwChannelCloseMsg,
},

IbcPacketReceive {
msg: CwPacketReceiveMsg,
},

IbcPacketAck {
msg: CwPacketAckMsg,
},

IbcPacketTimeout {
msg: CwPacketTimeoutMsg,
},
}

// Define your message structs here...

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct Admin {
pub address: String,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct Message {
pub msg: String,
}

// Define IBC-related message structs (if not using native IBC)...

```

In `state.rs`, define the storage functions and related logic:

```rust
// src/state.rs

use cosmwasm_std::{DepsMut, Env, MessageInfo, Response, ContractError, Addr, CanonicalAddr};

// Define your storage-related functions here...

pub fn add_admin(
storage: &mut dyn Storage,
info: MessageInfo,
address: String,
) -> Result<Response, ContractError> {
// Add admin logic here...
}

pub fn send_message(
deps: DepsMut,
info: MessageInfo,
env: Env,
msg: String,
timeout_height: u64,
) -> Result<Response, ContractError> {
// Send message logic here...
}

// Define IBC-related storage functions (if not using native IBC)...

```

## Conclusion

Congratulations! You've created a basic CosmWasm contract with the provided `execute` entrypoint and different message types. You can now build upon this foundation to add more functionality, handle additional message types, and integrate with other contracts and systems. Make sure to test your contract thoroughly before deploying it on a blockchain network.You can look at this contract for code details and how to proceed with channel handshake and other methods.

Remember to consult the official CosmWasm documentation and resources for more advanced topics and best practices. Happy coding!
```

Please note that this guide assumes you have some knowledge of Rust and blockchain concepts. It's a starting point, and you should refer to the official CosmWasm documentation for more in-depth information and advanced features. Additionally, ensure that your environment is set up properly with the necessary tools and dependencies.
41 changes: 10 additions & 31 deletions contracts/cosmwasm-vm/cw-mock-ibc-dapp/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ use cosmwasm_std::IbcChannel;
use cw_common::raw_types::channel::RawPacket;
use debug_print::debug_println;

use crate::types::{
config::Config, dapp_msg::ExecuteMsg, dapp_msg::QueryMsg, message::Message, LOG_PREFIX,
};
use crate::types::{config::Config, message::Message, LOG_PREFIX};

use super::*;

Expand Down Expand Up @@ -99,40 +97,39 @@ impl<'a> CwIbcConnection<'a> {
self.send_message(deps, info, env, msg, timeout_height)
}

#[cfg(not(feature = "native_ibc"))]
ExecuteMsg::IbcChannelOpen { msg } => {
self.ensure_ibc_handler(deps.as_ref().storage, info.sender)?;
Ok(self.on_channel_open(deps.storage, msg)?)
}
#[cfg(not(feature = "native_ibc"))]

ExecuteMsg::IbcChannelConnect { msg } => {
self.ensure_ibc_handler(deps.as_ref().storage, info.sender)?;
Ok(self.on_channel_connect(deps.storage, msg)?)
}
#[cfg(not(feature = "native_ibc"))]

ExecuteMsg::IbcChannelClose { msg } => {
self.ensure_ibc_handler(deps.as_ref().storage, info.sender)?;
Ok(self.on_channel_close(msg)?)
}
#[cfg(not(feature = "native_ibc"))]

ExecuteMsg::IbcPacketReceive { msg } => {
self.ensure_ibc_handler(deps.as_ref().storage, info.sender)?;
Ok(self.on_packet_receive(deps, msg)?)
}
#[cfg(not(feature = "native_ibc"))]

ExecuteMsg::IbcPacketAck { msg } => {
self.ensure_ibc_handler(deps.as_ref().storage, info.sender)?;
Ok(self.on_packet_ack(deps, msg)?)
}
#[cfg(not(feature = "native_ibc"))]

ExecuteMsg::IbcPacketTimeout { msg } => {
self.ensure_ibc_handler(deps.as_ref().storage, info.sender)?;
Ok(self.on_packet_timeout(deps, msg)?)
}
#[cfg(feature = "native_ibc")]
_ => Err(ContractError::DecodeFailed {
error: "InvalidMessage Variant".to_string(),
}),
ExecuteMsg::IbcWriteAcknowledgement { seq } => {
let packet = self.get_received_packet(deps.as_ref().storage, seq)?;
return self.write_acknowledgement(deps.storage, packet);
}
}
}

Expand Down Expand Up @@ -250,24 +247,6 @@ impl<'a> CwIbcConnection<'a> {
.add_attribute("ibc_host", msg.ibc_host))
}

#[cfg(feature = "native_ibc")]
fn create_packet_response(&self, deps: Deps, env: Env, data: Binary) -> IbcMsg {
let ibc_config = self.ibc_config().may_load(deps.storage).unwrap().unwrap();

let timeout = IbcTimeout::with_timestamp(env.block.time.plus_seconds(300));

IbcMsg::SendPacket {
channel_id: ibc_config.dst_endpoint().channel_id.clone(),
data,
timeout,
}
}
fn reply_ack_on_error(&self, reply: Reply) -> Result<Response, ContractError> {
match reply.result {
SubMsgResult::Ok(_) => Ok(Response::new()),
SubMsgResult::Err(_err) => Ok(Response::new()),
}
}
/// This function handles the opening of an IBC channel and returns a response with relevant
/// attributes.
///
Expand Down
2 changes: 1 addition & 1 deletion contracts/cosmwasm-vm/cw-mock-ibc-dapp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ use cw_common::cw_types::{
};

use cw_storage_plus::Item;
use msg::{ExecuteMsg, QueryMsg};
use thiserror::Error;
use types::dapp_msg::{ExecuteMsg, QueryMsg};

/// This function instantiates a contract using the CwIbcConnection.
///
Expand Down
56 changes: 53 additions & 3 deletions contracts/cosmwasm-vm/cw-mock-ibc-dapp/src/msg.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
use super::*;
use cosmwasm_schema::QueryResponses;
use cw_common::cw_types::CwOrder;

/// This is a Rust struct representing a message to instantiate a contract with timeout height and IBC
/// host address.
///
/// Properties:
///
/// * `timeout_height`: `timeout_height` is a field of type `u64` (unsigned 64-bit integer) in the
/// `InstantiateMsg` struct. It represents the block height at which the transaction will timeout if it
/// has not been included in a block by that height. This is used to prevent transactions from being
/// * `ibc_host`: `ibc_host` is a field of type `Addr` in the `InstantiateMsg` struct. It likely
/// represents the address of the IBC host that the message is being sent to. However, without more
/// context it's difficult to say for sure.
Expand All @@ -19,3 +17,55 @@ pub struct InstantiateMsg {
pub denom: String,
pub order: CwOrder,
}

#[cw_serde]
pub enum ExecuteMsg {
SetAdmin {
address: String,
},

SendMessage {
msg: Vec<u8>,
timeout_height: u64,
},

#[cfg(not(feature = "native_ibc"))]
IbcChannelOpen {
msg: CwChannelOpenMsg,
},

#[cfg(not(feature = "native_ibc"))]
IbcChannelConnect {
msg: CwChannelConnectMsg,
},
#[cfg(not(feature = "native_ibc"))]
IbcChannelClose {
msg: CwChannelCloseMsg,
},
#[cfg(not(feature = "native_ibc"))]
IbcPacketReceive {
msg: CwPacketReceiveMsg,
},
#[cfg(not(feature = "native_ibc"))]
IbcPacketAck {
msg: CwPacketAckMsg,
},
#[cfg(not(feature = "native_ibc"))]
IbcPacketTimeout {
msg: CwPacketTimeoutMsg,
},
#[cfg(not(feature = "native_ibc"))]
IbcWriteAcknowledgement {
seq: u64,
},
}

#[cw_serde]
#[derive(QueryResponses)]
/// This is a Rust enum representing different types of queries that can be made to the contract. Each
/// variant of the enum corresponds to a specific query and has a return type specified using the
/// `#[returns]` attribute.
pub enum QueryMsg {
#[returns(String)]
GetAdmin {},
}
16 changes: 13 additions & 3 deletions contracts/cosmwasm-vm/cw-mock-ibc-dapp/src/receive_packet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,22 @@ impl<'a> CwIbcConnection<'a> {
/// a `Result` object with either an `IbcReceiveResponse` or a `ContractError`.
pub fn do_packet_receive(
&self,
_deps: DepsMut,
_packet: CwPacket,
deps: DepsMut,
packet: CwPacket,
_relayer: Addr,
) -> Result<CwReceiveResponse, ContractError> {
debug_println!("[IBCConnection]: forwarding to xcall");
debug_println!("[MockDapp]: Packet Received");
self.store_received_packet(deps.storage, packet.sequence, packet)?;

Ok(CwReceiveResponse::new())
}

pub fn write_acknowledgement(
&self,
store: &mut dyn Storage,
packet: CwPacket,
) -> Result<Response, ContractError> {
let submsg = self.call_host_write_acknowledgement(store, packet, b"ack".to_vec())?;
Ok(Response::new().add_submessage(submsg))
}
}
Loading