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

[wallet] Add merge/split coins command to the wallet #657

Merged
merged 3 commits into from
Mar 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
280 changes: 253 additions & 27 deletions doc/src/wallet.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion sui/src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ async fn main() -> Result<(), anyhow::Error> {
println!();

let mut shell = Shell {
prompt: "sui>-$ ",
prompt: "sui>-$ ".bold().green(),
state: context,
handler: ClientCommandHandler,
command: CommandStructure::from_clap(&install_shell_plugins(app)),
Expand Down
99 changes: 94 additions & 5 deletions sui/src/wallet_commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use std::sync::{Arc, RwLock};
use std::time::Instant;
use structopt::clap::AppSettings;
use structopt::StructOpt;
use sui_core::client::client_responses::{MergeCoinResponse, SplitCoinResponse};
use sui_types::crypto::{Signable, Signature};
use sui_types::object::ObjectRead;
use tracing::info;
Expand All @@ -41,7 +42,7 @@ use tracing::info;
pub struct WalletOpts {
#[structopt(subcommand)]
pub command: WalletCommands,
/// Return command outputs in json format.
/// Returns command outputs in JSON format.
#[structopt(long, global = true)]
pub json: bool,
}
Expand Down Expand Up @@ -146,6 +147,38 @@ pub enum WalletCommands {
#[structopt(long, parse(try_from_str = decode_bytes_hex))]
address: SuiAddress,
},

/// Split a coin object into multiple coins.
SplitCoin {
/// Coin to Split, in 20 bytes Hex string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optionally, lowercase hex throughout:
https://en.wikipedia.org/wiki/Hexadecimal

Totally not a big deal though.

#[structopt(long)]
coin_id: ObjectID,
/// Amount to split out from the coin
#[structopt(long)]
amounts: Vec<u64>,
/// ID of the gas object for gas payment, in 20 bytes Hex string
#[structopt(long)]
gas: ObjectID,
/// Gas budget for this call
#[structopt(long)]
gas_budget: u64,
},

/// Merge two coin objects into one coin
MergeCoin {
/// Coin to merge into, in 20 bytes Hex string
#[structopt(long)]
primary_coin: ObjectID,
/// Coin to be merged, in 20 bytes Hex string
#[structopt(long)]
coin_to_merge: ObjectID,
/// ID of the gas object for gas payment, in 20 bytes Hex string
#[structopt(long)]
gas: ObjectID,
/// Gas budget for this call
#[structopt(long)]
gas_budget: u64,
},
}

struct SimpleTransactionSigner {
Expand All @@ -171,7 +204,7 @@ impl WalletCommands {
&mut self,
context: &mut WalletContext,
) -> Result<WalletCommandResult, anyhow::Error> {
let signature_callback = Box::pin(SimpleTransactionSigner {
let tx_signer = Box::pin(SimpleTransactionSigner {
keystore: context.keystore.clone(),
});

Expand Down Expand Up @@ -202,7 +235,7 @@ impl WalletCommands {
compiled_modules,
gas_obj_ref,
*gas_budget,
signature_callback,
tx_signer,
)
.await?;

Expand Down Expand Up @@ -296,7 +329,7 @@ impl WalletCommands {
vec![],
pure_args,
*gas_budget,
signature_callback,
tx_signer,
)
.await?;
if matches!(effects.status, ExecutionStatus::Failure { .. }) {
Expand All @@ -315,7 +348,7 @@ impl WalletCommands {

let (cert, effects) = context
.address_manager
.transfer_coin(*from, *object_id, *gas, *to, signature_callback)
.transfer_coin(*from, *object_id, *gas, *to, tx_signer)
.await?;
let time_total = time_start.elapsed().as_micros();

Expand Down Expand Up @@ -370,6 +403,54 @@ impl WalletCommands {
}
WalletCommandResult::Gas(coins)
}
WalletCommands::SplitCoin {
coin_id,
amounts,
gas,
gas_budget,
} => {
let signer = &context
.address_manager
.get_object_owner(*gas)
.await?
.get_single_owner_address()?;
let response = context
.address_manager
.split_coin(
*signer,
*coin_id,
amounts.clone(),
*gas,
*gas_budget,
tx_signer,
)
.await?;
WalletCommandResult::SplitCoin(response)
}
WalletCommands::MergeCoin {
primary_coin,
coin_to_merge,
gas,
gas_budget,
} => {
let signer = &context
.address_manager
.get_object_owner(*gas)
.await?
.get_single_owner_address()?;
let response = context
.address_manager
.merge_coins(
*signer,
*primary_coin,
*coin_to_merge,
*gas,
*gas_budget,
tx_signer,
)
.await?;
WalletCommandResult::MergeCoin(response)
}
})
}
}
Expand Down Expand Up @@ -460,6 +541,12 @@ impl Display for WalletCommandResult {
)?;
}
}
WalletCommandResult::SplitCoin(response) => {
write!(writer, "{}", response)?;
}
WalletCommandResult::MergeCoin(response) => {
write!(writer, "{}", response)?;
}
}
write!(f, "{}", writer)
}
Expand Down Expand Up @@ -525,4 +612,6 @@ pub enum WalletCommandResult {
SyncClientState,
NewAddress(SuiAddress),
Gas(Vec<GasCoin>),
SplitCoin(SplitCoinResponse),
MergeCoin(MergeCoinResponse),
}
28 changes: 18 additions & 10 deletions sui_core/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,9 +216,9 @@ pub trait Client {
async fn split_coin(
&mut self,
signer: SuiAddress,
coin_object_ref: ObjectRef,
coin_object_id: ObjectID,
split_amounts: Vec<u64>,
gas_payment: ObjectRef,
gas_payment: ObjectID,
gas_budget: u64,
tx_signer: StableSyncTransactionSigner,
) -> Result<SplitCoinResponse, anyhow::Error>;
Expand All @@ -234,9 +234,9 @@ pub trait Client {
async fn merge_coins(
&mut self,
signer: SuiAddress,
primary_coin: ObjectRef,
coin_to_merge: ObjectRef,
gas_payment: ObjectRef,
primary_coin: ObjectID,
coin_to_merge: ObjectID,
gas_payment: ObjectID,
gas_budget: u64,
tx_signer: StableSyncTransactionSigner,
) -> Result<MergeCoinResponse, anyhow::Error>;
Expand Down Expand Up @@ -758,12 +758,15 @@ where
async fn split_coin(
&mut self,
signer: SuiAddress,
coin_object_ref: ObjectRef,
coin_object_id: ObjectID,
split_amounts: Vec<u64>,
gas_payment: ObjectRef,
gas_payment: ObjectID,
gas_budget: u64,
tx_signer: StableSyncTransactionSigner,
) -> Result<SplitCoinResponse, anyhow::Error> {
let account = self.get_account(&signer)?;
let coin_object_ref = account.latest_object_ref(&coin_object_id)?;
let gas_payment = account.latest_object_ref(&gas_payment)?;
let coin_type = self
.get_object_info(coin_object_ref.0)
.await?
Expand Down Expand Up @@ -818,12 +821,17 @@ where
async fn merge_coins(
&mut self,
signer: SuiAddress,
primary_coin: ObjectRef,
coin_to_merge: ObjectRef,
gas_payment: ObjectRef,
primary_coin: ObjectID,
coin_to_merge: ObjectID,
gas_payment: ObjectID,
gas_budget: u64,
tx_signer: StableSyncTransactionSigner,
) -> Result<MergeCoinResponse, anyhow::Error> {
let account = self.get_account(&signer)?;
let primary_coin = account.latest_object_ref(&primary_coin)?;
let coin_to_merge = account.latest_object_ref(&coin_to_merge)?;
let gas_payment = account.latest_object_ref(&gas_payment)?;

let coin_type = self
.get_object_info(primary_coin.0)
.await?
Expand Down
48 changes: 48 additions & 0 deletions sui_core/src/client/client_responses.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use serde::ser::Error;
use serde::Serialize;
use std::fmt;
use std::fmt::Write;
use std::fmt::{Display, Formatter};
use sui_types::gas_coin::GasCoin;
use sui_types::messages::CertifiedTransaction;
use sui_types::object::Object;

#[derive(Serialize)]
pub struct SplitCoinResponse {
/// Certificate of the transaction
pub certificate: CertifiedTransaction,
Expand All @@ -15,6 +22,32 @@ pub struct SplitCoinResponse {
pub updated_gas: Object,
}

impl Display for SplitCoinResponse {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut writer = String::new();
writeln!(writer, "----- Certificate ----")?;
write!(writer, "{}", self.certificate)?;
writeln!(writer, "----- Split Coin Results ----")?;

let coin = GasCoin::try_from(&self.updated_coin).map_err(fmt::Error::custom)?;
writeln!(writer, "Updated Coin : {}", coin)?;
let mut new_coin_text = Vec::new();
for coin in &self.new_coins {
let coin = GasCoin::try_from(coin).map_err(fmt::Error::custom)?;
new_coin_text.push(format!("{}", coin))
}
writeln!(
writer,
"New Coins : {}",
new_coin_text.join(",\n ")
)?;
let gas_coin = GasCoin::try_from(&self.updated_gas).map_err(fmt::Error::custom)?;
writeln!(writer, "Updated Gas : {}", gas_coin)?;
write!(f, "{}", writer)
}
}

#[derive(Serialize)]
pub struct MergeCoinResponse {
/// Certificate of the transaction
pub certificate: CertifiedTransaction,
Expand All @@ -23,3 +56,18 @@ pub struct MergeCoinResponse {
/// The updated gas payment object after deducting payment
pub updated_gas: Object,
}

impl Display for MergeCoinResponse {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut writer = String::new();
writeln!(writer, "----- Certificate ----")?;
write!(writer, "{}", self.certificate)?;
writeln!(writer, "----- Merge Coin Results ----")?;

let coin = GasCoin::try_from(&self.updated_coin).map_err(fmt::Error::custom)?;
writeln!(writer, "Updated Coin : {}", coin)?;
let gas_coin = GasCoin::try_from(&self.updated_gas).map_err(fmt::Error::custom)?;
writeln!(writer, "Updated Gas : {}", gas_coin)?;
write!(f, "{}", writer)
}
}
10 changes: 5 additions & 5 deletions sui_core/src/unit_tests/client_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2516,9 +2516,9 @@ async fn test_coin_split() {
let response = client
.split_coin(
addr1,
coin_object.to_object_reference(),
coin_object.id(),
split_amounts.clone(),
gas_object.to_object_reference(),
gas_object.id(),
GAS_VALUE_FOR_TESTING,
signature_callback(&key1),
)
Expand Down Expand Up @@ -2573,9 +2573,9 @@ async fn test_coin_merge() {
let response = client
.merge_coins(
addr1,
coin_object1.to_object_reference(),
coin_object2.to_object_reference(),
gas_object.to_object_reference(),
coin_object1.id(),
coin_object2.id(),
gas_object.id(),
GAS_VALUE_FOR_TESTING,
signature_callback(&key1),
)
Expand Down
7 changes: 7 additions & 0 deletions sui_types/src/gas_coin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use move_core_types::{
};
use serde::{Deserialize, Serialize};
use std::convert::{TryFrom, TryInto};
use std::fmt::{Display, Formatter};

use crate::{
base_types::{ObjectID, SequenceNumber},
Expand Down Expand Up @@ -104,3 +105,9 @@ impl TryFrom<&Object> for GasCoin {
}
}
}

impl Display for GasCoin {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Coin {{ id: {}, value: {} }}", self.id(), self.value())
}
}
2 changes: 1 addition & 1 deletion sui_types/src/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -729,7 +729,7 @@ impl Display for CertifiedTransaction {
TransactionKind::Call(c) => {
writeln!(writer, "Transaction Kind : Call")?;
writeln!(writer, "Gas Budget : {}", c.gas_budget)?;
writeln!(writer, "Package ID : {}", c.package.0.to_hex())?;
writeln!(writer, "Package ID : {}", c.package.0.to_hex_literal())?;
writeln!(writer, "Module : {}", c.module)?;
writeln!(writer, "Function : {}", c.function)?;
writeln!(writer, "Object Arguments : {:?}", c.object_arguments)?;
Expand Down