diff --git a/api-server/scanner-lib/src/blockchain_state/mod.rs b/api-server/scanner-lib/src/blockchain_state/mod.rs index 7b6282c273..002378a0a0 100644 --- a/api-server/scanner-lib/src/blockchain_state/mod.rs +++ b/api-server/scanner-lib/src/blockchain_state/mod.rs @@ -1003,7 +1003,7 @@ async fn update_tables_from_transaction_outputs( increase_address_amount( db_tx, &address, - &stake_pool_data.value(), + &stake_pool_data.pledge(), CoinOrTokenId::Coin, block_height, ) diff --git a/api-server/stack-test-suite/tests/v1/pool.rs b/api-server/stack-test-suite/tests/v1/pool.rs index a10a59302c..515a8cf29b 100644 --- a/api-server/stack-test-suite/tests/v1/pool.rs +++ b/api-server/stack-test-suite/tests/v1/pool.rs @@ -213,7 +213,7 @@ async fn ok(#[case] seed: Seed) { ); assert_eq!( body.get("staker_balance").unwrap(), - &serde_json::json!(amount_to_json(pool_data.value())) + &serde_json::json!(amount_to_json(pool_data.pledge())) ); assert_eq!( diff --git a/api-server/stack-test-suite/tests/v1/pools.rs b/api-server/stack-test-suite/tests/v1/pools.rs index 93bb0e08df..6e21487c3f 100644 --- a/api-server/stack-test-suite/tests/v1/pools.rs +++ b/api-server/stack-test-suite/tests/v1/pools.rs @@ -231,7 +231,7 @@ async fn ok(#[case] seed: Seed) { ); assert_eq!( json.get("staker_balance").unwrap(), - &serde_json::json!(amount_to_json(pool_data.value())) + &serde_json::json!(amount_to_json(pool_data.pledge())) ); assert_eq!( @@ -253,7 +253,7 @@ async fn ok(#[case] seed: Seed) { } { - pools.sort_by_key(|x| Reverse(x.1.value())); + pools.sort_by_key(|x| Reverse(x.1.pledge())); let items = pools.len(); let url = format!("/api/v1/pool?sort=by_pledge&items={items}&offset=0"); // Given that the listener port is open, this will block until a @@ -283,7 +283,7 @@ async fn ok(#[case] seed: Seed) { ); assert_eq!( json.get("staker_balance").unwrap(), - &serde_json::json!(amount_to_json(pool_data.value())) + &serde_json::json!(amount_to_json(pool_data.pledge())) ); assert_eq!( diff --git a/api-server/web-server/src/api/json_helpers.rs b/api-server/web-server/src/api/json_helpers.rs index 1be0932ed6..4b0743f0e2 100644 --- a/api-server/web-server/src/api/json_helpers.rs +++ b/api-server/web-server/src/api/json_helpers.rs @@ -79,7 +79,7 @@ pub fn txoutput_to_json(out: &TxOutput, chain_config: &ChainConfig) -> serde_jso "type": "CreateStakePool", "pool_id": Address::new(chain_config, pool_id).expect("no error").get(), "data": { - "amount": amount_to_json(data.value()), + "amount": amount_to_json(data.pledge()), "staker": Address::new(chain_config, data.staker()).expect("no error").get(), "vrf_public_key": Address::new(chain_config, data.vrf_public_key()).expect("no error").get(), "decommission_key": Address::new(chain_config, data.decommission_key()).expect("no error").get(), diff --git a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs index 57eac7cd95..ca10ed33ef 100644 --- a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs +++ b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs @@ -291,7 +291,7 @@ impl ConstrainedValueAccumulator { TxOutput::CreateStakePool(_, data) => insert_or_increase( &mut accumulator.unconstrained_value, CoinOrTokenId::Coin, - data.value(), + data.pledge(), )?, TxOutput::ProduceBlockFromStake(_, _) | TxOutput::CreateDelegationId(_, _) => { /* do nothing as these outputs cannot produce values */ diff --git a/chainstate/src/interface/chainstate_interface_impl.rs b/chainstate/src/interface/chainstate_interface_impl.rs index 6f2cbea2dd..967dbd21a0 100644 --- a/chainstate/src/interface/chainstate_interface_impl.rs +++ b/chainstate/src/interface/chainstate_interface_impl.rs @@ -649,7 +649,7 @@ fn get_output_coin_amount( TxOutput::Transfer(v, _) | TxOutput::LockThenTransfer(v, _, _) | TxOutput::Burn(v) => { v.coin_amount() } - TxOutput::CreateStakePool(_, data) => Some(data.value()), + TxOutput::CreateStakePool(_, data) => Some(data.pledge()), TxOutput::ProduceBlockFromStake(_, pool_id) => { let pledge_amount = pos_accounting_view .get_pool_data(*pool_id) diff --git a/chainstate/tx-verifier/src/transaction_verifier/mod.rs b/chainstate/tx-verifier/src/transaction_verifier/mod.rs index 46a00133e9..c43c790116 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/mod.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/mod.rs @@ -363,7 +363,7 @@ where Some(input_utxo_outpoint) => { let expected_pool_id = pos_accounting::make_pool_id(input_utxo_outpoint); let res = if expected_pool_id == *pool_id { - if data.value() >= self.chain_config.as_ref().min_stake_pool_pledge() { + if data.pledge() >= self.chain_config.as_ref().min_stake_pool_pledge() { self.pos_accounting_adapter .operations(tx_source) .create_pool(*pool_id, data.as_ref().clone().into()) @@ -371,7 +371,7 @@ where } else { Err(ConnectTransactionError::NotEnoughPledgeToCreateStakePool( tx.get_id(), - data.value(), + data.pledge(), self.chain_config.as_ref().min_stake_pool_pledge(), )) } diff --git a/common/src/chain/transaction/mod.rs b/common/src/chain/transaction/mod.rs index a05b7a4041..59eb7a808c 100644 --- a/common/src/chain/transaction/mod.rs +++ b/common/src/chain/transaction/mod.rs @@ -39,12 +39,16 @@ pub use output::*; pub mod signature; +mod printout; + use self::signature::inputsig::InputWitness; use self::signed_transaction::SignedTransaction; mod transaction_v1; use transaction_v1::TransactionV1; +use super::ChainConfig; + /// In case multiple types of transactions are developed, the size of that transaction can be described here along with its type pub enum TransactionSize { ScriptedTransaction(usize), @@ -139,6 +143,10 @@ impl Transaction { } SignedTransaction::new(self, witnesses) } + + pub fn printable(&self, chain_config: &ChainConfig) -> String { + printout::transaction_summary(self, chain_config) + } } impl serde::Serialize for Id { diff --git a/common/src/chain/transaction/output/stakelock.rs b/common/src/chain/transaction/output/stakelock.rs index 2c752ea939..a4c9c0474c 100644 --- a/common/src/chain/transaction/output/stakelock.rs +++ b/common/src/chain/transaction/output/stakelock.rs @@ -33,7 +33,7 @@ use super::Destination; serde::Deserialize, )] pub struct StakePoolData { - value: Amount, + pledge: Amount, staker: Destination, vrf_public_key: VRFPublicKey, decommission_key: Destination, @@ -43,7 +43,7 @@ pub struct StakePoolData { impl StakePoolData { pub fn new( - value: Amount, + pledge: Amount, staker: Destination, vrf_public_key: VRFPublicKey, decommission_key: Destination, @@ -51,7 +51,7 @@ impl StakePoolData { cost_per_block: Amount, ) -> Self { Self { - value, + pledge, staker, vrf_public_key, decommission_key, @@ -80,7 +80,7 @@ impl StakePoolData { self.cost_per_block } - pub fn value(&self) -> Amount { - self.value + pub fn pledge(&self) -> Amount { + self.pledge } } diff --git a/common/src/chain/transaction/printout.rs b/common/src/chain/transaction/printout.rs new file mode 100644 index 0000000000..2eb3e52272 --- /dev/null +++ b/common/src/chain/transaction/printout.rs @@ -0,0 +1,382 @@ +// Copyright (c) 2021-2024 RBB S.r.l +// opensource@mintlayer.org +// SPDX-License-Identifier: MIT +// Licensed under the MIT License; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/mintlayer/mintlayer-core/blob/master/LICENSE +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crypto::vrf::VRFPublicKey; +use serialization::Encode; + +use crate::{ + address::Address, + chain::{ + tokens::{IsTokenFreezable, NftIssuance, TokenId, TokenIssuance, TokenTotalSupply}, + ChainConfig, DelegationId, PoolId, + }, + primitives::{Amount, Idable, H256}, +}; + +use super::{ + output_value::OutputValue, stakelock::StakePoolData, timelock::OutputTimeLock, Destination, + Transaction, TxOutput, +}; +use std::fmt::Write; + +fn id_to_hex_string(id: H256) -> String { + let hex_string = format!("{:?}", id); + hex_string.strip_prefix("0x").unwrap_or(&hex_string).to_string() +} + +pub fn transaction_summary(tx: &Transaction, chain_config: &ChainConfig) -> String { + let mut result = format!( + "Transaction summary:\n\ + Transaction id: {}\n\ + === BEGIN OF INPUTS ===\n\ + ", + id_to_hex_string(tx.get_id().to_hash()) + ); + + let fmt_ml = |v: &Amount| v.into_fixedpoint_str(chain_config.coin_decimals()); + let fmt_val = |val: &OutputValue| { + match val { + OutputValue::Coin(amount) => fmt_ml(amount), + OutputValue::TokenV0(token_data) => format!("{token_data:?}"), // Not important since it's deprecated + OutputValue::TokenV1(id, amount) => { + format!( + "TokenV1({}, {amount:?})", + Address::new(chain_config, id) + .expect("Cannot fail due to TokenId being fixed size") + ) + } + } + }; + let fmt_timelock = |tl: &OutputTimeLock| match tl { + OutputTimeLock::UntilHeight(h) => format!("OutputTimeLock::UntilHeight({h})"), + OutputTimeLock::UntilTime(t) => format!( + "OutputTimeLock::UntilTime({})", + t.into_time().as_standard_printable_time() + ), + OutputTimeLock::ForBlockCount(n) => format!("OutputTimeLock::ForBlockCount({n} blocks)"), + OutputTimeLock::ForSeconds(secs) => { + format!("OutputTimeLock::ForSeconds({secs} seconds)") + } + }; + let fmt_dest = + |d: &Destination| format!("{}", Address::new(chain_config, d).expect("addressable")); + let fmt_vrf = + |k: &VRFPublicKey| format!("{}", Address::new(chain_config, k).expect("addressable")); + let fmt_poolid = |id: &PoolId| { + Address::new(chain_config, id).expect("Cannot fail because fixed size addressable") + }; + let fmt_tknid = |id: &TokenId| { + Address::new(chain_config, id).expect("Cannot fail because fixed size addressable") + }; + let fmt_delid = |id: &DelegationId| { + Address::new(chain_config, id).expect("Cannot fail because fixed size addressable") + }; + let fmt_stakepooldata = |p: &StakePoolData| { + let pledge = fmt_ml(&p.pledge()); + format!( + "Pledge({pledge}), Staker({}), VRFPubKey({}), DecommissionKey({}), MarginRatio({}), CostPerBlock({})", + fmt_dest(p.staker()), + fmt_vrf(p.vrf_public_key()), + fmt_dest(p.decommission_key()), + p.margin_ratio_per_thousand().into_percentage_str(), + fmt_ml(&p.cost_per_block()) + ) + }; + let fmt_tkn_supply = |s: &TokenTotalSupply, d: u8| match s { + TokenTotalSupply::Fixed(v) => format!("Fixed({})", v.into_fixedpoint_str(d)), + TokenTotalSupply::Lockable => "Lockable".to_string(), + TokenTotalSupply::Unlimited => "Unlimited".to_string(), + }; + let fmt_tkn_frzble = |f: &IsTokenFreezable| match f { + IsTokenFreezable::No => "Yes".to_string(), + IsTokenFreezable::Yes => "No".to_string(), + }; + let fmt_tkn_iss = |iss: &TokenIssuance| { + match iss { + TokenIssuance::V1(iss1) => format!( + "TokenIssuance(Ticker({}), Decimals({}), MetadataUri({}), TotalSupply({}), Authority({}), IsFreezable({}))", + String::from_utf8_lossy(&iss1.token_ticker), + iss1.number_of_decimals, + String::from_utf8_lossy(&iss1.metadata_uri), + fmt_tkn_supply(&iss1.total_supply, iss1.number_of_decimals), + fmt_dest(&iss1.authority), + fmt_tkn_frzble(&iss1.is_freezable) + ), + } + }; + let fmt_nft_iss = |iss: &NftIssuance| match iss { + NftIssuance::V0(iss1) => { + let md = &iss1.metadata; + let creator = match &md.creator { + Some(c) => hex::encode(c.public_key.encode()).to_string(), + None => "Unspecified".to_string(), + }; + format!( + "Create({}), Name({}), Description({}), Ticker({}), IconUri({}), AdditionalMetaData({}), MediaUri({}), MediaHash(0x{})", + creator, + String::from_utf8_lossy(&md.name), + String::from_utf8_lossy(&md.description), + String::from_utf8_lossy(&md.ticker), + String::from_utf8_lossy(md.icon_uri.as_ref().as_ref().unwrap_or(&vec![])), + String::from_utf8_lossy( + md.additional_metadata_uri.as_ref().as_ref().unwrap_or(&vec![]) + ), + String::from_utf8_lossy( + md.media_uri.as_ref().as_ref().unwrap_or(&vec![]) + ), + hex::encode(&md.media_hash), + + ) + } + }; + + for input in tx.inputs() { + writeln!(&mut result, "- {input:?}").expect("Writing to a memory buffer should not fail"); + } + + writeln!( + &mut result, + "=== END OF INPUTS ===\n=== BEGIN OF OUTPUTS ===" + ) + .expect("Writing to a memory buffer should not fail"); + + for output in tx.outputs() { + let s = match output { + TxOutput::Transfer(val, dest) => { + let val_str = fmt_val(val); + format!("Transfer({}, {val_str})", fmt_dest(dest)) + } + TxOutput::LockThenTransfer(val, dest, timelock) => { + let val_str = fmt_val(val); + format!( + "LockThenTransfer({}, {val_str}, {})", + fmt_dest(dest), + fmt_timelock(timelock) + ) + } + TxOutput::Burn(val) => fmt_val(val), + TxOutput::CreateStakePool(id, data) => { + format!( + "CreateStakePool(Id({}), {})", + fmt_poolid(id), + fmt_stakepooldata(data) + ) + } + TxOutput::ProduceBlockFromStake(dest, pool_id) => { + format!( + "ProduceBlockFromStake({}, {})", + fmt_dest(dest), + fmt_poolid(pool_id) + ) + } + TxOutput::CreateDelegationId(owner, pool_id) => { + format!( + "CreateDelegationId(Owner({}), StakingPool({}))", + fmt_dest(owner), + fmt_poolid(pool_id) + ) + } + TxOutput::DelegateStaking(amount, del_ig) => { + format!( + "DelegateStaking(Owner({}), StakingPool({}))", + fmt_ml(amount), + fmt_delid(del_ig) + ) + } + TxOutput::IssueFungibleToken(issuance) => { + format!("IssueFungibleToken({})", fmt_tkn_iss(issuance)) + } + TxOutput::IssueNft(token_id, iss, receiver) => { + format!( + "IssueNft(Id({}), NftIssuance({}), Receiver({}))", + fmt_tknid(token_id), + fmt_nft_iss(iss), + fmt_dest(receiver) + ) + } + TxOutput::DataDeposit(data) => { + format!("DataDeposit(0x{})", hex::encode(data)) + } + }; + writeln!(&mut result, "- {s}").expect("Writing to a memory buffer should not fail"); + } + writeln!(&mut result, "=== END OF OUTPUTS ===") + .expect("Writing to a memory buffer should not fail"); + + result +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::{ + address::Address, + chain::{ + block::timestamp::BlockTimestamp, + config::create_mainnet, + output_value::OutputValue, + stakelock::StakePoolData, + timelock::OutputTimeLock, + tokens::{ + IsTokenFreezable, Metadata, NftIssuance, NftIssuanceV0, TokenCreator, TokenId, + TokenIssuance, TokenIssuanceV1, TokenTotalSupply, + }, + DelegationId, Destination, OutPointSourceId, PoolId, Transaction, TxInput, TxOutput, + UtxoOutPoint, + }, + primitives::{per_thousand::PerThousand, Amount, Id, H256}, + time_getter::TimeGetter, + }; + use crypto::{ + key::{KeyKind, PrivateKey}, + vrf::{VRFKeyKind, VRFPrivateKey}, + }; + use serialization::extras::non_empty_vec::DataOrNoVec; + + // This test is made so that the data can be viewed for evaluation purposes + #[test] + fn try_it_out() { + let cfg = create_mainnet(); + + let (_vrf_priv_key, vrf_pub_key) = VRFPrivateKey::new_from_entropy(VRFKeyKind::Schnorrkel); + let (_priv_key, pub_key) = PrivateKey::new_from_entropy(KeyKind::Secp256k1Schnorr); + + let outputs = [ + TxOutput::Burn(OutputValue::Coin( + Amount::from_fixedpoint_str("10.123", 11).unwrap(), + )), + TxOutput::Burn(OutputValue::TokenV1( + TokenId::zero(), + Amount::from_fixedpoint_str("15.221", 11).unwrap(), + )), + TxOutput::Transfer( + OutputValue::Coin(Amount::from_fixedpoint_str("123.15", 11).unwrap()), + Address::from_str(&cfg, "mtc1q9d860uag5swe78ac9c2lct9mkctfyftqvwj3ypa") + .unwrap() + .decode_object(&cfg) + .unwrap(), + ), + TxOutput::LockThenTransfer( + OutputValue::Coin(Amount::from_fixedpoint_str("123.15", 11).unwrap()), + Address::from_str(&cfg, "mtc1q9d860uag5swe78ac9c2lct9mkctfyftqvwj3ypa") + .unwrap() + .decode_object(&cfg) + .unwrap(), + OutputTimeLock::ForBlockCount(10), + ), + TxOutput::LockThenTransfer( + OutputValue::Coin(Amount::from_fixedpoint_str("123.15", 11).unwrap()), + Address::from_str(&cfg, "mtc1q9d860uag5swe78ac9c2lct9mkctfyftqvwj3ypa") + .unwrap() + .decode_object(&cfg) + .unwrap(), + OutputTimeLock::ForSeconds(2000), + ), + TxOutput::LockThenTransfer( + OutputValue::Coin(Amount::from_fixedpoint_str("123.15", 11).unwrap()), + Address::from_str(&cfg, "mtc1q9d860uag5swe78ac9c2lct9mkctfyftqvwj3ypa") + .unwrap() + .decode_object(&cfg) + .unwrap(), + OutputTimeLock::UntilHeight(1000.into()), + ), + TxOutput::LockThenTransfer( + OutputValue::Coin(Amount::from_fixedpoint_str("123.15", 11).unwrap()), + Address::from_str(&cfg, "mtc1q9d860uag5swe78ac9c2lct9mkctfyftqvwj3ypa") + .unwrap() + .decode_object(&cfg) + .unwrap(), + OutputTimeLock::UntilTime(BlockTimestamp::from_time( + TimeGetter::default().get_time(), + )), + ), + TxOutput::CreateStakePool( + PoolId::new(H256::random()), + Box::new(StakePoolData::new( + Amount::from_fixedpoint_str("1000.225", 11).unwrap(), + Destination::AnyoneCanSpend, + vrf_pub_key, + Destination::AnyoneCanSpend, + PerThousand::new(15).unwrap(), + Amount::from_fixedpoint_str("5.2", 11).unwrap(), + )), + ), + TxOutput::ProduceBlockFromStake( + Destination::AnyoneCanSpend, + PoolId::new(H256::random()), + ), + TxOutput::CreateDelegationId(Destination::AnyoneCanSpend, PoolId::new(H256::random())), + TxOutput::DelegateStaking( + Amount::from_fixedpoint_str("1.2", 11).unwrap(), + DelegationId::new(H256::random()), + ), + TxOutput::IssueFungibleToken(Box::new(TokenIssuance::V1(TokenIssuanceV1 { + token_ticker: "abc".to_owned().into_bytes(), + number_of_decimals: 5, + metadata_uri: "http://xyz.xyz".to_owned().into_bytes(), + total_supply: TokenTotalSupply::Fixed( + Amount::from_fixedpoint_str("1000000", 5).unwrap(), + ), + authority: Destination::AnyoneCanSpend, + is_freezable: IsTokenFreezable::No, + }))), + TxOutput::IssueNft( + TokenId::new(H256::random()), + Box::new(NftIssuance::V0(NftIssuanceV0 { + metadata: Metadata { + creator: Some(TokenCreator { + public_key: pub_key, + }), + name: "MyGreatNFT".to_string().into_bytes(), + description: "NFTDescription".to_string().into_bytes(), + ticker: "abc".to_owned().into_bytes(), + icon_uri: DataOrNoVec::from(Some( + "http://icon.com".to_string().into_bytes(), + )), + additional_metadata_uri: DataOrNoVec::from(Some( + "http://uri.com".to_string().into_bytes(), + )), + media_uri: DataOrNoVec::from(Some( + "http://media.com".to_string().into_bytes(), + )), + media_hash: H256::random().as_bytes().to_vec(), + }, + })), + Destination::AnyoneCanSpend, + ), + TxOutput::DataDeposit("DataToDeposit!".to_string().into_bytes()), + ]; + + let tx = Transaction::new( + 0, + [ + TxInput::Utxo(UtxoOutPoint::new( + OutPointSourceId::Transaction(Id::new(H256::random())), + 2, + )), + TxInput::Utxo(UtxoOutPoint::new( + OutPointSourceId::Transaction(Id::new(H256::random())), + 1, + )), + ] + .to_vec(), + outputs.to_vec(), + ) + .unwrap(); + + println!("{}", transaction_summary(&tx, &cfg)); + } +} diff --git a/common/src/primitives/per_thousand.rs b/common/src/primitives/per_thousand.rs index 4f305afdec..2b36f98605 100644 --- a/common/src/primitives/per_thousand.rs +++ b/common/src/primitives/per_thousand.rs @@ -60,7 +60,7 @@ impl PerThousand { Some(result) } - pub fn into_decimal_str(&self) -> String { + pub fn into_percentage_str(&self) -> String { format!( "{}%", Amount::from_atoms(self.0.into()).into_fixedpoint_str(1) @@ -120,18 +120,30 @@ mod tests { #[test] fn test_into_decimal_str() { - assert_eq!(PerThousand::new(1).unwrap().into_decimal_str(), "0.1%"); - assert_eq!(PerThousand::new(10).unwrap().into_decimal_str(), "1%"); - assert_eq!(PerThousand::new(100).unwrap().into_decimal_str(), "10%"); - assert_eq!(PerThousand::new(1000).unwrap().into_decimal_str(), "100%"); - - assert_eq!(PerThousand::new(11).unwrap().into_decimal_str(), "1.1%"); - assert_eq!(PerThousand::new(23).unwrap().into_decimal_str(), "2.3%"); - assert_eq!(PerThousand::new(98).unwrap().into_decimal_str(), "9.8%"); - - assert_eq!(PerThousand::new(311).unwrap().into_decimal_str(), "31.1%"); - assert_eq!(PerThousand::new(564).unwrap().into_decimal_str(), "56.4%"); - assert_eq!(PerThousand::new(827).unwrap().into_decimal_str(), "82.7%"); + assert_eq!(PerThousand::new(1).unwrap().into_percentage_str(), "0.1%"); + assert_eq!(PerThousand::new(10).unwrap().into_percentage_str(), "1%"); + assert_eq!(PerThousand::new(100).unwrap().into_percentage_str(), "10%"); + assert_eq!( + PerThousand::new(1000).unwrap().into_percentage_str(), + "100%" + ); + + assert_eq!(PerThousand::new(11).unwrap().into_percentage_str(), "1.1%"); + assert_eq!(PerThousand::new(23).unwrap().into_percentage_str(), "2.3%"); + assert_eq!(PerThousand::new(98).unwrap().into_percentage_str(), "9.8%"); + + assert_eq!( + PerThousand::new(311).unwrap().into_percentage_str(), + "31.1%" + ); + assert_eq!( + PerThousand::new(564).unwrap().into_percentage_str(), + "56.4%" + ); + assert_eq!( + PerThousand::new(827).unwrap().into_percentage_str(), + "82.7%" + ); } #[rstest] diff --git a/node-gui/src/main_window/mod.rs b/node-gui/src/main_window/mod.rs index c769667893..1d2c52cca5 100644 --- a/node-gui/src/main_window/mod.rs +++ b/node-gui/src/main_window/mod.rs @@ -83,7 +83,7 @@ fn print_coin_amount(chain_config: &ChainConfig, value: Amount) -> String { } fn print_margin_ratio(value: PerThousand) -> String { - value.into_decimal_str() + value.into_percentage_str() } fn print_coin_amount_with_ticker(chain_config: &ChainConfig, value: Amount) -> String { diff --git a/pos-accounting/src/pool/pool_data.rs b/pos-accounting/src/pool/pool_data.rs index 079fe92782..0787789eee 100644 --- a/pos-accounting/src/pool/pool_data.rs +++ b/pos-accounting/src/pool/pool_data.rs @@ -99,7 +99,7 @@ impl From for PoolData { fn from(stake_data: StakePoolData) -> Self { Self { decommission_destination: stake_data.decommission_key().clone(), - pledge_amount: stake_data.value(), + pledge_amount: stake_data.pledge(), staker_rewards: Amount::ZERO, vrf_public_key: stake_data.vrf_public_key().clone(), margin_ratio_per_thousand: stake_data.margin_ratio_per_thousand(), diff --git a/wallet/src/account/currency_grouper/mod.rs b/wallet/src/account/currency_grouper/mod.rs index 93e48280ed..6f1b0025d5 100644 --- a/wallet/src/account/currency_grouper/mod.rs +++ b/wallet/src/account/currency_grouper/mod.rs @@ -44,7 +44,7 @@ pub(crate) fn group_outputs( TxOutput::Transfer(v, _) | TxOutput::LockThenTransfer(v, _, _) | TxOutput::Burn(v) => { v.clone() } - TxOutput::CreateStakePool(_, stake) => OutputValue::Coin(stake.value()), + TxOutput::CreateStakePool(_, stake) => OutputValue::Coin(stake.pledge()), TxOutput::DelegateStaking(amount, _) => OutputValue::Coin(*amount), TxOutput::CreateDelegationId(_, _) | TxOutput::IssueFungibleToken(_) @@ -93,7 +93,7 @@ pub fn group_outputs_with_issuance_fee( TxOutput::Transfer(v, _) | TxOutput::LockThenTransfer(v, _, _) | TxOutput::Burn(v) => { v.clone() } - TxOutput::CreateStakePool(_, stake) => OutputValue::Coin(stake.value()), + TxOutput::CreateStakePool(_, stake) => OutputValue::Coin(stake.pledge()), TxOutput::DelegateStaking(amount, _) => OutputValue::Coin(*amount), TxOutput::IssueFungibleToken(_) => { OutputValue::Coin(chain_config.fungible_token_issuance_fee()) @@ -142,7 +142,7 @@ pub fn group_utxos_for_input( // Get the supported output value let output_value = match get_tx_output(&output) { TxOutput::Transfer(v, _) | TxOutput::LockThenTransfer(v, _, _) => v.clone(), - TxOutput::CreateStakePool(_, stake) => OutputValue::Coin(stake.value()), + TxOutput::CreateStakePool(_, stake) => OutputValue::Coin(stake.pledge()), TxOutput::IssueNft(token_id, _, _) => { OutputValue::TokenV1(*token_id, Amount::from_atoms(1)) } diff --git a/wallet/wallet-cli-lib/src/commands/mod.rs b/wallet/wallet-cli-lib/src/commands/mod.rs index abb3da45c2..bad250500e 100644 --- a/wallet/wallet-cli-lib/src/commands/mod.rs +++ b/wallet/wallet-cli-lib/src/commands/mod.rs @@ -1032,6 +1032,8 @@ where let output_str = match result.into_signed_tx() { Ok(signed_tx) => { + let summary = + signed_tx.transaction().printable(self.wallet_rpc.chain_config()); let result_hex: HexEncoded = signed_tx.into(); let qr_code_string = utils::qrcode::qrcode_from_str(result_hex.to_string()) @@ -1042,13 +1044,13 @@ where "The transaction has been fully signed signed as is ready to be broadcast to network. \ You can use the command `node-submit-transaction` in a wallet connected to the internet (this one or elsewhere). \ Pass the following data to the wallet to broadcast:\n\n{result_hex}\n\n\ - Or scan the Qr code with it:\n\n{qr_code_string}" + Or scan the Qr code with it:\n\n{qr_code_string}\n\n{summary}" ), Err(_) => format!( "The transaction has been fully signed signed as is ready to be broadcast to network. \ You can use the command `node-submit-transaction` in a wallet connected to the internet (this one or elsewhere). \ Pass the following data to the wallet to broadcast:\n\n{result_hex}\n\n\ - Transaction is too long to be put into a Qr code" + Transaction is too long to be put into a Qr code\n\n{summary}" ), } }