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

Receive a token to a payment address over IBC #1917

Merged
merged 14 commits into from
Oct 24, 2023
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- IBC transfer to a payment address
([\#1917](https://github.com/anoma/namada/issues/1917))
2 changes: 2 additions & 0 deletions .changelog/unreleased/improvements/1946-ibc-balance-query.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Query also IBC token balances
([\#1946](https://github.com/anoma/namada/issues/1946))
100 changes: 96 additions & 4 deletions apps/src/lib/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ pub mod cmds {
.subcommand(QueryValidatorState::def().display_order(5))
// Actions
.subcommand(SignTx::def().display_order(6))
.subcommand(GenIbcShieldedTransafer::def().display_order(6))
// Utils
.subcommand(Utils::def().display_order(7))
}
Expand Down Expand Up @@ -313,6 +314,8 @@ pub mod cmds {
let add_to_eth_bridge_pool =
Self::parse_with_ctx(matches, AddToEthBridgePool);
let sign_tx = Self::parse_with_ctx(matches, SignTx);
let gen_ibc_shielded =
Self::parse_with_ctx(matches, GenIbcShieldedTransafer);
let utils = SubCmd::parse(matches).map(Self::WithoutContext);
tx_custom
.or(tx_transfer)
Expand Down Expand Up @@ -350,6 +353,7 @@ pub mod cmds {
.or(query_validator_state)
.or(query_account)
.or(sign_tx)
.or(gen_ibc_shielded)
.or(utils)
}
}
Expand Down Expand Up @@ -424,6 +428,7 @@ pub mod cmds {
QueryPgf(QueryPgf),
QueryValidatorState(QueryValidatorState),
SignTx(SignTx),
GenIbcShieldedTransafer(GenIbcShieldedTransafer),
}

#[allow(clippy::large_enum_variant)]
Expand Down Expand Up @@ -1874,6 +1879,29 @@ pub mod cmds {
}
}

#[derive(Clone, Debug)]
pub struct GenIbcShieldedTransafer(
pub args::GenIbcShieldedTransafer<args::CliTypes>,
);

impl SubCmd for GenIbcShieldedTransafer {
const CMD: &'static str = "ibc-gen-shielded";

fn parse(matches: &ArgMatches) -> Option<Self> {
matches.subcommand_matches(Self::CMD).map(|matches| {
GenIbcShieldedTransafer(args::GenIbcShieldedTransafer::parse(
matches,
))
})
}

fn def() -> App {
App::new(Self::CMD)
.about("Generate shielded transfer for IBC.")
.add_args::<args::GenIbcShieldedTransafer<args::CliTypes>>()
}
}

#[derive(Clone, Debug)]
pub struct EpochSleep(pub args::Query<args::CliTypes>);

Expand Down Expand Up @@ -2659,7 +2687,7 @@ pub mod args {
pub const HD_WALLET_DERIVATION_PATH_OPT: ArgOpt<String> =
HD_WALLET_DERIVATION_PATH.opt();
pub const HISTORIC: ArgFlag = flag("historic");
pub const IBC_TRANSFER_MEMO: ArgOpt<String> = arg_opt("memo");
pub const IBC_TRANSFER_MEMO_PATH: ArgOpt<PathBuf> = arg_opt("memo-path");
pub const LEDGER_ADDRESS_ABOUT: &str =
"Address of a ledger node as \"{scheme}://{host}:{port}\". If the \
scheme is not supplied, it is assumed to be TCP.";
Expand Down Expand Up @@ -2713,6 +2741,7 @@ pub mod args {
pub const SAFE_MODE: ArgFlag = flag("safe-mode");
pub const SCHEME: ArgDefault<SchemeType> =
arg_default("scheme", DefaultFn(|| SchemeType::Ed25519));
pub const SENDER: Arg<String> = arg("sender");
pub const SIGNING_KEYS: ArgMulti<WalletKeypair> = arg_multi("signing-keys");
pub const SIGNATURES: ArgMulti<PathBuf> = arg_multi("signatures");
pub const SOURCE: Arg<WalletAddress> = arg("source");
Expand Down Expand Up @@ -3574,7 +3603,10 @@ pub mod args {
let channel_id = CHANNEL_ID.parse(matches);
let timeout_height = TIMEOUT_HEIGHT.parse(matches);
let timeout_sec_offset = TIMEOUT_SEC_OFFSET.parse(matches);
let memo = IBC_TRANSFER_MEMO.parse(matches);
let memo = IBC_TRANSFER_MEMO_PATH.parse(matches).map(|path| {
std::fs::read_to_string(path)
.expect("Expected a file at given path")
});
let tx_code_path = PathBuf::from(TX_IBC_WASM);
Self {
tx,
Expand Down Expand Up @@ -3611,9 +3643,9 @@ pub mod args {
)
.arg(TIMEOUT_SEC_OFFSET.def().help("The timeout as seconds."))
.arg(
IBC_TRANSFER_MEMO
IBC_TRANSFER_MEMO_PATH
.def()
.help("Memo field of ICS20 transfer."),
.help("The path for the memo field of ICS20 transfer."),
)
}
}
Expand Down Expand Up @@ -4731,6 +4763,66 @@ pub mod args {
}
}

impl CliToSdk<GenIbcShieldedTransafer<SdkTypes>>
for GenIbcShieldedTransafer<CliTypes>
{
fn to_sdk(
self,
ctx: &mut Context,
) -> GenIbcShieldedTransafer<SdkTypes> {
GenIbcShieldedTransafer::<SdkTypes> {
query: self.query.to_sdk(ctx),
output_folder: self.output_folder,
target: ctx.get(&self.target),
token: ctx.get(&self.token),
amount: self.amount,
port_id: self.port_id,
channel_id: self.channel_id,
}
}
}

impl Args for GenIbcShieldedTransafer<CliTypes> {
fn parse(matches: &ArgMatches) -> Self {
let query = Query::parse(matches);
let output_folder = OUTPUT_FOLDER_PATH.parse(matches);
let target = TRANSFER_TARGET.parse(matches);
let token = TOKEN.parse(matches);
let amount = InputAmount::Unvalidated(AMOUNT.parse(matches));
let port_id = PORT_ID.parse(matches);
let channel_id = CHANNEL_ID.parse(matches);
Self {
query,
output_folder,
target,
token,
amount,
port_id,
channel_id,
}
}

fn def(app: App) -> App {
app.add_args::<Query<CliTypes>>()
.arg(OUTPUT_FOLDER_PATH.def().help(
"The output folder path where the artifact will be stored.",
))
.arg(TRANSFER_TARGET.def().help("The target address."))
.arg(TOKEN.def().help("The transfer token."))
.arg(AMOUNT.def().help("The amount to transfer in decimal."))
.arg(
PORT_ID
.def()
.help("The port ID via which the token is received."),
)
.arg(
CHANNEL_ID.def().help(
"The channel ID via which the token is received.",
),
)
}
}

impl CliToSdk<QueryCommissionRate<SdkTypes>> for QueryCommissionRate<CliTypes> {
fn to_sdk(self, ctx: &mut Context) -> QueryCommissionRate<SdkTypes> {
QueryCommissionRate::<SdkTypes> {
Expand Down
18 changes: 18 additions & 0 deletions apps/src/lib/cli/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,24 @@ impl<IO: Io> CliApi<IO> {
let args = args.to_sdk(&mut ctx);
tx::sign_tx::<_, IO>(&client, &mut ctx, args).await?;
}
Sub::GenIbcShieldedTransafer(GenIbcShieldedTransafer(
mut args,
)) => {
let client = client.unwrap_or_else(|| {
C::from_tendermint_address(
&mut args.query.ledger_address,
)
});
client
.wait_until_node_is_synced::<IO>()
.await
.proceed_or_else(error)?;
let args = args.to_sdk(&mut ctx);
tx::gen_ibc_shielded_transfer::<_, IO>(
&client, &mut ctx, args,
)
.await?;
}
}
}
cli::NamadaClient::WithoutContext(cmd, global_args) => match cmd {
Expand Down
16 changes: 16 additions & 0 deletions apps/src/lib/cli/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ use std::path::{Path, PathBuf};
use std::str::FromStr;

use color_eyre::eyre::Result;
use namada::ledger::ibc::storage::ibc_token;
use namada::sdk::masp::ShieldedContext;
use namada::sdk::wallet::Wallet;
use namada::types::address::{Address, InternalAddress};
use namada::types::chain::ChainId;
use namada::types::ethereum_events::EthAddress;
use namada::types::ibc::is_ibc_denom;
use namada::types::io::Io;
use namada::types::key::*;
use namada::types::masp::*;
Expand Down Expand Up @@ -367,6 +369,20 @@ impl ArgFromContext for Address {
})
.unwrap_or(Err(Skip))
})
// An IBC token
.or_else(|_| {
is_ibc_denom(raw)
.map(|(trace_path, base_denom)| {
let base_token = ctx
.wallet
.find_address(&base_denom)
.map(|addr| addr.to_string())
.unwrap_or(base_denom);
let ibc_denom = format!("{trace_path}/{base_token}");
ibc_token(ibc_denom)
})
.ok_or(Skip)
})
// Or it can be an alias that may be found in the wallet
.or_else(|_| ctx.wallet.find_address(raw).cloned().ok_or(Skip))
.map_err(|_| format!("Unknown address {raw}"))
Expand Down
Loading