From 3357a1de008240cd6136620d75d0df76012886e1 Mon Sep 17 00:00:00 2001 From: yito88 Date: Mon, 17 Oct 2022 23:34:47 +0200 Subject: [PATCH 1/7] add ibc-transfer command --- apps/src/bin/anoma-client/cli.rs | 3 + apps/src/bin/anoma/cli.rs | 1 + apps/src/lib/cli.rs | 95 ++++++++++++++++++++++++++++++ apps/src/lib/client/tx.rs | 99 ++++++++++++++++++++++++++++++++ 4 files changed, 198 insertions(+) diff --git a/apps/src/bin/anoma-client/cli.rs b/apps/src/bin/anoma-client/cli.rs index b87cdb5c66..5b084c842e 100644 --- a/apps/src/bin/anoma-client/cli.rs +++ b/apps/src/bin/anoma-client/cli.rs @@ -18,6 +18,9 @@ pub async fn main() -> Result<()> { Sub::TxTransfer(TxTransfer(args)) => { tx::submit_transfer(ctx, args).await; } + Sub::TxIbcTransfer(TxIbcTransfer(args)) => { + tx::submit_ibc_transfer(ctx, args).await; + } Sub::TxUpdateVp(TxUpdateVp(args)) => { tx::submit_update_vp(ctx, args).await; } diff --git a/apps/src/bin/anoma/cli.rs b/apps/src/bin/anoma/cli.rs index ccde0c3618..cda1e8bc63 100644 --- a/apps/src/bin/anoma/cli.rs +++ b/apps/src/bin/anoma/cli.rs @@ -45,6 +45,7 @@ fn handle_command(cmd: cli::cmds::Anoma, raw_sub_cmd: String) -> Result<()> { cli::cmds::Anoma::Client(_) | cli::cmds::Anoma::TxCustom(_) | cli::cmds::Anoma::TxTransfer(_) + | cli::cmds::Anoma::TxIbcTransfer(_) | cli::cmds::Anoma::TxUpdateVp(_) | cli::cmds::Anoma::TxInitNft(_) | cli::cmds::Anoma::TxMintNft(_) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 4534d5bc0d..ea70f137b6 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -46,6 +46,7 @@ pub mod cmds { // Inlined commands from the client. TxCustom(TxCustom), TxTransfer(TxTransfer), + TxIbcTransfer(TxIbcTransfer), TxUpdateVp(TxUpdateVp), TxInitNft(TxInitNft), TxMintNft(TxMintNft), @@ -61,6 +62,7 @@ pub mod cmds { .subcommand(Ledger::def()) .subcommand(TxCustom::def()) .subcommand(TxTransfer::def()) + .subcommand(TxIbcTransfer::def()) .subcommand(TxUpdateVp::def()) .subcommand(TxInitNft::def()) .subcommand(TxMintNft::def()) @@ -75,6 +77,8 @@ pub mod cmds { let ledger = SubCmd::parse(matches).map(Self::Ledger); let tx_custom = SubCmd::parse(matches).map(Self::TxCustom); let tx_transfer = SubCmd::parse(matches).map(Self::TxTransfer); + let tx_ibc_transfer = + SubCmd::parse(matches).map(Self::TxIbcTransfer); let tx_update_vp = SubCmd::parse(matches).map(Self::TxUpdateVp); let tx_nft_create = SubCmd::parse(matches).map(Self::TxInitNft); let tx_nft_mint = SubCmd::parse(matches).map(Self::TxMintNft); @@ -87,6 +91,7 @@ pub mod cmds { .or(ledger) .or(tx_custom) .or(tx_transfer) + .or(tx_ibc_transfer) .or(tx_update_vp) .or(tx_nft_create) .or(tx_nft_mint) @@ -152,6 +157,7 @@ pub mod cmds { // Simple transactions .subcommand(TxCustom::def().display_order(1)) .subcommand(TxTransfer::def().display_order(1)) + .subcommand(TxIbcTransfer::def().display_order(1)) .subcommand(TxUpdateVp::def().display_order(1)) .subcommand(TxInitAccount::def().display_order(1)) .subcommand(TxInitValidator::def().display_order(1)) @@ -184,6 +190,7 @@ pub mod cmds { use AnomaClientWithContext::*; let tx_custom = Self::parse_with_ctx(matches, TxCustom); let tx_transfer = Self::parse_with_ctx(matches, TxTransfer); + let tx_ibc_transfer = Self::parse_with_ctx(matches, TxIbcTransfer); let tx_update_vp = Self::parse_with_ctx(matches, TxUpdateVp); let tx_init_account = Self::parse_with_ctx(matches, TxInitAccount); let tx_init_validator = @@ -213,6 +220,7 @@ pub mod cmds { let utils = SubCmd::parse(matches).map(Self::WithoutContext); tx_custom .or(tx_transfer) + .or(tx_ibc_transfer) .or(tx_update_vp) .or(tx_init_account) .or(tx_init_validator) @@ -271,6 +279,7 @@ pub mod cmds { // Ledger cmds TxCustom(TxCustom), TxTransfer(TxTransfer), + TxIbcTransfer(TxIbcTransfer), QueryResult(QueryResult), TxUpdateVp(TxUpdateVp), TxInitAccount(TxInitAccount), @@ -794,6 +803,25 @@ pub mod cmds { } } + #[derive(Clone, Debug)] + pub struct TxIbcTransfer(pub args::TxIbcTransfer); + + impl SubCmd for TxIbcTransfer { + const CMD: &'static str = "ibc-transfer"; + + fn parse(matches: &ArgMatches) -> Option { + matches.subcommand_matches(Self::CMD).map(|matches| { + TxIbcTransfer(args::TxIbcTransfer::parse(matches)) + }) + } + + fn def() -> App { + App::new(Self::CMD) + .about("Send a signed IBC transfer transaction.") + .add_args::() + } + } + #[derive(Clone, Debug)] pub struct TxUpdateVp(pub args::TxUpdateVp); @@ -1248,6 +1276,7 @@ pub mod args { use std::path::PathBuf; use std::str::FromStr; + use namada::ibc::core::ics24_host::identifier::{ChannelId, PortId}; use namada::types::address::Address; use namada::types::chain::{ChainId, ChainIdPrefix}; use namada::types::governance::ProposalVote; @@ -1281,6 +1310,7 @@ pub mod args { const CHAIN_ID: Arg = arg("chain-id"); const CHAIN_ID_OPT: ArgOpt = CHAIN_ID.opt(); const CHAIN_ID_PREFIX: Arg = arg("chain-prefix"); + const CHANNEL_ID: Arg = arg("channel-id"); const CODE_PATH: Arg = arg("code-path"); const CODE_PATH_OPT: ArgOpt = CODE_PATH.opt(); const CONSENSUS_TIMEOUT_COMMIT: ArgDefault = arg_default( @@ -1318,6 +1348,10 @@ pub mod args { const NET_ADDRESS: Arg = arg("net-address"); const NFT_ADDRESS: Arg
= arg("nft-address"); const OWNER: ArgOpt = arg_opt("owner"); + const PORT_ID: ArgDefault = arg_default( + "port-id", + DefaultFn(|| PortId::from_str("transfer").unwrap()), + ); const PROPOSAL_OFFLINE: ArgFlag = flag("offline"); const PROTOCOL_KEY: ArgOpt = arg_opt("protocol-key"); const PRE_GENESIS_PATH: ArgOpt = arg_opt("pre-genesis-path"); @@ -1328,6 +1362,7 @@ pub mod args { const RAW_ADDRESS: Arg
= arg("address"); const RAW_ADDRESS_OPT: ArgOpt
= RAW_ADDRESS.opt(); const RAW_PUBLIC_KEY_OPT: ArgOpt = arg_opt("public-key"); + const RECEIVER: Arg = arg("receiver"); const REWARDS_CODE_PATH: ArgOpt = arg_opt("rewards-code-path"); const REWARDS_KEY: ArgOpt = arg_opt("rewards-key"); const SCHEME: ArgDefault = @@ -1515,6 +1550,66 @@ pub mod args { } } + /// IBC transfer transaction arguments + #[derive(Clone, Debug)] + pub struct TxIbcTransfer { + /// Common tx arguments + pub tx: Tx, + /// Transfer source address + pub source: WalletAddress, + /// Transfer target address + pub receiver: String, + /// Transferred token address + pub token: WalletAddress, + /// Transferred token address + pub sub_prefix: Option, + /// Transferred token amount + pub amount: token::Amount, + /// Port ID + pub port_id: PortId, + /// Channel ID + pub channel_id: ChannelId, + } + + impl Args for TxIbcTransfer { + fn parse(matches: &ArgMatches) -> Self { + let tx = Tx::parse(matches); + let source = SOURCE.parse(matches); + let receiver = RECEIVER.parse(matches); + let token = TOKEN.parse(matches); + let sub_prefix = SUB_PREFIX.parse(matches); + let amount = AMOUNT.parse(matches); + let port_id = PORT_ID.parse(matches); + let channel_id = CHANNEL_ID.parse(matches); + Self { + tx, + source, + receiver, + token, + sub_prefix, + amount, + port_id, + channel_id, + } + } + + fn def(app: App) -> App { + app.add_args::() + .arg(SOURCE.def().about( + "The source account address. The source's key is used to \ + produce the signature.", + )) + .arg(RECEIVER.def().about( + "The receiver address on the destination chain as string.", + )) + .arg(TOKEN.def().about("The transfer token.")) + .arg(SUB_PREFIX.def().about("The token's sub prefix.")) + .arg(AMOUNT.def().about("The amount to transfer in decimal.")) + .arg(PORT_ID.def().about("The port ID.")) + .arg(CHANNEL_ID.def().about("The channel ID.")) + } + } + /// Transaction to initialize a new account #[derive(Clone, Debug)] pub struct TxInitAccount { diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 7a4d542536..4dd3e12e78 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -8,6 +8,12 @@ use async_std::io::prelude::WriteExt; use async_std::io::{self}; use borsh::BorshSerialize; use itertools::Either::*; +use namada::ibc::applications::ics20_fungible_token_transfer::msgs::transfer::MsgTransfer; +use namada::ibc::signer::Signer; +use namada::ibc::timestamp::Timestamp as IbcTimestamp; +use namada::ibc::tx_msg::Msg; +use namada::ibc::Height as IbcHeight; +use namada::ibc_proto::cosmos::base::v1beta1::Coin; use namada::ledger::governance::storage as gov_storage; use namada::ledger::pos::{BondId, Bonds, Unbonds}; use namada::proto::Tx; @@ -49,6 +55,7 @@ const TX_INIT_PROPOSAL: &str = "tx_init_proposal.wasm"; const TX_VOTE_PROPOSAL: &str = "tx_vote_proposal.wasm"; const TX_UPDATE_VP_WASM: &str = "tx_update_vp.wasm"; const TX_TRANSFER_WASM: &str = "tx_transfer.wasm"; +const TX_IBC_WASM: &str = "tx_ibc.wasm"; const TX_INIT_NFT: &str = "tx_init_nft.wasm"; const TX_MINT_NFT: &str = "tx_mint_nft.wasm"; const VP_USER_WASM: &str = "vp_user.wasm"; @@ -465,6 +472,98 @@ pub async fn submit_transfer(ctx: Context, args: args::TxTransfer) { process_tx(ctx, &args.tx, tx, Some(&args.source)).await; } +pub async fn submit_ibc_transfer(ctx: Context, args: args::TxIbcTransfer) { + let source = ctx.get(&args.source); + // Check that the source address exists on chain + let source_exists = + rpc::known_address(&source, args.tx.ledger_address.clone()).await; + if !source_exists { + eprintln!("The source address {} doesn't exist on chain.", source); + if !args.tx.force { + safe_exit(1) + } + } + + // We cannot check the receiver + + let token = ctx.get(&args.token); + // Check that the token address exists on chain + let token_exists = + rpc::known_address(&token, args.tx.ledger_address.clone()).await; + if !token_exists { + eprintln!("The token address {} doesn't exist on chain.", token); + if !args.tx.force { + safe_exit(1) + } + } + // Check source balance + let (sub_prefix, balance_key) = match args.sub_prefix { + Some(sub_prefix) => { + let sub_prefix = storage::Key::parse(sub_prefix).unwrap(); + let prefix = token::multitoken_balance_prefix(&token, &sub_prefix); + ( + Some(sub_prefix), + token::multitoken_balance_key(&prefix, &source), + ) + } + None => (None, token::balance_key(&token, &source)), + }; + let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); + match rpc::query_storage_value::(&client, &balance_key).await + { + Some(balance) => { + if balance < args.amount { + eprintln!( + "The balance of the source {} of token {} is lower than \ + the amount to be transferred. Amount to transfer is {} \ + and the balance is {}.", + source, token, args.amount, balance + ); + if !args.tx.force { + safe_exit(1) + } + } + } + None => { + eprintln!( + "No balance found for the source {} of token {}", + source, token + ); + if !args.tx.force { + safe_exit(1) + } + } + } + let tx_code = ctx.read_wasm(TX_IBC_WASM); + + let denom = match sub_prefix { + Some(sp) => format!("{}/{}", sp, token), + None => token.to_string(), + }; + let token = Some(Coin { + denom, + amount: args.amount.to_string(), + }); + let msg = MsgTransfer { + source_port: args.port_id, + source_channel: args.channel_id, + token, + sender: Signer::new(source.to_string()), + receiver: Signer::new(args.receiver), + // TODO timeout isn't supported for now + timeout_height: IbcHeight::new(0, 1000), + timeout_timestamp: IbcTimestamp::none(), + }; + tracing::debug!("IBC transfer message {:?}", msg); + let any_msg = msg.to_any(); + let mut data = vec![]; + prost::Message::encode(&any_msg, &mut data) + .expect("Encoding tx data shouldn't fail"); + + let tx = Tx::new(tx_code, Some(data)); + process_tx(ctx, &args.tx, tx, Some(&args.source)).await; +} + pub async fn submit_init_nft(ctx: Context, args: args::NftCreate) { let file = File::open(&args.nft_data).expect("File must exist."); let nft: Nft = serde_json::from_reader(file) From b202c2fd3490cc96693a184e278dd67611fc7e34 Mon Sep 17 00:00:00 2001 From: yito88 Date: Tue, 18 Oct 2022 00:11:56 +0200 Subject: [PATCH 2/7] fix timeout --- apps/src/lib/cli.rs | 16 ++++++++++++++++ apps/src/lib/client/tx.rs | 26 +++++++++++++++++++++++--- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index ea70f137b6..4b67e25c4f 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -1375,6 +1375,8 @@ pub mod args { const STORAGE_KEY: Arg = arg("storage-key"); const SUB_PREFIX: ArgOpt = arg_opt("sub-prefix"); const TARGET: Arg = arg("target"); + const TIMEOUT_HEIGHT_OFFSET: ArgOpt = arg_opt("timeout-height-offset"); + const TIMEOUT_SEC_OFFSET: ArgOpt = arg_opt("timeout-sec-offset"); const TOKEN_OPT: ArgOpt = TOKEN.opt(); const TOKEN: Arg = arg("token"); const TX_HASH: Arg = arg("tx-hash"); @@ -1569,6 +1571,10 @@ pub mod args { pub port_id: PortId, /// Channel ID pub channel_id: ChannelId, + /// Timeout height offset + pub timeout_height_offset: Option, + /// Timeout timestamp offset + pub timeout_sec_offset: Option, } impl Args for TxIbcTransfer { @@ -1581,6 +1587,8 @@ pub mod args { let amount = AMOUNT.parse(matches); let port_id = PORT_ID.parse(matches); let channel_id = CHANNEL_ID.parse(matches); + let timeout_height_offset = TIMEOUT_HEIGHT_OFFSET.parse(matches); + let timeout_sec_offset = TIMEOUT_SEC_OFFSET.parse(matches); Self { tx, source, @@ -1590,6 +1598,8 @@ pub mod args { amount, port_id, channel_id, + timeout_height_offset, + timeout_sec_offset, } } @@ -1607,6 +1617,12 @@ pub mod args { .arg(AMOUNT.def().about("The amount to transfer in decimal.")) .arg(PORT_ID.def().about("The port ID.")) .arg(CHANNEL_ID.def().about("The channel ID.")) + .arg( + TIMEOUT_HEIGHT_OFFSET + .def() + .about("The timeout height offset."), + ) + .arg(TIMEOUT_SEC_OFFSET.def().about("The timeout as seconds.")) } } diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 4dd3e12e78..bc70765793 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -544,15 +544,35 @@ pub async fn submit_ibc_transfer(ctx: Context, args: args::TxIbcTransfer) { denom, amount: args.amount.to_string(), }); + + let timeout_height = match args.timeout_height_offset { + Some(offset) => { + let current_height: u64 = match client.status().await { + Ok(resp) => resp.sync_info.latest_block_height.into(), + Err(e) => { + eprintln!("Getting the current height failed: {}", e); + safe_exit(1) + } + }; + IbcHeight::new(0, current_height + offset) + } + None => IbcHeight::zero(), + }; + + let timeout_timestamp = if let Some(offset) = args.timeout_sec_offset { + (IbcTimestamp::now() + Duration::new(offset, 0)).unwrap() + } else { + IbcTimestamp::none() + }; + let msg = MsgTransfer { source_port: args.port_id, source_channel: args.channel_id, token, sender: Signer::new(source.to_string()), receiver: Signer::new(args.receiver), - // TODO timeout isn't supported for now - timeout_height: IbcHeight::new(0, 1000), - timeout_timestamp: IbcTimestamp::none(), + timeout_height, + timeout_timestamp, }; tracing::debug!("IBC transfer message {:?}", msg); let any_msg = msg.to_any(); From fbad8c5cc39b00893c8aa1e713716cc4d2abe0d4 Mon Sep 17 00:00:00 2001 From: yito88 Date: Tue, 18 Oct 2022 12:26:09 +0200 Subject: [PATCH 3/7] fix timeout height --- apps/src/lib/cli.rs | 14 +++++++------- apps/src/lib/client/tx.rs | 14 +++----------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 4b67e25c4f..ce53859ee8 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -1375,7 +1375,7 @@ pub mod args { const STORAGE_KEY: Arg = arg("storage-key"); const SUB_PREFIX: ArgOpt = arg_opt("sub-prefix"); const TARGET: Arg = arg("target"); - const TIMEOUT_HEIGHT_OFFSET: ArgOpt = arg_opt("timeout-height-offset"); + const TIMEOUT_HEIGHT: ArgOpt = arg_opt("timeout-height"); const TIMEOUT_SEC_OFFSET: ArgOpt = arg_opt("timeout-sec-offset"); const TOKEN_OPT: ArgOpt = TOKEN.opt(); const TOKEN: Arg = arg("token"); @@ -1571,8 +1571,8 @@ pub mod args { pub port_id: PortId, /// Channel ID pub channel_id: ChannelId, - /// Timeout height offset - pub timeout_height_offset: Option, + /// Timeout height of the destination chain + pub timeout_height: Option, /// Timeout timestamp offset pub timeout_sec_offset: Option, } @@ -1587,7 +1587,7 @@ pub mod args { let amount = AMOUNT.parse(matches); let port_id = PORT_ID.parse(matches); let channel_id = CHANNEL_ID.parse(matches); - let timeout_height_offset = TIMEOUT_HEIGHT_OFFSET.parse(matches); + let timeout_height = TIMEOUT_HEIGHT.parse(matches); let timeout_sec_offset = TIMEOUT_SEC_OFFSET.parse(matches); Self { tx, @@ -1598,7 +1598,7 @@ pub mod args { amount, port_id, channel_id, - timeout_height_offset, + timeout_height, timeout_sec_offset, } } @@ -1618,9 +1618,9 @@ pub mod args { .arg(PORT_ID.def().about("The port ID.")) .arg(CHANNEL_ID.def().about("The channel ID.")) .arg( - TIMEOUT_HEIGHT_OFFSET + TIMEOUT_HEIGHT .def() - .about("The timeout height offset."), + .about("The timeout height of the destination chain."), ) .arg(TIMEOUT_SEC_OFFSET.def().about("The timeout as seconds.")) } diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index bc70765793..a7e063d62a 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -545,17 +545,9 @@ pub async fn submit_ibc_transfer(ctx: Context, args: args::TxIbcTransfer) { amount: args.amount.to_string(), }); - let timeout_height = match args.timeout_height_offset { - Some(offset) => { - let current_height: u64 = match client.status().await { - Ok(resp) => resp.sync_info.latest_block_height.into(), - Err(e) => { - eprintln!("Getting the current height failed: {}", e); - safe_exit(1) - } - }; - IbcHeight::new(0, current_height + offset) - } + // this height should be that of the destination chain, not this chain + let timeout_height = match args.timeout_height { + Some(h) => IbcHeight::new(0, h), None => IbcHeight::zero(), }; From 24e7619eebf41f01eabccff13ab0cf094595d63b Mon Sep 17 00:00:00 2001 From: yito88 Date: Tue, 18 Oct 2022 12:29:20 +0200 Subject: [PATCH 4/7] fix timeout timestamp --- apps/src/lib/client/tx.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index a7e063d62a..b42bc57f9e 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -553,6 +553,9 @@ pub async fn submit_ibc_transfer(ctx: Context, args: args::TxIbcTransfer) { let timeout_timestamp = if let Some(offset) = args.timeout_sec_offset { (IbcTimestamp::now() + Duration::new(offset, 0)).unwrap() + } else if timeout_height.is_zero() { + // we cannot set 0 to both the height and the timestamp + (IbcTimestamp::now() + Duration::new(3600, 0)).unwrap() } else { IbcTimestamp::none() }; From 0e4093b9622938f78d431ceb19bd5132fbf8cb9d Mon Sep 17 00:00:00 2001 From: yito88 Date: Tue, 18 Oct 2022 14:01:05 +0200 Subject: [PATCH 5/7] replace now func --- apps/src/lib/client/tx.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index b42bc57f9e..0783700d6a 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -24,6 +24,7 @@ use namada::types::governance::{ use namada::types::key::*; use namada::types::nft::{self, Nft, NftToken}; use namada::types::storage::Epoch; +use namada::types::time::DateTimeUtc; use namada::types::transaction::governance::{ InitProposalData, VoteProposalData, }; @@ -551,11 +552,13 @@ pub async fn submit_ibc_transfer(ctx: Context, args: args::TxIbcTransfer) { None => IbcHeight::zero(), }; + let now: namada::tendermint::Time = DateTimeUtc::now().try_into().unwrap(); + let now: IbcTimestamp = now.into(); let timeout_timestamp = if let Some(offset) = args.timeout_sec_offset { - (IbcTimestamp::now() + Duration::new(offset, 0)).unwrap() + (now + Duration::new(offset, 0)).unwrap() } else if timeout_height.is_zero() { // we cannot set 0 to both the height and the timestamp - (IbcTimestamp::now() + Duration::new(3600, 0)).unwrap() + (now + Duration::new(3600, 0)).unwrap() } else { IbcTimestamp::none() }; From 40666b1539c7dde16b5d0371fe1ee3ab67e2c2f5 Mon Sep 17 00:00:00 2001 From: yito88 Date: Tue, 18 Oct 2022 15:34:45 +0200 Subject: [PATCH 6/7] fix sub-prefix --- apps/src/lib/client/tx.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 0783700d6a..edb8c56f13 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -23,7 +23,7 @@ use namada::types::governance::{ }; use namada::types::key::*; use namada::types::nft::{self, Nft, NftToken}; -use namada::types::storage::Epoch; +use namada::types::storage::{Epoch, RESERVED_ADDRESS_PREFIX}; use namada::types::time::DateTimeUtc; use namada::types::transaction::governance::{ InitProposalData, VoteProposalData, @@ -538,7 +538,8 @@ pub async fn submit_ibc_transfer(ctx: Context, args: args::TxIbcTransfer) { let tx_code = ctx.read_wasm(TX_IBC_WASM); let denom = match sub_prefix { - Some(sp) => format!("{}/{}", sp, token), + // To parse IbcToken address, remove the address prefix + Some(sp) => sp.to_string().replace(RESERVED_ADDRESS_PREFIX, ""), None => token.to_string(), }; let token = Some(Coin { From 7008dec1323e870a4e2f03c2f4879a87c59daf57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Mon, 14 Nov 2022 17:55:05 +0100 Subject: [PATCH 7/7] changelog: add #626 --- .changelog/unreleased/features/626-ibc-transfer-cmd.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/features/626-ibc-transfer-cmd.md diff --git a/.changelog/unreleased/features/626-ibc-transfer-cmd.md b/.changelog/unreleased/features/626-ibc-transfer-cmd.md new file mode 100644 index 0000000000..23fd574b3b --- /dev/null +++ b/.changelog/unreleased/features/626-ibc-transfer-cmd.md @@ -0,0 +1,2 @@ +- Add client command 'ibc-transfer'. + ([#626](https://github.com/anoma/namada/pull/626)) \ No newline at end of file