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

[draft] Implement Osmosis shielded swaps #4133

Open
wants to merge 47 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
e60dbb7
Add PFM related deps
sug0 Nov 29, 2024
ab95f14
Increase the visibility of some IBC code
sug0 Nov 29, 2024
4ac2959
Implement PFM module
sug0 Nov 29, 2024
f6c4c6a
Hook up PFM middleware to ICS-20 transfer module
sug0 Nov 29, 2024
627fc40
Add PFM e2e tests
sug0 Nov 29, 2024
c5072aa
Enable PFM e2e tests in CI
sug0 Nov 29, 2024
f04bd8e
Changelog for #4082
sug0 Nov 29, 2024
fec9214
Remove receiver validation on pfm ibc packets
batconjurer Dec 2, 2024
b016639
Add function to build transfer middlewares stack
sug0 Dec 2, 2024
ef1ab03
Move PFM to middlewares
sug0 Dec 2, 2024
9226766
Move module wrapper impl
sug0 Dec 2, 2024
3ebec98
Handle receiving PFM packets with invalid receivers
sug0 Dec 2, 2024
ea057c5
Changelog for #4134
sug0 Dec 2, 2024
2ddabb7
Add osmosis swap CLI command
sug0 Oct 11, 2024
e8c86e1
CLI improvements for osmosis swaps
batconjurer Dec 5, 2024
98e4e94
Further clean up CLI code
sug0 Dec 6, 2024
804da76
Interpret IBC memo data included in JSON objects
sug0 Dec 6, 2024
b08e49c
Improve docs and runtime checks of osmosis swaps
sug0 Dec 6, 2024
d844161
Switch SQS RPC server
sug0 Dec 6, 2024
24439d0
Remove shielding transfer memo variant
sug0 Dec 7, 2024
57cc8f4
Fixed next_memo type in swap cli command. Should be json, not string
batconjurer Dec 7, 2024
6683dd3
Added shielded receive middleware implementation
batconjurer Dec 10, 2024
64b8fe3
Move IBC imports
sug0 Dec 10, 2024
7a4d0de
Create shielded recv packet meta
sug0 Dec 10, 2024
2b18d7c
Add shielded recv to stack of middlewares
sug0 Dec 10, 2024
59555f3
Reduce PFM boilerplate
sug0 Dec 10, 2024
547f9f3
Check that receiver must be MASP in shielded recv middleware
sug0 Dec 10, 2024
50bdb6a
Fixed cli slippage inputs
batconjurer Dec 10, 2024
77ed73e
Update ORM
sug0 Dec 10, 2024
aa94225
Add osmosis swap IBC memo variant
sug0 Dec 10, 2024
b3dfeb3
Encode shielding transfer in shielded recv memo
sug0 Dec 10, 2024
d03ebaf
Added overflow addr to cli. Added cli commands to parse namadad targe…
batconjurer Dec 10, 2024
e8e40a8
Run make fmt
sug0 Dec 10, 2024
48f33dc
Use MASP addr instead of payment addr
sug0 Dec 10, 2024
2c296a8
WIP: Fully shielded swap
sug0 Dec 10, 2024
46940f5
WIP
batconjurer Dec 11, 2024
77fcba1
Run make fmt
sug0 Dec 11, 2024
5f08cca
Fix shielded recv message format
sug0 Dec 11, 2024
8f0745b
Add `ibc_shielded_recv_middleware_happy_flow` e2e test
sug0 Dec 11, 2024
ded5912
Add marker trait to denote valid namada ibc memos
sug0 Dec 11, 2024
8ec933d
Added an unhappy flow e2e test for the ibc shielded-recv middleware. …
batconjurer Dec 12, 2024
bf165a1
Refactor IBC memo fns in e2e tests
sug0 Dec 18, 2024
079530b
Refactor e2e test cosmos bind ports
sug0 Dec 18, 2024
0687063
Allow setting up Osmosis chains in e2e tests
sug0 Dec 18, 2024
09ad020
Add Osmosis contract bytecode fixtures
sug0 Dec 20, 2024
a4bdd73
WIP: Add Osmosis shielded swaps e2e test
sug0 Dec 20, 2024
e898c4d
WIP: Current e2e stub not passing
batconjurer Dec 30, 2024
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
3 changes: 3 additions & 0 deletions .changelog/unreleased/features/4082-ibc-pfm.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- Implement compatibility with Strangelove's Packet Forward Middleware
in Namada, to allow forwarding ICS-20 packets over multiple chains.
([\#4082](https://github.com/anoma/namada/pull/4082))
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Disable validation of IBC ICS-20 receivers, while handling PFM packets.
([\#4134](https://github.com/anoma/namada/pull/4134))
4 changes: 4 additions & 0 deletions .github/workflows/scripts/e2e.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
"e2e::ibc_tests::fee_payment_with_ibc_token": 357,
"e2e::ibc_tests::ibc_token_inflation": 840,
"e2e::ibc_tests::ibc_rate_limit": 485,
"e2e::ibc_tests::ibc_pfm_happy_flows": 485,
"e2e::ibc_tests::ibc_pfm_unhappy_flows": 485,
"e2e::ibc_tests::ibc_upgrade_client": 280,
"e2e::ibc_tests::ibc_shielded_recv_middleware_happy_flow": 280,
"e2e::ibc_tests::ibc_shielded_recv_middleware_unhappy_flow": 280,
"e2e::eth_bridge_tests::test_add_to_bridge_pool": 10,
"e2e::ledger_tests::double_signing_gets_slashed": 12,
"e2e::ledger_tests::ledger_many_txs_in_a_block": 55,
Expand Down
202 changes: 164 additions & 38 deletions Cargo.lock

Large diffs are not rendered by default.

15 changes: 10 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ derivation-path = "0.2.0"
derivative = "2.2.0"
directories = "4.0.1"
drain_filter_polyfill = "0.1.3"
dur = "0.5.3"
duration-str = "0.10.0"
ed25519-consensus = "2.1.0"
either = "1.12.0"
Expand All @@ -117,10 +118,14 @@ flume = "0.11.1"
fs_extra = "1.2.0"
futures = "0.3"
git2 = { version = "0.18.1", default-features = false }
# branch yuji/derive-arbitrary
ibc = { git = "https://github.com/heliaxdev/cosmos-ibc-rs", rev = "38bd2a32f35117d4d9165a3c68c64ccd87ad56dd", features = ["serde"] }
ibc-derive = { git = "https://github.com/heliaxdev/cosmos-ibc-rs", rev = "38bd2a32f35117d4d9165a3c68c64ccd87ad56dd" }
ibc-testkit = { git = "https://github.com/heliaxdev/cosmos-ibc-rs", rev = "38bd2a32f35117d4d9165a3c68c64ccd87ad56dd", default-features = false }
# branch tiago/optional-ack
ibc = { git = "https://github.com/heliaxdev/cosmos-ibc-rs", rev = "38489943c4e75206eaffeeeec6153c039c2499d1", features = ["serde"] }
ibc-derive = { git = "https://github.com/heliaxdev/cosmos-ibc-rs", rev = "38489943c4e75206eaffeeeec6153c039c2499d1" }
ibc-middleware-module = { git = "https://github.com/heliaxdev/ibc-middleware", tag = "module/v0.1.0" }
ibc-middleware-module-macros = { git = "https://github.com/heliaxdev/ibc-middleware", tag = "module-macros/v0.1.0" }
ibc-middleware-overflow-receive = { git = "https://github.com/heliaxdev/ibc-middleware", tag = "orm/v0.4.0" }
ibc-middleware-packet-forward = { git = "https://github.com/heliaxdev/ibc-middleware", tag = "pfm/v0.9.0", features = ["borsh"] }
ibc-testkit = { git = "https://github.com/heliaxdev/cosmos-ibc-rs", rev = "38489943c4e75206eaffeeeec6153c039c2499d1", default-features = false }
ics23 = "0.12.0"
usize-set = { version = "0.10.3", features = ["serialize-borsh", "serialize-serde"] }
indexmap = { git = "https://github.com/heliaxdev/indexmap", tag = "2.2.4-heliax-1", features = ["borsh-schema", "serde"] }
Expand Down Expand Up @@ -173,7 +178,7 @@ rpassword = "5.0.1"
rustversion = "1.0"
serde = {version = "1.0.125", features = ["derive"]}
serde_bytes = "0.11.5"
serde_json = "1.0.62"
serde_json = "1.0.133"
serde_tuple = "0.5.0"
sha2 = "0.9.3"
sha2-const = "0.1.2"
Expand Down
1 change: 1 addition & 0 deletions crates/apps/src/bin/namada/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ fn handle_command(cmd: cli::cmds::Namada, raw_sub_cmd: String) -> Result<()> {
| cli::cmds::Namada::TxShieldingTransfer(_)
| cli::cmds::Namada::TxUnshieldingTransfer(_)
| cli::cmds::Namada::TxIbcTransfer(_)
| cli::cmds::Namada::TxOsmosisSwap(_)
| cli::cmds::Namada::TxUpdateAccount(_)
| cli::cmds::Namada::TxRevealPk(_)
| cli::cmds::Namada::TxInitProposal(_)
Expand Down
199 changes: 199 additions & 0 deletions crates/apps_lib/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ pub mod cmds {
TxShieldingTransfer(TxShieldingTransfer),
TxUnshieldingTransfer(TxUnshieldingTransfer),
TxIbcTransfer(TxIbcTransfer),
TxOsmosisSwap(TxOsmosisSwap),
TxUpdateAccount(TxUpdateAccount),
TxInitProposal(TxInitProposal),
TxVoteProposal(TxVoteProposal),
Expand All @@ -84,6 +85,7 @@ pub mod cmds {
.subcommand(TxShieldingTransfer::def().display_order(2))
.subcommand(TxUnshieldingTransfer::def().display_order(2))
.subcommand(TxIbcTransfer::def().display_order(2))
.subcommand(TxOsmosisSwap::def().display_order(2))
.subcommand(TxUpdateAccount::def().display_order(2))
.subcommand(TxInitProposal::def().display_order(2))
.subcommand(TxVoteProposal::def().display_order(2))
Expand All @@ -107,6 +109,8 @@ pub mod cmds {
SubCmd::parse(matches).map(Self::TxUnshieldingTransfer);
let tx_ibc_transfer =
SubCmd::parse(matches).map(Self::TxIbcTransfer);
let tx_osmosis_swap =
SubCmd::parse(matches).map(Self::TxOsmosisSwap);
let tx_update_account =
SubCmd::parse(matches).map(Self::TxUpdateAccount);
let tx_init_proposal =
Expand All @@ -124,6 +128,7 @@ pub mod cmds {
.or(tx_shielding_transfer)
.or(tx_unshielding_transfer)
.or(tx_ibc_transfer)
.or(tx_osmosis_swap)
.or(tx_update_account)
.or(tx_init_proposal)
.or(tx_vote_proposal)
Expand Down Expand Up @@ -239,6 +244,7 @@ pub mod cmds {
.subcommand(TxShieldingTransfer::def().display_order(1))
.subcommand(TxUnshieldingTransfer::def().display_order(1))
.subcommand(TxIbcTransfer::def().display_order(1))
.subcommand(TxOsmosisSwap::def().display_order(1))
.subcommand(TxUpdateAccount::def().display_order(1))
.subcommand(TxInitAccount::def().display_order(1))
.subcommand(TxRevealPk::def().display_order(1))
Expand Down Expand Up @@ -313,6 +319,7 @@ pub mod cmds {
let tx_unshielding_transfer =
Self::parse_with_ctx(matches, TxUnshieldingTransfer);
let tx_ibc_transfer = Self::parse_with_ctx(matches, TxIbcTransfer);
let tx_osmosis_swap = Self::parse_with_ctx(matches, TxOsmosisSwap);
let tx_update_account =
Self::parse_with_ctx(matches, TxUpdateAccount);
let tx_init_account = Self::parse_with_ctx(matches, TxInitAccount);
Expand Down Expand Up @@ -401,6 +408,7 @@ pub mod cmds {
.or(tx_shielding_transfer)
.or(tx_unshielding_transfer)
.or(tx_ibc_transfer)
.or(tx_osmosis_swap)
.or(tx_update_account)
.or(tx_init_account)
.or(tx_reveal_pk)
Expand Down Expand Up @@ -495,6 +503,7 @@ pub mod cmds {
TxShieldingTransfer(TxShieldingTransfer),
TxUnshieldingTransfer(TxUnshieldingTransfer),
TxIbcTransfer(TxIbcTransfer),
TxOsmosisSwap(TxOsmosisSwap),
QueryResult(QueryResult),
TxUpdateAccount(TxUpdateAccount),
TxInitAccount(TxInitAccount),
Expand Down Expand Up @@ -1433,6 +1442,25 @@ pub mod cmds {
}
}

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

impl SubCmd for TxOsmosisSwap {
const CMD: &'static str = "osmosis-swap";

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

fn def() -> App {
App::new(Self::CMD)
.about(wrap!("Swap two asset kinds using Osmosis."))
.add_args::<args::TxOsmosisSwap<args::CliTypes>>()
}
}

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

Expand Down Expand Up @@ -3399,6 +3427,7 @@ pub mod args {
use crate::wrap;

pub const ADDRESS: Arg<WalletAddress> = arg("address");
pub const ADDRESS_OPT: ArgOpt<WalletAddress> = arg_opt("address");
pub const ADD_PERSISTENT_PEERS: ArgFlag = flag("add-persistent-peers");
pub const ALIAS_OPT: ArgOpt<String> = ALIAS.opt();
pub const ALIAS: Arg<String> = arg("alias");
Expand Down Expand Up @@ -3551,6 +3580,7 @@ pub mod args {
pub const LIST_FIND_ADDRESSES_ONLY: ArgFlag = flag("addr");
pub const LIST_FIND_KEYS_ONLY: ArgFlag = flag("keys");
pub const LOCALHOST: ArgFlag = flag("localhost");
pub const LOCAL_RECOVERY_ADDR: Arg<String> = arg("local-recovery-addr");
pub const MASP_EPOCH: ArgOpt<MaspEpoch> = arg_opt("masp-epoch");
pub const MAX_COMMISSION_RATE_CHANGE: Arg<Dec> =
arg("max-commission-rate-change");
Expand All @@ -3559,6 +3589,8 @@ pub mod args {
pub const MAX_ETH_GAS: ArgOpt<u64> = arg_opt("max_eth-gas");
pub const MEMO_OPT: ArgOpt<String> = arg_opt("memo");
pub const MIGRATION_PATH: ArgOpt<PathBuf> = arg_opt("migration-path");
pub const MINIMUM_AMOUNT: ArgOpt<token::DenominatedAmount> =
arg_opt("minimum-amount");
pub const MODE: ArgOpt<String> = arg_opt("mode");
pub const NET_ADDRESS: Arg<SocketAddr> = arg("net-address");
pub const NAMADA_START_TIME: ArgOpt<DateTimeUtc> = arg_opt("time");
Expand All @@ -3567,13 +3599,19 @@ pub mod args {
pub const NUT: ArgFlag = flag("nut");
pub const OUT_FILE_PATH_OPT: ArgOpt<PathBuf> = arg_opt("out-file-path");
pub const OUTPUT: ArgOpt<PathBuf> = arg_opt("output");
pub const OUTPUT_DENOM: Arg<String> = arg("output-denom");
pub const OUTPUT_FOLDER_PATH: ArgOpt<PathBuf> =
arg_opt("output-folder-path");
pub const OSMOSIS_POOL_HOP: ArgMulti<OsmosisPoolHop, GlobStar> =
arg_multi("pool-hop");
pub const OVERFLOW_OPT: ArgOpt<WalletAddress> = arg_opt("overflow-addr");
pub const OWNER: Arg<WalletAddress> = arg("owner");
pub const OWNER_OPT: ArgOpt<WalletAddress> = OWNER.opt();
pub const PATH: Arg<PathBuf> = arg("path");
pub const PATH_OPT: ArgOpt<PathBuf> = arg_opt("path");
pub const PAYMENT_ADDRESS_TARGET: Arg<WalletPaymentAddr> = arg("target");
pub const PAYMENT_ADDRESS_TARGET_OPT: ArgOpt<WalletPaymentAddr> =
arg_opt("target-pa");
pub const PORT_ID: ArgDefault<PortId> = arg_default(
"port-id",
DefaultFn(|| PortId::from_str("transfer").unwrap()),
Expand Down Expand Up @@ -3621,6 +3659,7 @@ pub mod args {
pub const SHIELDED: ArgFlag = flag("shielded");
pub const SHOW_IBC_TOKENS: ArgFlag = flag("show-ibc-tokens");
pub const SIGNER: ArgOpt<WalletAddress> = arg_opt("signer");
pub const SLIPPAGE: ArgOpt<u64> = arg_opt("slippage-percentage");
pub const SIGNING_KEYS: ArgMulti<WalletPublicKey, GlobStar> =
arg_multi("signing-keys");
pub const SIGNATURES: ArgMulti<PathBuf, GlobStar> = arg_multi("signatures");
Expand All @@ -3634,6 +3673,7 @@ pub mod args {
pub const STORAGE_KEY: Arg<storage::Key> = arg("storage-key");
pub const SUSPEND_ACTION: ArgFlag = flag("suspend");
pub const TARGET: Arg<WalletAddress> = arg("target");
pub const TARGET_OPT: ArgOpt<WalletAddress> = arg_opt("target");
pub const TEMPLATES_PATH: Arg<PathBuf> = arg("templates-path");
pub const TIMEOUT_HEIGHT: ArgOpt<u64> = arg_opt("timeout-height");
pub const TIMEOUT_SEC_OFFSET: ArgOpt<u64> = arg_opt("timeout-sec-offset");
Expand Down Expand Up @@ -3677,6 +3717,7 @@ pub mod args {
pub const WASM_CHECKSUMS_PATH: Arg<PathBuf> = arg("wasm-checksums-path");
pub const WASM_DIR: ArgOpt<PathBuf> = arg_opt("wasm-dir");
pub const WEBSITE_OPT: ArgOpt<String> = arg_opt("website");
pub const WINDOW_SECONDS: ArgOpt<u64> = arg_opt("window-seconds");
pub const WITH_INDEXER: ArgOpt<String> = arg_opt("with-indexer");
pub const WRAPPER_SIGNATURE_OPT: ArgOpt<PathBuf> = arg_opt("gas-signature");
pub const TX_PATH: Arg<PathBuf> = arg("tx-path");
Expand Down Expand Up @@ -5106,6 +5147,164 @@ pub mod args {
}
}

impl CliToSdk<TxOsmosisSwap<SdkTypes>> for TxOsmosisSwap<CliTypes> {
type Error = std::io::Error;

fn to_sdk(
self,
ctx: &mut Context,
) -> Result<TxOsmosisSwap<SdkTypes>, Self::Error> {
let chain_ctx = ctx.borrow_mut_chain_or_exit();
let recipient = match self.recipient {
Either::Left(r) => Either::Left(chain_ctx.get(&r)),
Either::Right(r) => Either::Right(chain_ctx.get(&r)),
};
let overflow = self.overflow.map(|r| chain_ctx.get(&r));
Ok(TxOsmosisSwap {
transfer: self.transfer.to_sdk(ctx)?,
output_denom: self.output_denom,
recipient,
overflow,
slippage: self.slippage,
local_recovery_addr: self.local_recovery_addr,
route: self.route,
})
}
}

impl Args for TxOsmosisSwap<CliTypes> {
fn parse(matches: &ArgMatches) -> Self {
let transfer = TxIbcTransfer::parse(matches);
let output_denom = OUTPUT_DENOM.parse(matches);
let maybe_traans_recipient = TARGET_OPT.parse(matches);
let maybe_shielded_recipient =
PAYMENT_ADDRESS_TARGET_OPT.parse(matches);
let maybe_overflow = OVERFLOW_OPT.parse(matches);
let slippage_percent = SLIPPAGE.parse(matches);
if let Some(percent) = slippage_percent {
if percent > 100 {
panic!(
"The slippage percent must be an integer between 0 \
and 100."
)
}
}
let window_seconds = WINDOW_SECONDS.parse(matches);
let minimum_amount = MINIMUM_AMOUNT.parse(matches);
let slippage = minimum_amount
.map(|d| Slippage::MinOutputAmount(d.redenominate(0).amount()))
.or_else(|| {
Some(Slippage::Twap {
slippage_percentage: slippage_percent
.expect(
"If a minimum amount was not provided, \
slippage-percent and window-seconds must be \
specified.",
)
.to_string(),
window_seconds: window_seconds.expect(
"If a minimum amount was not provided, \
slippage-percent and window-seconds must be \
specified.",
),
})
})
.unwrap();
let local_recovery_addr = LOCAL_RECOVERY_ADDR.parse(matches);
let route = match OSMOSIS_POOL_HOP.parse(matches) {
r if r.is_empty() => None,
r => Some(r),
};
Self {
transfer,
output_denom,
recipient: if let Some(target) = maybe_traans_recipient {
Either::Left(target)
} else {
Either::Right(maybe_shielded_recipient.unwrap())
},
overflow: maybe_overflow,
slippage,
local_recovery_addr,
route,
}
}

fn def(app: App) -> App {
app.add_args::<TxIbcTransfer<CliTypes>>()
.arg(OSMOSIS_POOL_HOP.def().help(wrap!(
"Individual hop of the route to take through Osmosis \
pools. This value takes the form \
<osmosis-pool-id>:<pool-output-denom>. When unspecified, \
the optimal route is queried on the fly."
)))
.arg(OUTPUT_DENOM.def().help(wrap!(
"The IBC denomination (on Osmosis) of the desired asset."
)))
.arg(TARGET_OPT.def().help(wrap!(
"The transparent Namada address that shall receive the \
swapped tokens."
)))
.arg(
PAYMENT_ADDRESS_TARGET_OPT
.def()
.requires(OVERFLOW_OPT.name)
.help(wrap!(
"The shielded Namada address that shall receive \
the minimum amount of swapped tokens."
)),
)
.arg(OVERFLOW_OPT.def().help(wrap!(
"The transparent address that receives the amount of \
target asset exceeding the minimum amount. If used in \
conjunction with shielding, this address should not be \
linkable to any of your transparent accounts."
)))
.arg(SLIPPAGE.def().requires(WINDOW_SECONDS.name).help(wrap!(
"The slippage percentage as an integer between 0 and 100. \
Represents the maximum acceptable deviation from the \
expected price during a trade."
)))
.arg(WINDOW_SECONDS.def().requires(SLIPPAGE.name).help(wrap!(
"The time period (in seconds) over which the average \
price is calculated."
)))
.arg(
MINIMUM_AMOUNT
.def()
.conflicts_with(SLIPPAGE.name)
.conflicts_with(WINDOW_SECONDS.name)
.help(wrap!(
"The minimum amount of target asset that the \
trade should produce."
)),
)
.arg(LOCAL_RECOVERY_ADDR.def().help(wrap!(
"An address on Osmosis from which to recover funds in \
case of failure."
)))
.group(
ArgGroup::new("slippage")
.args([SLIPPAGE.name, MINIMUM_AMOUNT.name])
.required(true),
)
.group(
ArgGroup::new("transfer-target")
.args([
TARGET_OPT.name,
PAYMENT_ADDRESS_TARGET_OPT.name,
])
.required(true),
)
.mut_arg(RECEIVER.name, |arg| {
arg.long("swap-contract").help(wrap!(
"The address of the Osmosis contract performing the \
swap. It will be the receiver of the IBC transfer."
))
})
}
}

impl CliToSdk<TxInitAccount<SdkTypes>> for TxInitAccount<CliTypes> {
type Error = std::io::Error;

Expand Down
Loading
Loading