Skip to content

Commit f11f3e9

Browse files
authored
docs: IBCv2 documentation (#271)
* docs: IBCv2 documentation * Update after review * chore: Update entry_point attributes
1 parent 958166c commit f11f3e9

File tree

6 files changed

+478
-0
lines changed

6 files changed

+478
-0
lines changed

src/pages/_meta.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export default {
55
core: "CosmWasm Core",
66
wasmd: "Wasmd",
77
ibc: "IBC",
8+
ibc2: "IBCv2",
89
sylvia: "Sylvia",
910
storey: "Storey",
1011
"cw-storage-plus": "StoragePlus",

src/pages/ibc2/_meta.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export default {
2+
"getting-started": "Getting started",
3+
"entrypoints": "Entry points",
4+
"message-passing": "Message passing",
5+
"example": "Example contract",
6+
};

src/pages/ibc2/entrypoints.mdx

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
---
2+
tags: ["ibc"]
3+
---
4+
5+
# Entry Points in IBCv2
6+
7+
IBCv2 introduces four primary entry points for smart contracts interacting via the Inter-Blockchain
8+
Communication protocol. These entry points define how contracts handle incoming packets, timeouts,
9+
acknowledgements, and outbound messages. Each of these entry points plays a critical role in
10+
enabling robust, verifiable, and asynchronous cross-chain communication between smart contracts via
11+
IBCv2.
12+
13+
## Receive Entry Point
14+
15+
```rust template="core"
16+
#[cfg_attr(not(feature = "library"), entry_point)]
17+
pub fn ibc2_packet_receive(
18+
deps: DepsMut,
19+
env: Env,
20+
msg: Ibc2PacketReceiveMsg,
21+
) -> StdResult<IbcReceiveResponse> {
22+
// [...]
23+
Ok(IbcReceiveResponse::new(StdAck::success(b"\x01")))
24+
}
25+
```
26+
27+
The `ibc2_packet_receive` function is invoked when an IBCv2 packet is received on a port ID
28+
associated with the contract instance. The `Ibc2PacketReceiveMsg` includes:
29+
30+
- The packet payload data
31+
- The relayer address
32+
- The source client ID
33+
- The unique packet sequence number
34+
35+
This entry point allows the contract to process incoming cross-chain messages.
36+
37+
There are two options for sending acknowledgements:
38+
39+
- Send a synchronous acknowledgement immediately using, for example,
40+
`IbcReceiveResponse::new(StdAck::success(b"\x01"))`.
41+
- Defer the acknowledgement for
42+
[asynchronous processing](message-passing#asynchronous-acknowledgements) using
43+
`IbcReceiveResponse::without_ack()`.
44+
45+
## Timeout Entry Point
46+
47+
```rust template="core"
48+
#[cfg_attr(not(feature = "library"), entry_point)]
49+
pub fn ibc2_packet_timeout(
50+
deps: DepsMut,
51+
env: Env,
52+
msg: Ibc2PacketTimeoutMsg,
53+
) -> StdResult<IbcBasicResponse> {
54+
// [...]
55+
Ok(IbcBasicResponse::default())
56+
}
57+
```
58+
59+
This function is triggered when a packet sent by the contract is proven not to have been received or
60+
processed by the destination chain. It serves as a fallback mechanism in case of connection issues.
61+
The `Ibc2PacketTimeoutMsg` provides:
62+
63+
- The original packet payload
64+
- Source and destination client IDs
65+
- The packet sequence number
66+
- The relayer address
67+
68+
## Acknowledgement Entry Point
69+
70+
```rust template="core"
71+
#[cfg_attr(not(feature = "library"), entry_point)]
72+
pub fn ibc2_acknowledge_receive(
73+
deps: DepsMut,
74+
env: Env,
75+
msg: Ibc2PacketAckMsg,
76+
) -> StdResult<IbcBasicResponse> {
77+
// [...]
78+
Ok(IbcBasicResponse::default())
79+
}
80+
```
81+
82+
When an acknowledgement for a previously sent packet is received, this entry point is called. The
83+
`Ibc2PacketAckMsg` contains:
84+
85+
- Source and destination client IDs
86+
- The relayer address
87+
- The acknowledgement response data
88+
- The payload of the original packet
89+
90+
This allows the contract to confirm and act upon the acknowledgement of a sent message.
91+
92+
## Send Entry Point
93+
94+
```rust template="core"
95+
#[cfg_attr(not(feature = "library"), entry_point)]
96+
pub fn ibc2_packet_send(
97+
deps: DepsMut,
98+
env: Env,
99+
msg: Ibc2PacketSendMsg,
100+
) -> StdResult<IbcBasicResponse> {
101+
// [...]
102+
Ok(IbcBasicResponse::default())
103+
}
104+
```
105+
106+
To support permissionless packet sending, IBCv2 introduces the `ibc2_packet_send` entry point. This
107+
function allows the contract to validate outbound messages initiated from its associated port. The
108+
`Ibc2PacketSendMsg` includes:
109+
110+
- Source and destination client IDs
111+
- Packet sequence number
112+
- Signer address
113+
- Message payload

src/pages/ibc2/example.mdx

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
---
2+
tags: ["ibc"]
3+
---
4+
5+
# Ping-Pong contract
6+
7+
This CosmWasm smart contract implements a basic IBCv2 Ping-Pong protocol using custom IBCv2
8+
messages. It demonstrates cross-chain communication by sending a `PingPongMsg` back and forth
9+
between two contracts across IBCv2-compatible clients.
10+
11+
The core idea is to initialize a connection with a "ping" (starting with counter = 1) and increment
12+
the counter with each received response, sending it back to the origin. The contract handles packet
13+
receipt, acknowledgement, timeouts, and enforces security checks on packet sends.
14+
15+
```rust template="core"
16+
use cosmwasm_schema::cw_serde;
17+
use cosmwasm_std::{
18+
entry_point, from_json, to_json_vec, Binary, ContractInfoResponse, DepsMut, Empty, Env,
19+
Ibc2Msg, Ibc2PacketAckMsg, Ibc2PacketReceiveMsg, Ibc2PacketSendMsg, Ibc2PacketTimeoutMsg,
20+
Ibc2Payload, IbcBasicResponse, IbcReceiveResponse, MessageInfo, Response, StdAck, StdError,
21+
StdResult,
22+
};
23+
24+
/// Represents a simple IBCv2 ping-pong message containing a counter.
25+
#[cw_serde]
26+
pub struct PingPongMsg {
27+
pub counter: u64,
28+
}
29+
30+
/// Initialization message for the contract.
31+
#[cw_serde]
32+
pub struct ExecuteMsg {
33+
pub source_client: String,
34+
pub destination_port: String,
35+
}
36+
37+
/// Initializes the contract.
38+
///
39+
/// # Arguments
40+
/// - `_deps`: Mutable dependencies of the contract.
41+
/// - `_env`: The current blockchain environment.
42+
/// - `_info`: Message sender information (unused).
43+
/// - `_msg`: Empty message.
44+
///
45+
#[cfg_attr(not(feature = "library"), entry_point)]
46+
pub fn instantiate(
47+
_deps: DepsMut,
48+
_env: Env,
49+
_info: MessageInfo,
50+
_msg: Empty,
51+
) -> StdResult<Response> {
52+
Ok(Response::default())
53+
}
54+
55+
/// Sends the first IBCv2 ping message.
56+
///
57+
/// # Arguments
58+
/// - `deps`: Mutable dependencies of the contract.
59+
/// - `env`: The current blockchain environment.
60+
/// - `_info`: Message sender information (unused).
61+
/// - `msg`: The execute message containing client and port info.
62+
///
63+
/// # Returns
64+
/// - `StdResult<Response>`: Result containing a response with the IBCv2 packet to be sent.
65+
#[cfg_attr(not(feature = "library"), entry_point)]
66+
pub fn execute(
67+
deps: DepsMut,
68+
env: Env,
69+
_info: MessageInfo,
70+
msg: ExecuteMsg,
71+
) -> StdResult<Response> {
72+
let ContractInfoResponse { ibc2_port, .. } = deps
73+
.querier
74+
.query_wasm_contract_info(env.contract.address)?;
75+
let source_port =
76+
ibc2_port.ok_or(StdError::generic_err("Contract's IBCv2 port ID not found"))?;
77+
let new_payload = Ibc2Payload::new(
78+
source_port,
79+
msg.destination_port,
80+
"V1".to_owned(),
81+
"application/json".to_owned(),
82+
Binary::new(to_json_vec(&PingPongMsg { counter: 1 })?),
83+
);
84+
85+
let new_msg = Ibc2Msg::SendPacket {
86+
source_client: msg.source_client,
87+
payloads: vec![new_payload],
88+
timeout: env.block.time.plus_minutes(5_u64),
89+
};
90+
91+
Ok(Response::default().add_message(new_msg))
92+
}
93+
94+
/// Handles acknowledgements for IBCv2 packets. No action is taken in this implementation.
95+
///
96+
/// # Arguments
97+
/// - `_deps`: Mutable dependencies of the contract.
98+
/// - `_env`: The current blockchain environment.
99+
/// - `_msg`: Acknowledgement message received from the IBC channel.
100+
///
101+
/// # Returns
102+
/// - `StdResult<IbcBasicResponse>`: Default empty response.
103+
#[cfg_attr(not(feature = "library"), entry_point)]
104+
pub fn ibc2_packet_ack(
105+
_deps: DepsMut,
106+
_env: Env,
107+
_msg: Ibc2PacketAckMsg,
108+
) -> StdResult<IbcBasicResponse> {
109+
// Do nothing
110+
111+
Ok(IbcBasicResponse::default())
112+
}
113+
114+
/// Handles the receipt of an IBCv2 packet and responds by incrementing the counter
115+
/// and sending it back to the source.
116+
///
117+
/// # Arguments
118+
/// - `_deps`: Mutable dependencies of the contract.
119+
/// - `env`: The current blockchain environment.
120+
/// - `msg`: The received IBCv2 packet message.
121+
///
122+
/// # Returns
123+
/// - `StdResult<IbcReceiveResponse>`: Response including a new IBC packet and a successful ack.
124+
#[cfg_attr(not(feature = "library"), entry_point)]
125+
pub fn ibc2_packet_receive(
126+
_deps: DepsMut,
127+
env: Env,
128+
msg: Ibc2PacketReceiveMsg,
129+
) -> StdResult<IbcReceiveResponse> {
130+
let binary_payload = &msg.payload.value;
131+
let json_payload: PingPongMsg = from_json(binary_payload)?;
132+
133+
let new_payload = Ibc2Payload::new(
134+
// Swap the source with destination ports to send the message back to the source contract
135+
msg.payload.destination_port,
136+
msg.payload.source_port,
137+
msg.payload.version,
138+
msg.payload.encoding,
139+
Binary::new(to_json_vec(&PingPongMsg {
140+
counter: json_payload.counter + 1,
141+
})?),
142+
);
143+
144+
let new_msg = Ibc2Msg::SendPacket {
145+
source_client: msg.source_client,
146+
payloads: vec![new_payload],
147+
timeout: env.block.time.plus_minutes(5_u64),
148+
};
149+
150+
Ok(IbcReceiveResponse::new(StdAck::success(b"\x01")).add_message(new_msg))
151+
}
152+
153+
/// Handles timeouts of previously sent IBC packets. Automatically resends the message
154+
/// without validation or retry limits.
155+
///
156+
/// # Arguments
157+
/// - `_deps`: Mutable dependencies of the contract.
158+
/// - `env`: The current blockchain environment.
159+
/// - `msg`: The timeout message with the failed payload.
160+
///
161+
/// # Returns
162+
/// - `StdResult<IbcBasicResponse>`: Response with the resend attempt.
163+
#[cfg_attr(not(feature = "library"), entry_point)]
164+
pub fn ibc2_packet_timeout(
165+
_deps: DepsMut,
166+
env: Env,
167+
msg: Ibc2PacketTimeoutMsg,
168+
) -> StdResult<IbcBasicResponse> {
169+
// Let's resend the message without any check.
170+
// It'd be good to constrain the number of trials.
171+
172+
let msg = Ibc2Msg::SendPacket {
173+
source_client: msg.source_client,
174+
payloads: vec![msg.payload],
175+
timeout: env.block.time.plus_minutes(5_u64),
176+
};
177+
178+
Ok(IbcBasicResponse::default().add_message(msg))
179+
}
180+
181+
/// Called when an IBCv2 packet is sent. Validates that the sender is the contract itself.
182+
///
183+
/// # Arguments
184+
/// - `_deps`: Mutable dependencies of the contract.
185+
/// - `_env`: The current blockchain environment.
186+
/// - `msg`: The packet send message.
187+
///
188+
/// # Returns
189+
/// - `StdResult<IbcBasicResponse>`: Default response if sender is valid, error otherwise.
190+
#[cfg_attr(not(feature = "library"), entry_point)]
191+
pub fn ibc2_packet_send(
192+
_deps: DepsMut,
193+
_env: Env,
194+
msg: Ibc2PacketSendMsg,
195+
) -> StdResult<IbcBasicResponse> {
196+
if msg.signer != _env.contract.address {
197+
return Err(StdError::generic_err(
198+
"Only this contract can send messages from its IBCv2 port ID",
199+
));
200+
}
201+
Ok(IbcBasicResponse::default())
202+
}
203+
```

src/pages/ibc2/getting-started.mdx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
---
2+
tags: ["ibc"]
3+
---
4+
5+
import { Callout } from "nextra/components";
6+
7+
# Getting Started
8+
9+
The CosmWasm module leverages the [ibc-go](https://ibc.cosmos.network/main/) implementation to
10+
enable interaction with other blockchains through the Inter-Blockchain Communication (IBC) protocol.
11+
With the release of ibc-go version 10, a new protocol
12+
version—[IBCv2](https://ibcprotocol.dev/blog/ibc-v2-announcement)—was introduced. IBCv2 expands the
13+
interoperability of the Cosmos ecosystem to include non-Cosmos blockchains. To support this broader
14+
scope, the protocol team simplified the communication model and reduced the number of required entry
15+
points. This streamlining makes it more practical to implement IBC support on chains such as
16+
Ethereum. As a result, smart contract developers now need to implement only a few well-defined entry
17+
points to fully harness cross-chain capabilities.
18+
19+
<Callout type="info">
20+
To use IBCv2 entry points and messages in your smart contract, ensure that the `ibc2` feature is
21+
enabled in your `cosmwasm-std` dependency. Add the following to your `Cargo.toml`:
22+
23+
```toml
24+
cosmwasm-std = { version = "3.0", features = ["ibc2"] }
25+
```
26+
27+
</Callout>
28+
29+
<Callout>
30+
This section of the documentation provides a basic overview of how to configure smart contracts to
31+
use IBCv2 entry points and communicate with services and contracts on other chains. For a deeper
32+
understanding of the IBC architecture, refer to the [official ibc-go
33+
documentation](https://ibc.cosmos.network/main/).
34+
</Callout>

0 commit comments

Comments
 (0)