Skip to content

Commit

Permalink
[wallet] Add merge/split coins command to the wallet (MystenLabs#657)
Browse files Browse the repository at this point in the history
* * added merge split coin to wallet

* * added docs for merge and split coin

* address PR comments
  • Loading branch information
patrickkuo authored Mar 5, 2022
1 parent ae842e2 commit 2b5e4f7
Show file tree
Hide file tree
Showing 8 changed files with 427 additions and 49 deletions.
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
#[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

0 comments on commit 2b5e4f7

Please sign in to comment.