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 17707be commit 9f64ce2
Show file tree
Hide file tree
Showing 6 changed files with 383 additions and 27 deletions.
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
75 changes: 73 additions & 2 deletions packages/std/src/ibc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,63 @@
// The rest of the IBC related functionality is defined here

use core::cmp::{Ord, Ordering, PartialOrd};
use std::vec;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::coin::Coin;
use crate::prelude::*;
use crate::{prelude::*, Uint256};
use crate::results::{Attribute, CosmosMsg, Empty, Event, SubMsg};
use crate::StdResult;
use crate::{to_json_binary, Binary};
use crate::{Addr, Timestamp};

mod callbacks;
mod transfer_msg_builder;
mod transfer_msg_builder_v2;

pub use callbacks::*;
pub use transfer_msg_builder::*;
pub use transfer_msg_builder_v2::*;


#[non_exhaustive]
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct Token {
base: String,
trace: Vec<Hop>,
amount: Uint256,
}

impl From<Coin> for Token {
fn from(w: Coin) -> Token {
Token {
base: w.denom,
trace: vec![],
amount: w.amount.into(),
}
}
}

#[non_exhaustive]
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema, Default)]
#[serde(rename_all = "snake_case")]
pub struct Forwarding {
hops: Vec<Hop>,
memo: String,
}

#[non_exhaustive]
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct Hop {
port_id: String,
channel_id: String,
}

/// These are messages in the IBC lifecycle. Only usable by IBC-enabled contracts
/// (contracts that directly speak the IBC protocol via 6 entry points)
/// (contracts that directly speak the IBC protocol via 7 entry points)
#[non_exhaustive]
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
Expand Down Expand Up @@ -52,6 +91,38 @@ pub enum IbcMsg {
/// protobuf encoder instead.
memo: Option<String>,
},
/// 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.
TransferV2 {
/// existing channel to send the tokens over
channel_id: String,
/// address on the remote chain to receive these tokens
to_address: String,
/// 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
tokens: Vec<Token>,
/// when packet times out, measured on remote chain
timeout: IbcTimeout,
/// 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.
///
/// There is no difference between setting this to `None` or an empty string.
///
/// This 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.
memo: Option<String>,
// a struct containing the list of next hops,
// determining where the tokens must be forwarded next,
// and the memo for the final hop
forwarding: Forwarding,
},
/// 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.
Expand Down
14 changes: 9 additions & 5 deletions packages/std/src/ibc/transfer_msg_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,25 @@ use crate::{
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct EmptyMemo;
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct WithMemo {
memo: String,
pub memo: String,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct WithSrcCallback {
src_callback: IbcSrcCallback,
pub src_callback: IbcSrcCallback,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct WithDstCallback {
dst_callback: IbcDstCallback,
pub dst_callback: IbcDstCallback,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct WithCallbacks {
src_callback: IbcSrcCallback,
dst_callback: IbcDstCallback,
pub src_callback: IbcSrcCallback,
pub dst_callback: IbcDstCallback,
}

pub trait MemoSource {
Expand Down
Loading

0 comments on commit 9f64ce2

Please sign in to comment.