Skip to content

Commit b36f7e5

Browse files
committed
feat: Add support for IBCv2 Sent entry point
1 parent e38f652 commit b36f7e5

File tree

16 files changed

+424
-13
lines changed

16 files changed

+424
-13
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ and this project adheres to
2929
allows for advanced configurations such as custom address prefixes. ([#2442])
3030
- cosmwasm-vm: Add support for `ibc2_packet_ack` endpoint ([#2474])
3131
- cosmwasm-std: Add support for `ibc2_packet_ack` endpoint ([#2474])
32+
- cosmwasm-std: Add `Ibc2PacketSendMsg` message - ([#2477])
33+
- cosmwasm-vm: Add `ibc2_packet_send` entrypoint ([#2477])
3234

3335
## Changed
3436

@@ -127,6 +129,7 @@ and this project adheres to
127129
[#2467]: https://github.com/CosmWasm/cosmwasm/pull/2467
128130
[#2472]: https://github.com/CosmWasm/cosmwasm/pull/2472
129131
[#2473]: https://github.com/CosmWasm/cosmwasm/pull/2473
132+
[#2477]: https://github.com/CosmWasm/cosmwasm/pull/2477
130133

131134
## [2.2.0] - 2024-12-17
132135

contracts/ibc2/schema/ibc2.json

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,16 @@
5959
"format": "uint32",
6060
"minimum": 0.0
6161
},
62+
"last_packet_sent": {
63+
"anyOf": [
64+
{
65+
"$ref": "#/definitions/Ibc2PacketSendMsg"
66+
},
67+
{
68+
"type": "null"
69+
}
70+
]
71+
},
6272
"last_packet_seq": {
6373
"type": "integer",
6474
"format": "uint64",
@@ -68,7 +78,99 @@
6878
"type": "string"
6979
}
7080
},
71-
"additionalProperties": false
81+
"additionalProperties": false,
82+
"definitions": {
83+
"Addr": {
84+
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
85+
"type": "string"
86+
},
87+
"Binary": {
88+
"description": "Binary is a wrapper around Vec<u8> to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec<u8>. See also <https://github.com/CosmWasm/cosmwasm/blob/main/docs/MESSAGE_TYPES.md>.",
89+
"type": "string"
90+
},
91+
"Ibc2PacketSendMsg": {
92+
"description": "Ibc2PacketSendMsg represents a payload sent event in the IBC2 protocol. Since sending IBCv2 packet is permissionless, the IBC protocol indtroduces an extra entry point, in which the application can verify the message sent from a port ID belonging to the contract.\n\nIt includes details about the source and destination clients, the sequence number of the packet and the signer that sent the message.",
93+
"type": "object",
94+
"required": [
95+
"destination_client",
96+
"packet_sequence",
97+
"payload",
98+
"signer",
99+
"source_client"
100+
],
101+
"properties": {
102+
"destination_client": {
103+
"description": "The identifier of the client that was the intended recipient.",
104+
"type": "string"
105+
},
106+
"packet_sequence": {
107+
"description": "The sequence number of the sent packet.",
108+
"type": "integer",
109+
"format": "uint64",
110+
"minimum": 0.0
111+
},
112+
"payload": {
113+
"description": "The payload to be sent.",
114+
"allOf": [
115+
{
116+
"$ref": "#/definitions/Ibc2Payload"
117+
}
118+
]
119+
},
120+
"signer": {
121+
"description": "The address of the signer that sent the packet.",
122+
"allOf": [
123+
{
124+
"$ref": "#/definitions/Addr"
125+
}
126+
]
127+
},
128+
"source_client": {
129+
"description": "The identifier of the client that originally sent the packet.",
130+
"type": "string"
131+
}
132+
},
133+
"additionalProperties": false
134+
},
135+
"Ibc2Payload": {
136+
"description": "Payload value should be encoded in a format defined by the channel version, and the module on the other side should know how to parse this.",
137+
"type": "object",
138+
"required": [
139+
"destination_port",
140+
"encoding",
141+
"source_port",
142+
"value",
143+
"version"
144+
],
145+
"properties": {
146+
"destination_port": {
147+
"description": "The port id on the chain where the packet is sent to.",
148+
"type": "string"
149+
},
150+
"encoding": {
151+
"description": "Encoding used to serialize the [Ibc2Payload::value].",
152+
"type": "string"
153+
},
154+
"source_port": {
155+
"description": "The port id on the chain where the packet is sent from.",
156+
"type": "string"
157+
},
158+
"value": {
159+
"description": "Encoded payload data.",
160+
"allOf": [
161+
{
162+
"$ref": "#/definitions/Binary"
163+
}
164+
]
165+
},
166+
"version": {
167+
"description": "Version of the receiving contract.",
168+
"type": "string"
169+
}
170+
},
171+
"additionalProperties": false
172+
}
173+
}
72174
}
73175
}
74176
}

contracts/ibc2/schema/raw/response_to_query_state.json

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@
2525
"format": "uint32",
2626
"minimum": 0.0
2727
},
28+
"last_packet_sent": {
29+
"anyOf": [
30+
{
31+
"$ref": "#/definitions/Ibc2PacketSendMsg"
32+
},
33+
{
34+
"type": "null"
35+
}
36+
]
37+
},
2838
"last_packet_seq": {
2939
"type": "integer",
3040
"format": "uint64",
@@ -34,5 +44,97 @@
3444
"type": "string"
3545
}
3646
},
37-
"additionalProperties": false
47+
"additionalProperties": false,
48+
"definitions": {
49+
"Addr": {
50+
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
51+
"type": "string"
52+
},
53+
"Binary": {
54+
"description": "Binary is a wrapper around Vec<u8> to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec<u8>. See also <https://github.com/CosmWasm/cosmwasm/blob/main/docs/MESSAGE_TYPES.md>.",
55+
"type": "string"
56+
},
57+
"Ibc2PacketSendMsg": {
58+
"description": "Ibc2PacketSendMsg represents a payload sent event in the IBC2 protocol. Since sending IBCv2 packet is permissionless, the IBC protocol indtroduces an extra entry point, in which the application can verify the message sent from a port ID belonging to the contract.\n\nIt includes details about the source and destination clients, the sequence number of the packet and the signer that sent the message.",
59+
"type": "object",
60+
"required": [
61+
"destination_client",
62+
"packet_sequence",
63+
"payload",
64+
"signer",
65+
"source_client"
66+
],
67+
"properties": {
68+
"destination_client": {
69+
"description": "The identifier of the client that was the intended recipient.",
70+
"type": "string"
71+
},
72+
"packet_sequence": {
73+
"description": "The sequence number of the sent packet.",
74+
"type": "integer",
75+
"format": "uint64",
76+
"minimum": 0.0
77+
},
78+
"payload": {
79+
"description": "The payload to be sent.",
80+
"allOf": [
81+
{
82+
"$ref": "#/definitions/Ibc2Payload"
83+
}
84+
]
85+
},
86+
"signer": {
87+
"description": "The address of the signer that sent the packet.",
88+
"allOf": [
89+
{
90+
"$ref": "#/definitions/Addr"
91+
}
92+
]
93+
},
94+
"source_client": {
95+
"description": "The identifier of the client that originally sent the packet.",
96+
"type": "string"
97+
}
98+
},
99+
"additionalProperties": false
100+
},
101+
"Ibc2Payload": {
102+
"description": "Payload value should be encoded in a format defined by the channel version, and the module on the other side should know how to parse this.",
103+
"type": "object",
104+
"required": [
105+
"destination_port",
106+
"encoding",
107+
"source_port",
108+
"value",
109+
"version"
110+
],
111+
"properties": {
112+
"destination_port": {
113+
"description": "The port id on the chain where the packet is sent to.",
114+
"type": "string"
115+
},
116+
"encoding": {
117+
"description": "Encoding used to serialize the [Ibc2Payload::value].",
118+
"type": "string"
119+
},
120+
"source_port": {
121+
"description": "The port id on the chain where the packet is sent from.",
122+
"type": "string"
123+
},
124+
"value": {
125+
"description": "Encoded payload data.",
126+
"allOf": [
127+
{
128+
"$ref": "#/definitions/Binary"
129+
}
130+
]
131+
},
132+
"version": {
133+
"description": "Version of the receiving contract.",
134+
"type": "string"
135+
}
136+
},
137+
"additionalProperties": false
138+
}
139+
}
38140
}

contracts/ibc2/src/contract.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use cosmwasm_std::{
22
entry_point, from_json, to_json_vec, Binary, Deps, DepsMut, Empty, Env, Ibc2Msg,
3-
Ibc2PacketAckMsg, Ibc2PacketReceiveMsg, Ibc2PacketTimeoutMsg, Ibc2Payload, IbcAcknowledgement,
3+
Ibc2PacketAckMsg, Ibc2PacketReceiveMsg, Ibc2PacketTimeoutMsg, Ibc2Payload, IbcAcknowledgement, Ibc2PacketSendMsg,
44
IbcBasicResponse, IbcReceiveResponse, MessageInfo, QueryResponse, Response, StdAck, StdError,
55
StdResult,
66
};
@@ -23,6 +23,7 @@ pub fn instantiate(
2323
ibc2_packet_timeout_counter: 0,
2424
last_source_client: "".to_owned(),
2525
last_packet_seq: 0,
26+
last_packet_sent: None,
2627
})?,
2728
);
2829

@@ -145,3 +146,26 @@ pub fn ibc2_packet_timeout(
145146

146147
Ok(IbcBasicResponse::default())
147148
}
149+
150+
#[entry_point]
151+
pub fn ibc2_packet_send(
152+
deps: DepsMut,
153+
_env: Env,
154+
msg: Ibc2PacketSendMsg,
155+
) -> StdResult<IbcBasicResponse> {
156+
let data = deps
157+
.storage
158+
.get(STATE_KEY)
159+
.ok_or_else(|| StdError::generic_err("State not found."))?;
160+
let state: State = from_json(data)?;
161+
162+
deps.storage.set(
163+
STATE_KEY,
164+
&to_json_vec(&State {
165+
last_packet_sent: Some(msg),
166+
..state
167+
})?,
168+
);
169+
170+
Ok(IbcBasicResponse::default())
171+
}

contracts/ibc2/src/state.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use cosmwasm_std::Ibc2PacketSendMsg;
12
use schemars::JsonSchema;
23
use serde::{Deserialize, Serialize};
34

@@ -8,6 +9,7 @@ pub struct State {
89
pub ibc2_packet_timeout_counter: u32,
910
pub last_source_client: String,
1011
pub last_packet_seq: u64,
12+
pub last_packet_sent: Option<Ibc2PacketSendMsg>,
1113
}
1214

1315
pub const STATE_KEY: &[u8] = b"state";

packages/std/src/exports/exports.rs

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use crate::ibc::{
2626
};
2727
use crate::ibc::{IbcChannelOpenMsg, IbcChannelOpenResponse};
2828
#[cfg(feature = "ibc2")]
29-
use crate::ibc2::{Ibc2PacketAckMsg, Ibc2PacketReceiveMsg, Ibc2PacketTimeoutMsg};
29+
use crate::ibc2::{Ibc2PacketAckMsg, Ibc2PacketReceiveMsg, Ibc2PacketTimeoutMsg, Ibc2PacketSendMsg};
3030
use crate::query::CustomQuery;
3131
use crate::results::{ContractResult, QueryResponse, Reply, Response};
3232
use crate::serde::{from_json, to_json_vec};
@@ -612,6 +612,36 @@ where
612612
Region::from_vec(v).to_heap_ptr() as u32
613613
}
614614

615+
/// do_ibc2_packet_send is designed for use with #[entry_point] to make a "C" extern
616+
///
617+
/// contract_fn is called when there is an Ibc2 payload on port belonging
618+
/// to this contract waiting to be verified before sending it to another
619+
/// blockchain.
620+
///
621+
/// - `Q`: custom query type (see QueryRequest)
622+
/// - `C`: custom response message type (see CosmosMsg)
623+
/// - `E`: error type for responses
624+
#[cfg(feature = "ibc2")]
625+
pub fn do_ibc2_packet_send<Q, C, E>(
626+
contract_fn: &dyn Fn(DepsMut<Q>, Env, Ibc2PacketSendMsg) -> Result<IbcBasicResponse<C>, E>,
627+
env_ptr: u32,
628+
msg_ptr: u32,
629+
) -> u32
630+
where
631+
Q: CustomQuery,
632+
C: CustomMsg,
633+
E: ToString,
634+
{
635+
install_panic_handler();
636+
let res = _do_ibc2_packet_send(
637+
contract_fn,
638+
env_ptr as *mut Region<Owned>,
639+
msg_ptr as *mut Region<Owned>,
640+
);
641+
let v = to_json_vec(&res).unwrap();
642+
Region::from_vec(v).to_heap_ptr() as u32
643+
}
644+
615645
fn _do_instantiate<Q, M, C, E>(
616646
instantiate_fn: &dyn Fn(DepsMut<Q>, Env, MessageInfo, M) -> Result<Response<C>, E>,
617647
env_ptr: *mut Region<Owned>,
@@ -1036,6 +1066,29 @@ where
10361066
contract_fn(deps.as_mut(), env, msg).into()
10371067
}
10381068

1069+
#[cfg(feature = "ibc2")]
1070+
fn _do_ibc2_packet_send<Q, C, E>(
1071+
contract_fn: &dyn Fn(DepsMut<Q>, Env, Ibc2PacketSendMsg) -> Result<IbcBasicResponse<C>, E>,
1072+
env_ptr: *mut Region<Owned>,
1073+
msg_ptr: *mut Region<Owned>,
1074+
) -> ContractResult<IbcBasicResponse<C>>
1075+
where
1076+
Q: CustomQuery,
1077+
C: CustomMsg,
1078+
E: ToString,
1079+
{
1080+
let env: Vec<u8> =
1081+
unsafe { Region::from_heap_ptr(ptr::NonNull::new(env_ptr).unwrap()).into_vec() };
1082+
let msg: Vec<u8> =
1083+
unsafe { Region::from_heap_ptr(ptr::NonNull::new(msg_ptr).unwrap()).into_vec() };
1084+
1085+
let env: Env = try_into_contract_result!(from_json(env));
1086+
let msg: Ibc2PacketSendMsg = try_into_contract_result!(from_json(msg));
1087+
1088+
let mut deps = deps_from_imports();
1089+
contract_fn(deps.as_mut(), env, msg).into()
1090+
}
1091+
10391092
/// Makes all bridges to external dependencies (i.e. Wasm imports) that are injected by the VM
10401093
fn deps_from_imports<Q>() -> OwnedDeps<ExternalStorage, ExternalApi, ExternalQuerier, Q>
10411094
where

packages/std/src/exports/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pub use exports::{
1010
do_query, do_reply, do_sudo,
1111
};
1212
#[cfg(feature = "ibc2")]
13-
pub use exports::{do_ibc2_packet_ack, do_ibc2_packet_receive, do_ibc2_packet_timeout};
13+
pub use exports::{do_ibc2_packet_ack, do_ibc2_packet_receive, do_ibc2_packet_timeout, do_ibc2_packet_send};
1414
#[cfg(feature = "stargate")]
1515
pub use exports::{
1616
do_ibc_channel_close, do_ibc_channel_connect, do_ibc_channel_open, do_ibc_packet_ack,

0 commit comments

Comments
 (0)