Skip to content

Commit

Permalink
feat(iota)!: remove legacy transfer-iota CLI command (#4701)
Browse files Browse the repository at this point in the history
* feat(iota)!: remove legacy transfer-iota CLI command

* Update docs/content/references/cli/client.mdx

Co-authored-by: Lucas Tortora <[email protected]>

* change example to pay-all-iota so only a single object is needed

* change example to pay-all-iota so only a single object is needed

* use --serialize-signed-transaction

---------

Co-authored-by: Lucas Tortora <[email protected]>
Co-authored-by: Thibault Martinez <[email protected]>
  • Loading branch information
3 people authored Jan 9, 2025
1 parent a742e11 commit e3629c8
Show file tree
Hide file tree
Showing 9 changed files with 42 additions and 185 deletions.
2 changes: 1 addition & 1 deletion crates/iota-transaction-builder/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ impl TransactionBuilder {
}
}
Err(anyhow!(
"Cannot find gas coin for signer address {signer} with amount sufficient for the required gas budget {gas_budget}. If you are using the pay or transfer commands, you can use pay-iota or transfer-iota commands instead, which will use the only object as gas payment."
"Cannot find gas coin for signer address {signer} with amount sufficient for the required gas budget {gas_budget}. If you are using the pay or transfer commands, you can use the pay-iota command instead, which will use the only object as gas payment."
))
}
}
Expand Down
44 changes: 0 additions & 44 deletions crates/iota/src/client_commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -425,27 +425,6 @@ pub enum IotaClientCommands {
#[clap(flatten)]
opts: OptsWithGas,
},
/// Transfer IOTA, and pay gas with the same IOTA coin object.
/// If amount is specified, only the amount is transferred; otherwise the
/// entire object is transferred.
#[clap(name = "transfer-iota")]
TransferIota {
/// Recipient address (or its alias if it's an address in the keystore)
#[clap(long)]
to: KeyIdentity,

/// ID of the coin to transfer. This is also the gas object.
#[clap(long)]
iota_coin_object_id: ObjectID,

/// The amount to transfer, if not specified, the entire coin object
/// will be transferred.
#[clap(long)]
amount: Option<u64>,

#[clap(flatten)]
opts: Opts,
},
/// Upgrade Move modules
#[clap(name = "upgrade")]
Upgrade {
Expand Down Expand Up @@ -1293,29 +1272,6 @@ impl IotaClientCommands {
)
.await?
}
IotaClientCommands::TransferIota {
to,
iota_coin_object_id: object_id,
amount,
opts,
} => {
let signer = context.get_object_owner(&object_id).await?;
let to = get_identity_address(Some(to), context)?;
let client = context.get_client().await?;
let tx_kind = client
.transaction_builder()
.transfer_iota_tx_kind(to, amount);
dry_run_or_execute_or_serialize(
signer,
tx_kind,
context,
None,
None,
Some(object_id),
opts,
)
.await?
}
IotaClientCommands::Pay {
input_coins,
recipients,
Expand Down
141 changes: 18 additions & 123 deletions crates/iota/tests/cli_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2976,10 +2976,10 @@ async fn test_serialize_tx() -> Result<(), anyhow::Error> {
.data;
let coin = object_refs.get(1).unwrap().object().unwrap().object_id;

IotaClientCommands::TransferIota {
to: KeyIdentity::Address(address1),
iota_coin_object_id: coin,
amount: Some(1),
IotaClientCommands::PayIota {
input_coins: vec![coin],
recipients: vec![KeyIdentity::Address(address1)],
amounts: vec![1],
opts: Opts {
gas_budget: Some(rgp * TEST_ONLY_GAS_UNIT_FOR_TRANSFER),
dry_run: false,
Expand All @@ -2991,10 +2991,10 @@ async fn test_serialize_tx() -> Result<(), anyhow::Error> {
.execute(context)
.await?;

IotaClientCommands::TransferIota {
to: KeyIdentity::Address(address1),
iota_coin_object_id: coin,
amount: Some(1),
IotaClientCommands::PayIota {
input_coins: vec![coin],
recipients: vec![KeyIdentity::Address(address1)],
amounts: vec![1],
opts: Opts {
gas_budget: Some(rgp * TEST_ONLY_GAS_UNIT_FOR_TRANSFER),
dry_run: false,
Expand All @@ -3007,10 +3007,10 @@ async fn test_serialize_tx() -> Result<(), anyhow::Error> {
.await?;

// use alias for transfer
IotaClientCommands::TransferIota {
to: KeyIdentity::Alias(alias1),
iota_coin_object_id: coin,
amount: Some(1),
IotaClientCommands::PayIota {
input_coins: vec![coin],
recipients: vec![KeyIdentity::Alias(alias1)],
amounts: vec![1],
opts: Opts {
gas_budget: Some(rgp * TEST_ONLY_GAS_UNIT_FOR_TRANSFER),
dry_run: false,
Expand Down Expand Up @@ -3354,18 +3354,6 @@ async fn test_dry_run() -> Result<(), anyhow::Error> {

assert_dry_run(transfer_dry_run, object_id, "Transfer");

// === TRANSFER IOTA === //
let transfer_iota_dry_run = IotaClientCommands::TransferIota {
to: KeyIdentity::Address(IotaAddress::random_for_testing_only()),
iota_coin_object_id: object_to_send,
amount: Some(1),
opts: Opts::for_testing_dry_run(rgp * TEST_ONLY_GAS_UNIT_FOR_TRANSFER),
}
.execute(context)
.await?;

assert_dry_run(transfer_iota_dry_run, object_to_send, "TransferIota");

// === PAY === //
let pay_dry_run = IotaClientCommands::Pay {
input_coins: vec![object_id],
Expand Down Expand Up @@ -3753,99 +3741,6 @@ async fn test_transfer() -> Result<(), anyhow::Error> {
Ok(())
}

#[sim_test]
async fn test_transfer_iota() -> Result<(), anyhow::Error> {
let (mut test_cluster, client, rgp, objects, recipients, addresses) =
test_cluster_helper().await;
let object_id1 = objects[0];
let recipient1 = &recipients[0];
let address2 = addresses[0];
let context = &mut test_cluster.wallet;
let amount = 1000;
let transfer_iota = IotaClientCommands::TransferIota {
to: KeyIdentity::Address(address2),
iota_coin_object_id: object_id1,
amount: Some(amount),
opts: Opts::for_testing(rgp * TEST_ONLY_GAS_UNIT_FOR_TRANSFER),
}
.execute(context)
.await?;

// transfer iota will transfer the amount from object_id1 to address2, and use
// the same object as gas, and we check if the recipient address received
// the object, and the expected balance is correct
if let IotaClientCommandResult::TransactionBlock(response) = transfer_iota {
assert!(response.status_ok().unwrap());
assert_eq!(
response.effects.as_ref().unwrap().gas_object().object_id(),
object_id1
);
let objs_refs = client
.read_api()
.get_owned_objects(
address2,
Some(IotaObjectResponseQuery::new_with_options(
IotaObjectDataOptions::full_content(),
)),
None,
None,
)
.await?;
assert!(!objs_refs.has_next_page);
assert_eq!(objs_refs.data.len(), 1);
let balance = client
.coin_read_api()
.get_balance(address2, None)
.await?
.total_balance;
assert_eq!(balance, amount as u128);
} else {
panic!("TransferIota test failed");
}
// transfer the whole object by not passing an amount
let transfer_iota = IotaClientCommands::TransferIota {
to: recipient1.clone(),
iota_coin_object_id: object_id1,
amount: None,
opts: Opts::for_testing(rgp * TEST_ONLY_GAS_UNIT_FOR_TRANSFER),
}
.execute(context)
.await?;
if let IotaClientCommandResult::TransactionBlock(response) = transfer_iota {
assert!(response.status_ok().unwrap());
assert_eq!(
response.effects.as_ref().unwrap().gas_object().object_id(),
object_id1
);
let objs_refs = client
.read_api()
.get_owned_objects(
address2,
Some(IotaObjectResponseQuery::new_with_options(
IotaObjectDataOptions::full_content(),
)),
None,
None,
)
.await?;
assert!(!objs_refs.has_next_page);
assert_eq!(
objs_refs.data.len(),
2,
"Expected to have two coins when calling transfer iota the 2nd time"
);
assert!(
objs_refs
.data
.iter()
.any(|x| x.object().unwrap().object_id == object_id1)
);
} else {
panic!("TransferIota test failed");
}
Ok(())
}

#[sim_test]
async fn test_gas_estimation() -> Result<(), anyhow::Error> {
let (mut test_cluster, client, rgp, objects, _, addresses) = test_cluster_helper().await;
Expand All @@ -3859,10 +3754,10 @@ async fn test_gas_estimation() -> Result<(), anyhow::Error> {
let gas_estimate = estimate_gas_budget(context, sender, tx_kind, rgp, None, None).await;
assert!(gas_estimate.is_ok());

let transfer_iota_cmd = IotaClientCommands::TransferIota {
to: KeyIdentity::Address(address2),
iota_coin_object_id: object_id1,
amount: Some(amount),
let pay_iota_cmd = IotaClientCommands::PayIota {
recipients: vec![KeyIdentity::Address(address2)],
input_coins: vec![object_id1],
amounts: vec![amount],
opts: Opts {
gas_budget: None,
dry_run: false,
Expand All @@ -3874,7 +3769,7 @@ async fn test_gas_estimation() -> Result<(), anyhow::Error> {
.execute(context)
.await
.unwrap();
if let IotaClientCommandResult::TransactionBlock(response) = transfer_iota_cmd {
if let IotaClientCommandResult::TransactionBlock(response) = pay_iota_cmd {
assert!(response.status_ok().unwrap());
let gas_used = response.effects.as_ref().unwrap().gas_object().object_id();
assert_eq!(gas_used, object_id1);
Expand All @@ -3888,7 +3783,7 @@ async fn test_gas_estimation() -> Result<(), anyhow::Error> {
<= gas_estimate.unwrap()
);
} else {
panic!("TransferIota test failed");
panic!("PayIota failed in gas estimation test");
}
Ok(())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ The response resembles the following:

## Step 5: Serialize any transaction

This section demonstrates how to use an object that belongs to a multisig address and serialize a transfer to be signed. The `tx_bytes` value can be any serialized transaction data where the sender is the multisig address. Use the `--serialize-unsigned-transaction` flag for supported commands in `iota client -h` (`publish`, `upgrade`, `call`, `transfer`, `transfer-iota`, `pay`, `pay-all-iota`, `pay-iota`, `split`, `merge-coin`) to output the Base64 encoded transaction bytes.
This section demonstrates how to use an object that belongs to a multisig address and serialize a transfer to be signed. The `tx_bytes` value can be any serialized transaction data where the sender is the multisig address. Use the `--serialize-unsigned-transaction` flag for supported commands in `iota client -h` (`publish`, `upgrade`, `call`, `transfer`, `pay`, `pay-all-iota`, `pay-iota`, `split`, `merge-coin`) to output the Base64 encoded transaction bytes.

```shell
iota client transfer --to <IOTA-ADDRESS> --object-id <OBJECT-ID> --serialize-unsigned-transaction
Expand Down Expand Up @@ -177,4 +177,4 @@ Use `iota client` to execute a transaction using multisig:
iota client execute-signed-tx --tx-bytes <TX_BYTES> --signatures <SERIALIZED-MULTISIG>
```

<Quiz questions={questions} />
<Quiz questions={questions} />
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ You must serialize transaction data following [Binary Canonical Serialization](h
The following example demonstrates how to serialize data for a transfer using the [IOTA CLI](../../../references/cli.mdx). This returns serialized transaction data in Base64. Submit the raw transaction to execute as `tx_bytes`.

```shell
iota client transfer-iota --to <IOTA-ADDRESS> --iota-coin-object-id <COIN-OBJECT-ID> --serialize-unsigned-transaction
iota client pay-all-iota --input-coins <COIN-OBJECT-ID> --recipient <IOTA-ADDRESS> --gas-budget 2000000 --serialize-unsigned-transaction
```

The console responds with the resulting `<TX_BYTES>` value.
Expand Down Expand Up @@ -82,15 +82,15 @@ Mutated Objects:
Alternatively, you can use the active key in IOTA Keystore to sign and output a Base64-encoded sender signed data with flag `--serialize-signed-transaction`.
```shell
iota client transfer-iota --to <IOTA-ADDRESS> --iota-coin-object-id <COIN-OBJECT-ID> --serialize-signed-transaction
iota client pay-all-iota --input-coins <COIN-OBJECT-ID> --recipient <IOTA-ADDRESS> --gas-budget 2000000 --serialize-signed-transaction
```
The console responds with the resulting `<SIGNED-TX-BYTES>` value.
After you obtain the signed transaction bytes, you can submit it using the `execute-combined-signed-tx` command. This command takes `--signed-tx-bytes` as the signed transaction bytes to execute (see output of the previous `iota client transfer-iota` command). This executes the signed transaction and returns the certificate and transaction effects if successful.
After you obtain the signed transaction bytes, you can submit it using the `execute-combined-signed-tx` command. This command takes `--signed-tx-bytes` as the signed transaction bytes to execute (see output of the previous `iota client transfer` command). This executes the signed transaction and returns the certificate and transaction effects if successful.
```shell
iota client execute-combined-signed-tx --signed-tx-bytes <SIGNED-TX-BYTES>
```
<Quiz questions={questions} />
<Quiz questions={questions} />
Original file line number Diff line number Diff line change
Expand Up @@ -263,11 +263,21 @@ Create a transfer transaction in CLI. Set the $IOTA_ADDRESS to the one correspon

```shell
iota client gas
iota client transfer-iota --to $IOTA_ADDRESS --iota-coin-object-id $GAS_COIN_ID --serialize-unsigned-transaction
iota client pay-all-iota --input-coins $GAS_COIN_ID --recipient $IOTA_ADDRESS --gas-budget 2000000 --serialize-unsigned-transaction
iota keytool sign --address $IOTA_ADDRESS --data $TX_BYTES
iota client execute-signed-tx --tx-bytes $TX_BYTES --signatures $SERIALIZED_SIGNATURE
```

All combined:

```shell
GAS_COIN_ID=$(iota client gas --json | jq -r '.[0].gasCoinId')
IOTA_ADDRESS=$(iota keytool list --json | jq -r '.[0].iotaAddress')
UNSIGNED_TX_BYTES=$(iota client pay-all-iota --input-coins $GAS_COIN_ID --recipient $IOTA_ADDRESS --gas-budget 2000000 --serialize-unsigned-transaction)
SERIALIZED_SIGNATURE=$(iota keytool sign --address $IOTA_ADDRESS --data $UNSIGNED_TX_BYTES --json | jq -r '.iotaSignature')
iota client execute-signed-tx --tx-bytes $UNSIGNED_TX_BYTES --signatures $SERIALIZED_SIGNATURE
```

</TabItem>
</Tabs>

Expand Down
2 changes: 1 addition & 1 deletion docs/content/references/cli/cheatsheet.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ The cheat sheet highlights common IOTA CLI commands.
<td class="w-1/3">Transfer 0.1 IOTA to an address and use the same coin for gas</td>
</tr>
<tr>
<td class="w-2/3">`iota client transfer-iota \`<br/>&nbsp;&nbsp;`--iota-coin-object-id COIN_ID \`<br/>&nbsp;&nbsp;`--to ADDRESS`</td>
<td class="w-2/3">`iota client pay-all-iota \`<br/>&nbsp;&nbsp;`--input-coins COIN_ID \`<br/>&nbsp;&nbsp;`--recipient ADDRESS \`<br/>&nbsp;&nbsp;`--gas-budget 2000000`</td>
<td class="w-1/3">Transfer IOTA object to an address and use the same coin for gas</td>
</tr>
</tbody>
Expand Down
12 changes: 4 additions & 8 deletions docs/content/references/cli/client.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ Commands:
switch Switch active address and env (e.g. testnet, devnet, localnet, ...)
tx-block Get a transaction block with the effects, events and object changes of its execution
transfer Transfer object
transfer-iota Transfer IOTA, and pay gas with the same IOTA coin object. If amount is specified, only the amount is transferred; otherwise the entire object is transferred
upgrade Upgrade Move modules
verify-bytecode-meter Run the bytecode verifier on the package
verify-source Verify local Move packages against on-chain packages, and optionally their dependencies
Expand Down Expand Up @@ -202,14 +201,13 @@ $ iota client dynamic-field 0x5
### Send IOTA or objects

In this example, let's see how to transfer IOTA or transfer an object from one address to another. First of all, there two main commands for sending IOTA or transferring objects: `pay` and `transfer`.
Both `pay` and `transfer` have a few sister commands: `pay-iota`, `pay-all-iota`, `transfer-iota`.
The `pay` command has two related commands: `pay-iota`, `pay-all-iota`.

The differences between these commands are:
- commands that end in `-iota` deal with Iota's native coin, and they use the input coints to pay for gas and for transferring IOTA or the object.
- `pay-` commands typically deal with coins and handle (coin merging)[../../developer/iota-101/transactions/ptb/optimizing-gas-with-coin-merging.mdx] for you, whereas `transfer` commands can handle the transfer of any object that has public transfer, meaning any object that has the `store` ability.
- `pay` commands allow you to send coins to multiple recipients, whereas `transfer` commands only accept one recipient.
- `pay-all-iota` is a special case of `pay-iota` that offers a way to transfer the entire balance after smashing.
- `transfer-iota` is a legacy command and has been entirely superseded by `pay-iota` or `pay-all-iota` depending on whether an amount is specified or not.

Assume you have two addresses:

Expand Down Expand Up @@ -237,7 +235,7 @@ iota client gas hungry-spodumene
╰────────────────────────────────────────────────────────────────────┴────────────────────┴──────────────────╯
```
You want to send 0.5 IOTA to `eloquent-amber`. Given that you have a few gas coins, you can use `pay`. If only one gas coin exists, then you need to use `transfer-iota` or `pay-iota`, or you would need to split the coin first to have another coin to use for paying gas. In this case, let's use the `pay-iota` command as you do not need to provide a separate gas coin to be used for the gas fees. In the command below, you set the recipient to be `eloquent-amber`, which coin to use to transfer IOTA from, and the amount of IOTA to transfer.
You want to send 0.5 IOTA to `eloquent-amber`. Given that you have a few gas coins, you can use `pay`. If only one gas coin exists, then you need to use `pay-iota`, or you would need to split the coin first to have another coin to use for paying gas. In this case, let's use the `pay-iota` command as you do not need to provide a separate gas coin to be used for the gas fees. In the command below, you set the recipient to be `eloquent-amber`, which coin to use to transfer IOTA from, and the amount of IOTA to transfer.
```
iota client pay-iota --recipients eloquent-amber --input-coins 0xc9b447fff18f13fa035e028534b8344d5fc8a8760248fad10155e78f44dc3a52 --amounts 500000000
Expand Down Expand Up @@ -391,11 +389,9 @@ iota client gas eloquent-amber
╰────────────────────────────────────────────────────────────────────┴────────────────────┴──────────────────╯
```

If you want to transfer the whole object, you can use `iota client pay-all-iota` or `iota client transfer-iota` (without passing the amount):
If you want to transfer the whole object, you can use `iota client pay-all-iota` (without passing the amount):
```
iota client pay-iota --recipient eloquent-amber --input-coins 0xc9b447fff18f13fa035e028534b8344d5fc8a8760248fad10155e78f44dc3a52
or
iota client transfer-iota --to eloquent-amber --iota-coin-object-id 0xc9b447fff18f13fa035e028534b8344d5fc8a8760248fad10155e78f44dc3a52
iota client pay-all-iota --recipient eloquent-amber --input-coins 0xc9b447fff18f13fa035e028534b8344d5fc8a8760248fad10155e78f44dc3a52 --gas-budget 2000000
```

Then check the gas for `eloquent-amber` again:
Expand Down
Loading

0 comments on commit e3629c8

Please sign in to comment.