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 all 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 @@ -27,6 +27,7 @@ members = [
"mm2src/mm2_db",
"mm2src/mm2_err_handle",
"mm2src/mm2_eth",
"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 @@ -58,6 +58,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
3 changes: 3 additions & 0 deletions mm2src/coins/lp_coins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1794,6 +1794,8 @@ pub enum WithdrawError {
AddressMismatchError { my_address: String, from: String },
#[display(fmt = "Contract type {} doesnt support 'withdraw_nft' yet", _0)]
ContractTypeDoesntSupportNftWithdrawing(String),
#[display(fmt = "Action not allowed for coin: {}", _0)]
ActionNotAllowed(String),
}

impl HttpStatusCode for WithdrawError {
Expand All @@ -1815,6 +1817,7 @@ impl HttpStatusCode for WithdrawError {
| WithdrawError::UnexpectedUserAction { .. }
| WithdrawError::CoinDoesntSupportNftWithdraw { .. }
| WithdrawError::AddressMismatchError { .. }
| WithdrawError::ActionNotAllowed(_)
| WithdrawError::ContractTypeDoesntSupportNftWithdrawing(_) => StatusCode::BAD_REQUEST,
WithdrawError::HwError(_) => StatusCode::GONE,
#[cfg(target_arch = "wasm32")]
Expand Down
1 change: 1 addition & 0 deletions mm2src/coins/rpc_command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ pub mod init_create_account;
pub mod init_scan_for_new_addresses;
pub mod init_withdraw;
#[cfg(not(target_arch = "wasm32"))] pub mod lightning;
pub mod tendermint;
35 changes: 35 additions & 0 deletions mm2src/coins/rpc_command/tendermint/ibc_chains.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use common::HttpStatusCode;
use mm2_core::mm_ctx::MmArc;
use mm2_err_handle::prelude::MmError;

use crate::tendermint;

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

#[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 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,
}
}
}

#[inline(always)]
pub async fn ibc_chains(_ctx: MmArc, _req: serde_json::Value) -> IBCChainRegistriesResult {
tendermint::get_ibc_chain_list().await
}
76 changes: 76 additions & 0 deletions mm2src/coins/rpc_command/tendermint/ibc_transfer_channels.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use common::HttpStatusCode;
use mm2_core::mm_ctx::MmArc;
use mm2_err_handle::prelude::MmError;

use crate::{lp_coinfind_or_err, MmCoinEnum};

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

#[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, 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),
}

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,
}
}
}

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,
_ => MmError::err(IBCTransferChannelsRequestError::UnsupportedCoin(req.coin)),
}
}
27 changes: 27 additions & 0 deletions mm2src/coins/rpc_command/tendermint/ibc_withdraw.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use common::Future01CompatExt;
use mm2_core::mm_ctx::MmArc;
use mm2_err_handle::prelude::MmError;
use mm2_number::BigDecimal;

use crate::{lp_coinfind_or_err, MmCoinEnum, WithdrawError, WithdrawResult};

#[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>,
}

pub async fn ibc_withdraw(ctx: MmArc, req: IBCWithdrawRequest) -> WithdrawResult {
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,
_ => MmError::err(WithdrawError::ActionNotAllowed(req.coin)),
}
}
14 changes: 14 additions & 0 deletions mm2src/coins/rpc_command/tendermint/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
mod ibc_chains;
mod ibc_transfer_channels;
mod ibc_withdraw;

pub use ibc_chains::*;
pub use ibc_transfer_channels::*;
pub use ibc_withdraw::*;

// Global constants for interacting with https://github.com/KomodoPlatform/chain-registry repository
// using `mm2_git` crate.
pub(crate) const CHAIN_REGISTRY_REPO_OWNER: &str = "KomodoPlatform";
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";
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>,
}
6 changes: 6 additions & 0 deletions mm2src/coins/tendermint/ibc/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
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
pub(crate) const IBC_GAS_LIMIT_DEFAULT: u64 = 150_000;
108 changes: 108 additions & 0 deletions mm2src/coins/tendermint/ibc/transfer_v1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
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;

#[inline(always)]
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;
}
3 changes: 3 additions & 0 deletions mm2src/coins/tendermint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Useful resources
// https://docs.cosmos.network/

mod ibc;
mod iris;
mod rpc;
mod tendermint_coin;
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";
}
5 changes: 3 additions & 2 deletions mm2src/coins/tendermint/rpc/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#[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