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

[r2r] cosmos ibc transfer implementation #1636

Merged
merged 19 commits into from
Mar 9, 2023
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
f691de2
save dev state (p.o.c)
onur-ozkan Jan 25, 2023
464f25f
implement `ibc_withdraw` RPC
onur-ozkan Jan 25, 2023
18f0808
impl integration test for `ibc_withdraw`
onur-ozkan Jan 27, 2023
99c0e39
unify tendermint `ibc_withdraw` methods
onur-ozkan Jan 30, 2023
33ce034
create `mm2_git` crate and implement Git abstraction layer
onur-ozkan Feb 2, 2023
9794039
implement `ibc_transfer_channels` and `ibc_chains`
onur-ozkan Feb 6, 2023
948e4bd
add wasm compatibility to `mm2_git::github_client`
onur-ozkan Feb 7, 2023
95031b3
Merge branch 'dev' of github.com:KomodoPlatform/atomicDEX-API into co…
onur-ozkan Feb 9, 2023
18b92d5
Merge branch 'dev' of github.com:KomodoPlatform/atomicDEX-API into co…
onur-ozkan Feb 15, 2023
9766d1a
use `KomodoPlatform` source for `chain-registry`
onur-ozkan Feb 15, 2023
b8e3172
move ibc rpc related sources into `coins::rpc_command`
onur-ozkan Feb 15, 2023
2b341b4
inline `try_from` for `MsgTransfer`
onur-ozkan Feb 15, 2023
dcba19b
Merge branch 'dev' of github.com:KomodoPlatform/atomicDEX-API into co…
onur-ozkan Feb 15, 2023
4dbbbad
use request ticker itself instead of platform one
onur-ozkan Feb 22, 2023
9614fd3
Merge branch 'dev' of github.com:KomodoPlatform/atomicDEX-API into co…
onur-ozkan Feb 23, 2023
12f35db
Merge branch 'dev' of github.com:KomodoPlatform/atomicDEX-API into co…
onur-ozkan Feb 25, 2023
7e75ec2
add new error variant `WithdrawError::ActionNotAllowed`
onur-ozkan Mar 8, 2023
c676fa2
Merge branch 'dev' of github.com:KomodoPlatform/atomicDEX-API into co…
onur-ozkan Mar 8, 2023
5dc620a
typo fix
onur-ozkan Mar 9, 2023
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
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ members = [
"mm2src/mm2_core",
"mm2src/mm2_db",
"mm2src/mm2_err_handle",
"mm2src/mm2_git",
"mm2src/mm2_io",
"mm2src/mm2_libp2p",
"mm2src/mm2_metamask",
Expand Down
1 change: 1 addition & 0 deletions mm2src/coins/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ lazy_static = "1.4"
libc = "0.2"
mm2_core = { path = "../mm2_core" }
mm2_err_handle = { path = "../mm2_err_handle" }
mm2_git = { path = "../mm2_git" }
mm2_io = { path = "../mm2_io" }
mm2_metrics = { path = "../mm2_metrics" }
mm2_net = { path = "../mm2_net" }
Expand Down
35 changes: 35 additions & 0 deletions mm2src/coins/lp_coins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,8 @@ use rpc_command::{init_account_balance::{AccountBalanceTaskManager, AccountBalan
init_withdraw::{WithdrawTaskManager, WithdrawTaskManagerShared}};

pub mod tendermint;
use tendermint::rpc::ibc::{IBCChainRegistriesResult, IBCTransferChannelsRequest, IBCTransferChannelsRequestError,
IBCTransferChannelsResult, IBCWithdrawRequest};
use tendermint::{CosmosTransaction, CustomTendermintMsgType, TendermintCoin, TendermintFeeDetails,
TendermintProtocolInfo, TendermintToken, TendermintTokenProtocolInfo};

Expand Down Expand Up @@ -2968,6 +2970,39 @@ pub async fn withdraw(ctx: MmArc, req: WithdrawRequest) -> WithdrawResult {
coin.withdraw(req).compat().await
}

pub async fn ibc_withdraw(ctx: MmArc, req: IBCWithdrawRequest) -> WithdrawResult {
shamardy marked this conversation as resolved.
Show resolved Hide resolved
let coin = lp_coinfind_or_err(&ctx, &req.coin).await?;
match coin {
MmCoinEnum::Tendermint(coin) => coin.ibc_withdraw(req).compat().await,
MmCoinEnum::TendermintToken(token) => token.ibc_withdraw(req).compat().await,
coin => MmError::err(WithdrawError::UnexpectedUserAction {
expected: format!(
"Only tendermint based coins are allowed for `ibc_withdraw` operation. Current coin: {}",
coin.platform_ticker()
),
}),
}
}

#[inline(always)]
pub async fn ibc_chains(_ctx: MmArc, _req: serde_json::Value) -> IBCChainRegistriesResult {
tendermint::get_ibc_chain_list().await
}

pub async fn ibc_transfer_channels(ctx: MmArc, req: IBCTransferChannelsRequest) -> IBCTransferChannelsResult {
let coin = lp_coinfind_or_err(&ctx, &req.coin)
.await
.map_err(|_| IBCTransferChannelsRequestError::NoSuchCoin(req.coin.clone()))?;

match coin {
MmCoinEnum::Tendermint(coin) => coin.get_ibc_transfer_channels(req).await,
MmCoinEnum::TendermintToken(token) => token.platform_coin.get_ibc_transfer_channels(req).await,
coin => MmError::err(IBCTransferChannelsRequestError::UnsupportedCoin(
coin.platform_ticker().to_owned(),
)),
}
}

pub async fn get_raw_transaction(ctx: MmArc, req: RawTransactionRequest) -> RawTransactionResult {
let coin = lp_coinfind_or_err(&ctx, &req.coin).await?;
coin.get_raw_transaction(req).compat().await
Expand Down
20 changes: 20 additions & 0 deletions mm2src/coins/tendermint/ibc/ibc_proto.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#[derive(prost::Message)]
pub(crate) struct IBCTransferV1Proto {
#[prost(string, tag = "1")]
pub(crate) source_port: prost::alloc::string::String,
#[prost(string, tag = "2")]
pub(crate) source_channel: prost::alloc::string::String,
#[prost(message, optional, tag = "3")]
pub(crate) token: Option<cosmrs::proto::cosmos::base::v1beta1::Coin>,
#[prost(string, tag = "4")]
pub(crate) sender: prost::alloc::string::String,
#[prost(string, tag = "5")]
pub(crate) receiver: prost::alloc::string::String,
#[prost(message, optional, tag = "6")]
pub(crate) timeout_height: Option<cosmrs::proto::ibc::core::client::v1::Height>,
#[prost(uint64, tag = "7")]
pub(crate) timeout_timestamp: u64,
// Not supported by some of the cosmos chains like IRIS
// #[prost(string, optional, tag = "8")]
// pub(crate) memo: Option<String>,
}
5 changes: 5 additions & 0 deletions mm2src/coins/tendermint/ibc/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mod ibc_proto;
pub(crate) mod transfer_v1;

pub(crate) const IBC_OUT_SOURCE_PORT: &str = "transfer";
pub(crate) const IBC_OUT_TIMEOUT_IN_NANOS: u64 = 60000000000 * 15; // 15 minutes
107 changes: 107 additions & 0 deletions mm2src/coins/tendermint/ibc/transfer_v1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use super::{ibc_proto::IBCTransferV1Proto, IBC_OUT_SOURCE_PORT, IBC_OUT_TIMEOUT_IN_NANOS};
use crate::tendermint::type_urls::IBC_TRANSFER_TYPE_URL;
use common::number_type_casting::SafeTypeCastingNumbers;
use cosmrs::{tx::{Msg, MsgProto},
AccountId, Coin, ErrorReport};
use std::convert::TryFrom;

#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct MsgTransfer {
/// the port on which the packet will be sent
pub(crate) source_port: String,
/// the channel by which the packet will be sent
pub(crate) source_channel: String,
/// the tokens to be transferred
pub(crate) token: Coin,
/// the sender address
pub(crate) sender: AccountId,
/// the recipient address on the destination chain
pub(crate) receiver: AccountId,
/// Timeout height relative to the current block height.
/// The timeout is disabled when set to 0.
pub(crate) timeout_height: Option<cosmrs::tendermint::block::Height>,
/// Timeout timestamp in absolute nanoseconds since unix epoch.
/// The timeout is disabled when set to 0.
pub(crate) timeout_timestamp: u64,
// Not supported by some of the cosmos chains like IRIS
// pub(crate) memo: Option<String>,
shamardy marked this conversation as resolved.
Show resolved Hide resolved
}

impl MsgTransfer {
pub(crate) fn new_with_default_timeout(
source_channel: String,
sender: AccountId,
receiver: AccountId,
token: Coin,
) -> Self {
let timestamp_as_nanos: u64 = common::get_local_duration_since_epoch()
.expect("get_local_duration_since_epoch shouldn't fail")
.as_nanos()
.into_or_max();

Self {
source_port: IBC_OUT_SOURCE_PORT.to_owned(),
source_channel,
sender,
receiver,
token,
timeout_height: None,
timeout_timestamp: timestamp_as_nanos + IBC_OUT_TIMEOUT_IN_NANOS,
// memo: Some(memo.clone()),
}
}
}

impl Msg for MsgTransfer {
type Proto = IBCTransferV1Proto;
}

impl TryFrom<IBCTransferV1Proto> for MsgTransfer {
type Error = ErrorReport;

fn try_from(proto: IBCTransferV1Proto) -> Result<MsgTransfer, Self::Error> { MsgTransfer::try_from(&proto) }
shamardy marked this conversation as resolved.
Show resolved Hide resolved
}

impl TryFrom<&IBCTransferV1Proto> for MsgTransfer {
type Error = ErrorReport;

fn try_from(proto: &IBCTransferV1Proto) -> Result<MsgTransfer, Self::Error> {
Ok(MsgTransfer {
source_port: proto.source_port.to_owned(),
source_channel: proto.source_channel.to_owned(),
token: proto
.token
.to_owned()
.map(TryFrom::try_from)
.ok_or_else(|| ErrorReport::msg("token can't be empty"))??,
sender: proto.sender.parse()?,
receiver: proto.receiver.parse()?,
timeout_height: None,
timeout_timestamp: proto.timeout_timestamp,
// memo: proto.memo.to_owned(),
})
}
}

impl From<MsgTransfer> for IBCTransferV1Proto {
fn from(coin: MsgTransfer) -> IBCTransferV1Proto { IBCTransferV1Proto::from(&coin) }
}

impl From<&MsgTransfer> for IBCTransferV1Proto {
fn from(msg: &MsgTransfer) -> IBCTransferV1Proto {
IBCTransferV1Proto {
source_port: msg.source_port.to_owned(),
source_channel: msg.source_channel.to_owned(),
token: Some(msg.token.to_owned().into()),
sender: msg.sender.to_string(),
receiver: msg.receiver.to_string(),
timeout_height: None,
timeout_timestamp: msg.timeout_timestamp,
// memo: msg.memo.to_owned(),
}
}
}

impl MsgProto for IBCTransferV1Proto {
const TYPE_URL: &'static str = IBC_TRANSFER_TYPE_URL;
}
5 changes: 4 additions & 1 deletion mm2src/coins/tendermint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
// Useful resources
// https://docs.cosmos.network/

mod ibc;
mod iris;
mod rpc;
pub mod rpc;
mod tendermint_coin;
mod tendermint_token;
pub mod tendermint_tx_history_v2;
Expand All @@ -25,6 +26,8 @@ pub(crate) const TENDERMINT_COIN_PROTOCOL_TYPE: &str = "TENDERMINT";
pub(crate) const TENDERMINT_ASSET_PROTOCOL_TYPE: &str = "TENDERMINTTOKEN";

pub(crate) mod type_urls {
pub(crate) const IBC_TRANSFER_TYPE_URL: &str = "/ibc.applications.transfer.v1.MsgTransfer";

pub(crate) const CREATE_HTLC_TYPE_URL: &str = "/irismod.htlc.MsgCreateHTLC";
pub(crate) const CLAIM_HTLC_TYPE_URL: &str = "/irismod.htlc.MsgClaimHTLC";
}
105 changes: 105 additions & 0 deletions mm2src/coins/tendermint/rpc/ibc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
use common::HttpStatusCode;
use mm2_err_handle::prelude::MmError;
use mm2_number::BigDecimal;

pub type IBCChainRegistriesResult = Result<IBCChainRegistriesResponse, MmError<IBCChainsRequestError>>;
pub type IBCTransferChannelsResult = Result<IBCTransferChannelsResponse, MmError<IBCTransferChannelsRequestError>>;

// Global constants for interacting with https://github.com/cosmos/chain-registry repository
shamardy marked this conversation as resolved.
Show resolved Hide resolved
// using `mm2_git` crate.
pub(crate) const CHAIN_REGISTRY_REPO_OWNER: &str = "cosmos";
pub(crate) const CHAIN_REGISTRY_REPO_NAME: &str = "chain-registry";
pub(crate) const CHAIN_REGISTRY_BRANCH: &str = "master";
pub(crate) const CHAIN_REGISTRY_IBC_DIR_NAME: &str = "_IBC";

#[derive(Clone, Deserialize)]
pub struct IBCWithdrawRequest {
pub(crate) ibc_source_channel: String,
pub(crate) coin: String,
pub(crate) to: String,
#[serde(default)]
pub(crate) amount: BigDecimal,
#[serde(default)]
pub(crate) max: bool,
pub(crate) memo: Option<String>,
}

#[derive(Clone, Deserialize)]
pub struct IBCTransferChannelsRequest {
pub(crate) coin: String,
pub(crate) destination_chain_registry_name: String,
}

#[derive(Clone, Serialize)]
pub struct IBCTransferChannelsResponse {
pub(crate) ibc_transfer_channels: Vec<IBCTransferChannel>,
}

#[derive(Clone, Serialize, Deserialize)]
pub(crate) struct IBCTransferChannel {
pub(crate) channel_id: String,
pub(crate) ordering: String,
pub(crate) version: String,
pub(crate) tags: Option<IBCTransferChannelTag>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct IBCTransferChannelTag {
pub(crate) status: String,
pub(crate) preferred: bool,
pub(crate) dex: Option<String>,
}

#[derive(Clone, Serialize)]
pub struct IBCChainRegistriesResponse {
pub(crate) chain_registry_list: Vec<String>,
}

#[derive(Clone, Debug, Display, Serialize, SerializeErrorType, PartialEq)]
#[serde(tag = "error_type", content = "error_data")]
pub enum IBCTransferChannelsRequestError {
#[display(fmt = "No such coin {}", _0)]
NoSuchCoin(String),
#[display(
fmt = "Only tendermint based coins are allowed for `ibc_transfer_channels` operation. Current coin: {}",
_0
)]
UnsupportedCoin(String),
#[display(fmt = "Could not find '{}' registry source.", _0)]
RegistrySourceCouldNotFound(String),
#[display(fmt = "Transport error: {}", _0)]
Transport(String),
#[display(fmt = "Internal error: {}", _0)]
InternalError(String),
}

#[derive(Clone, Debug, Display, Serialize, SerializeErrorType, PartialEq)]
#[serde(tag = "error_type", content = "error_data")]
pub enum IBCChainsRequestError {
#[display(fmt = "Transport error: {}", _0)]
Transport(String),
#[display(fmt = "Internal error: {}", _0)]
InternalError(String),
}

impl HttpStatusCode for IBCChainsRequestError {
fn status_code(&self) -> common::StatusCode {
match self {
IBCChainsRequestError::Transport(_) => common::StatusCode::SERVICE_UNAVAILABLE,
IBCChainsRequestError::InternalError(_) => common::StatusCode::INTERNAL_SERVER_ERROR,
}
}
}

impl HttpStatusCode for IBCTransferChannelsRequestError {
fn status_code(&self) -> common::StatusCode {
match self {
IBCTransferChannelsRequestError::UnsupportedCoin(_) | IBCTransferChannelsRequestError::NoSuchCoin(_) => {
common::StatusCode::BAD_REQUEST
},
IBCTransferChannelsRequestError::RegistrySourceCouldNotFound(_) => common::StatusCode::NOT_FOUND,
IBCTransferChannelsRequestError::Transport(_) => common::StatusCode::SERVICE_UNAVAILABLE,
IBCTransferChannelsRequestError::InternalError(_) => common::StatusCode::INTERNAL_SERVER_ERROR,
}
}
}
7 changes: 5 additions & 2 deletions mm2src/coins/tendermint/rpc/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
pub mod ibc;

#[cfg(not(target_arch = "wasm32"))] mod tendermint_native_rpc;
#[cfg(not(target_arch = "wasm32"))]
pub use tendermint_native_rpc::*;
pub(crate) use tendermint_native_rpc::*;

#[cfg(target_arch = "wasm32")] mod tendermint_wasm_rpc;
#[cfg(target_arch = "wasm32")] pub use tendermint_wasm_rpc::*;
#[cfg(target_arch = "wasm32")]
pub(crate) use tendermint_wasm_rpc::*;

pub(crate) const TX_SUCCESS_CODE: u32 = 0;

Expand Down
Loading