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 ICS20 TransferV2 #2317

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
5 changes: 5 additions & 0 deletions contracts/ibc-callbacks/schema/ibc-callbacks.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"type": "object",
"required": [
"channel_id",
"channel_version",
"timeout_seconds",
"to_address"
],
Expand All @@ -40,6 +41,10 @@
"description": "The channel to send the packet through",
"type": "string"
},
"channel_version": {
"description": "IBC channel version",
"type": "string"
},
"timeout_seconds": {
"description": "The amount of seconds from now the transfer should timeout at",
"type": "integer",
Expand Down
5 changes: 5 additions & 0 deletions contracts/ibc-callbacks/schema/raw/execute.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"type": "object",
"required": [
"channel_id",
"channel_version",
"timeout_seconds",
"to_address"
],
Expand All @@ -29,6 +30,10 @@
"description": "The channel to send the packet through",
"type": "string"
},
"channel_version": {
"description": "IBC channel version",
"type": "string"
},
"timeout_seconds": {
"description": "The amount of seconds from now the transfer should timeout at",
"type": "integer",
Expand Down
70 changes: 48 additions & 22 deletions contracts/ibc-callbacks/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use cosmwasm_std::{
entry_point, to_json_binary, Binary, Deps, DepsMut, Empty, Env, IbcBasicResponse,
IbcDestinationCallbackMsg, IbcDstCallback, IbcSourceCallbackMsg, IbcSrcCallback, IbcTimeout,
MessageInfo, Response, StdError, StdResult, TransferMsgBuilder,
MessageInfo, Response, StdError, StdResult, TransferMsgBuilder, TransferMsgBuilderV2,
};

use crate::msg::{CallbackType, ExecuteMsg, QueryMsg};
Expand Down Expand Up @@ -35,13 +35,15 @@ pub fn execute(
channel_id,
timeout_seconds,
callback_type,
channel_version,
} => execute_transfer(
env,
info,
to_address,
channel_id,
timeout_seconds,
callback_type,
channel_version,
),
}
}
Expand All @@ -53,6 +55,7 @@ fn execute_transfer(
channel_id: String,
timeout_seconds: u32,
callback_type: CallbackType,
channel_version: String,
) -> StdResult<Response> {
let src_callback = IbcSrcCallback {
address: env.contract.address,
Expand All @@ -62,28 +65,51 @@ fn execute_transfer(
address: to_address.clone(),
gas_limit: None,
};
let coin = match &*info.funds {
[coin] if !coin.amount.is_zero() => coin,
_ => {
return Err(StdError::generic_err(
"Must send exactly one denom to trigger ics-20 transfer",
))
}
};

let builder = TransferMsgBuilder::new(
channel_id,
to_address.clone(),
coin.clone(),
IbcTimeout::with_timestamp(env.block.time.plus_seconds(timeout_seconds as u64)),
);
let transfer_msg = match callback_type {
CallbackType::Both => builder
.with_src_callback(src_callback)
.with_dst_callback(dst_callback)
.build(),
CallbackType::Src => builder.with_src_callback(src_callback).build(),
CallbackType::Dst => builder.with_dst_callback(dst_callback).build(),
let transfer_msg = match channel_version.as_str() {
"V1" => {
let coin = match &*info.funds {
[coin] if !coin.amount.is_zero() => coin,
_ => {
return Err(StdError::generic_err(
"Must send exactly one denom to trigger ics-20 transfer",
))
}
};
let builder = TransferMsgBuilder::new(
channel_id,
to_address.clone(),
coin.clone(),
IbcTimeout::with_timestamp(env.block.time.plus_seconds(timeout_seconds as u64)),
);
match callback_type {
CallbackType::Both => builder
.with_src_callback(src_callback)
.with_dst_callback(dst_callback)
.build(),
CallbackType::Src => builder.with_src_callback(src_callback).build(),
CallbackType::Dst => builder.with_dst_callback(dst_callback).build(),
}
}
"V2" => {
let builder = TransferMsgBuilderV2::new(
channel_id,
to_address.clone(),
info.funds.into_iter().map(Into::into).collect(),
IbcTimeout::with_timestamp(env.block.time.plus_seconds(timeout_seconds as u64)),
);
match callback_type {
CallbackType::Both => builder
.with_src_callback(src_callback)
.with_dst_callback(dst_callback)
.build(),
CallbackType::Src => builder.with_src_callback(src_callback).build(),
CallbackType::Dst => builder.with_dst_callback(dst_callback).build(),
}
}
_ => return Err(StdError::generic_err(
"Must specify \"V1\" or \"V2\" channel version",
))
};

Ok(Response::new()
Expand Down
2 changes: 2 additions & 0 deletions contracts/ibc-callbacks/src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ pub enum ExecuteMsg {
/// Who should receive callbacks for the message
#[serde(default)]
callback_type: CallbackType,
/// IBC channel version
channel_version: String,
},
}

Expand Down
148 changes: 147 additions & 1 deletion contracts/ibc-reflect-send/schema/ibc-reflect-send.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,34 @@
}
},
"additionalProperties": false
},
{
"description": "Same as `SendFunds`, but for Transfer V2 message.",
"type": "object",
"required": [
"send_funds_v2"
],
"properties": {
"send_funds_v2": {
"type": "object",
"required": [
"reflect_channel_id",
"transfer_channel_id"
],
"properties": {
"reflect_channel_id": {
"description": "The channel id we use above to talk with the reflect contract",
"type": "string"
},
"transfer_channel_id": {
"description": "The channel to use for ibctransfer. This is bound to a different port and handled by a different module. It should connect to the same chain as the reflect_channel_id does",
"type": "string"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
],
"definitions": {
Expand Down Expand Up @@ -364,6 +392,25 @@
"type": "object",
"additionalProperties": false
},
"Forwarding": {
"type": "object",
"required": [
"hops",
"memo"
],
"properties": {
"hops": {
"type": "array",
"items": {
"$ref": "#/definitions/Hop"
}
},
"memo": {
"type": "string"
}
},
"additionalProperties": false
},
"GovMsg": {
"description": "This message type allows the contract interact with the [x/gov] module in order to cast votes.\n\n[x/gov]: https://github.com/cosmos/cosmos-sdk/tree/v0.45.12/x/gov\n\n## Examples\n\nCast a simple vote:\n\n``` # use cosmwasm_std::{ # HexBinary, # Storage, Api, Querier, DepsMut, Deps, entry_point, Env, StdError, MessageInfo, # Response, QueryResponse, # }; # type ExecuteMsg = (); use cosmwasm_std::{GovMsg, VoteOption};\n\n#[entry_point] pub fn execute( deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result<Response, StdError> { // ... Ok(Response::new().add_message(GovMsg::Vote { proposal_id: 4, option: VoteOption::Yes, })) } ```\n\nCast a weighted vote:\n\n``` # use cosmwasm_std::{ # HexBinary, # Storage, Api, Querier, DepsMut, Deps, entry_point, Env, StdError, MessageInfo, # Response, QueryResponse, # }; # type ExecuteMsg = (); # #[cfg(feature = \"cosmwasm_1_2\")] use cosmwasm_std::{Decimal, GovMsg, VoteOption, WeightedVoteOption};\n\n# #[cfg(feature = \"cosmwasm_1_2\")] #[entry_point] pub fn execute( deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result<Response, StdError> { // ... Ok(Response::new().add_message(GovMsg::VoteWeighted { proposal_id: 4, options: vec![ WeightedVoteOption { option: VoteOption::Yes, weight: Decimal::percent(65), }, WeightedVoteOption { option: VoteOption::Abstain, weight: Decimal::percent(35), }, ], })) } ```",
"oneOf": [
Expand Down Expand Up @@ -402,8 +449,24 @@
}
]
},
"Hop": {
"type": "object",
"required": [
"channel_id",
"port_id"
],
"properties": {
"channel_id": {
"type": "string"
},
"port_id": {
"type": "string"
}
},
"additionalProperties": false
},
"IbcMsg": {
"description": "These are messages in the IBC lifecycle. Only usable by IBC-enabled contracts (contracts that directly speak the IBC protocol via 6 entry points)",
"description": "These are messages in the IBC lifecycle. Only usable by IBC-enabled contracts (contracts that directly speak the IBC protocol via 7 entry points)",
"oneOf": [
{
"description": "Sends bank tokens owned by the contract to the given address on another chain. The channel must already be established between the ibctransfer module on this chain and a matching module on the remote chain. We cannot select the port_id, this is whatever the local chain has bound the ibctransfer module to.",
Expand Down Expand Up @@ -458,6 +521,62 @@
},
"additionalProperties": false
},
{
"description": "Sends bank tokens owned by the contract to the given address on another chain. The channel must already be established between the ibctransfer module on this chain and a matching module on the remote chain. We cannot select the port_id, this is whatever the local chain has bound the ibctransfer module to.",
"type": "object",
"required": [
"transfer_v2"
],
"properties": {
"transfer_v2": {
"type": "object",
"required": [
"channel_id",
"forwarding",
"timeout",
"to_address",
"tokens"
],
"properties": {
"channel_id": {
"description": "existing channel to send the tokens over",
"type": "string"
},
"forwarding": {
"$ref": "#/definitions/Forwarding"
},
"memo": {
"description": "An optional memo. See the blog post [\"Moving Beyond Simple Token Transfers\"](https://medium.com/the-interchain-foundation/moving-beyond-simple-token-transfers-d42b2b1dc29b) for more information.\n\nThere is no difference between setting this to `None` or an empty string.\n\nThis field is only supported on chains with CosmWasm >= 2.0 and silently ignored on older chains. If you need support for both 1.x and 2.x chain with the same codebase, it is recommended to use `CosmosMsg::Stargate` with a custom MsgTransfer protobuf encoder instead.",
"type": [
"string",
"null"
]
},
"timeout": {
"description": "when packet times out, measured on remote chain",
"allOf": [
{
"$ref": "#/definitions/IbcTimeout"
}
]
},
"to_address": {
"description": "address on the remote chain to receive these tokens",
"type": "string"
},
"tokens": {
"description": "packet data only supports one coin https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/ibc/applications/transfer/v1/transfer.proto#L11-L20",
"type": "array",
"items": {
"$ref": "#/definitions/Token"
}
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"description": "Sends an IBC packet with given data over the existing channel. Data should be encoded in a format defined by the channel version, and the module on the other side should know how to parse this.",
"type": "object",
Expand Down Expand Up @@ -662,10 +781,37 @@
}
]
},
"Token": {
"type": "object",
"required": [
"amount",
"base",
"trace"
],
"properties": {
"amount": {
"$ref": "#/definitions/Uint256"
},
"base": {
"type": "string"
},
"trace": {
"type": "array",
"items": {
"$ref": "#/definitions/Hop"
}
}
},
"additionalProperties": false
},
"Uint128": {
"description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```",
"type": "string"
},
"Uint256": {
"description": "An implementation of u256 that is using strings for JSON encoding/decoding, such that the full u256 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances out of primitive uint types or `new` to provide big endian bytes:\n\n``` # use cosmwasm_std::Uint256; let a = Uint256::from(258u128); let b = Uint256::new([ 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, ]); assert_eq!(a, b); ```",
"type": "string"
},
"Uint64": {
"description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```",
"type": "string"
Expand Down
Loading
Loading