Skip to content

Commit

Permalink
feat: Add TransferV2
Browse files Browse the repository at this point in the history
  • Loading branch information
kulikthebird committed Jan 27, 2025
1 parent 589d242 commit 2c6e84d
Show file tree
Hide file tree
Showing 16 changed files with 1,199 additions and 70 deletions.
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
66 changes: 47 additions & 19 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,30 +65,55 @@ fn execute_transfer(
address: to_address.clone(),
gas_limit: None,
};
let coin = match &*info.funds {
[coin] if !coin.amount.is_zero() => coin,

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 send exactly one denom to trigger ics-20 transfer",
"Must specify \"V1\" or \"V2\" channel version",
))
}
};

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(),
};

Ok(Response::new()
.add_message(transfer_msg)
.add_attribute("action", "execute"))
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
120 changes: 119 additions & 1 deletion contracts/ibc-reflect-send/schema/ibc-reflect-send.json
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,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 +421,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 +493,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 +753,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

0 comments on commit 2c6e84d

Please sign in to comment.