Skip to content

Commit

Permalink
Implemented spats mint.
Browse files Browse the repository at this point in the history
Small refactorings in spats GUI as well as spats core.
Correct setting of total supply's number of decimals after dot in modify spark asset dialog.
Added more checks on spats actions construction/deserialization.
Some other, minor improvements.
  • Loading branch information
gevorgvoskanyan committed Feb 23, 2025
1 parent 2a5b7d3 commit c8b60c6
Show file tree
Hide file tree
Showing 23 changed files with 632 additions and 28 deletions.
4 changes: 4 additions & 0 deletions src/Makefile.qt.include
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ QT_FORMS_UI = \
qt/forms/masternodelist.ui \
qt/forms/myownspats.ui \
qt/forms/sparkassetdialog.ui \
qt/forms/spatsmintdialog.ui \
qt/forms/intro.ui \
qt/forms/modaloverlay.ui \
qt/forms/recover.ui \
Expand Down Expand Up @@ -150,6 +151,7 @@ QT_MOC_CPP = \
qt/moc_masternodelist.cpp \
qt/moc_myownspats.cpp \
qt/moc_sparkassetdialog.cpp \
qt/moc_spatsmintdialog.cpp \
qt/moc_modaloverlay.cpp \
qt/moc_notificator.cpp \
qt/moc_openuridialog.cpp \
Expand Down Expand Up @@ -232,6 +234,7 @@ BITCOIN_QT_H = \
qt/masternodelist.h \
qt/myownspats.h \
qt/sparkassetdialog.h \
qt/spatsmintdialog.h \
qt/modaloverlay.h \
qt/networkstyle.h \
qt/notificator.h \
Expand Down Expand Up @@ -460,6 +463,7 @@ BITCOIN_QT_WALLET_CPP = \
qt/masternodelist.cpp \
qt/myownspats.cpp \
qt/sparkassetdialog.cpp \
qt/spatsmintdialog.cpp \
qt/walletview.cpp \
qt/lelantusmodel.cpp \
qt/lelantusdialog.cpp \
Expand Down
10 changes: 9 additions & 1 deletion src/primitives/transaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,9 +239,17 @@ bool CTransaction::IsSpatsModify() const
return false;
}

bool CTransaction::IsSpatsMint() const
{
for (const CTxOut &txout: vout)
if (txout.scriptPubKey.IsSpatsMint())
return true;
return false;
}

bool CTransaction::IsSpatsTransaction() const
{
return IsSpatsCreate() || IsSpatsUnregister() || IsSpatsModify(); // TODO more
return IsSpatsCreate() || IsSpatsUnregister() || IsSpatsModify() || IsSpatsMint(); // TODO more
}

bool CTransaction::IsSparkSpend() const
Expand Down
1 change: 1 addition & 0 deletions src/primitives/transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,7 @@ class CTransaction
bool IsSpatsCreate() const;
bool IsSpatsUnregister() const;
bool IsSpatsModify() const;
bool IsSpatsMint() const;

bool HasNoRegularInputs() const;
bool HasPrivateInputs() const;
Expand Down
102 changes: 102 additions & 0 deletions src/qt/forms/spatsmintdialog.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SpatsMintDialog</class>
<widget class="QDialog" name="SpatsMintDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>200</height>
</rect>
</property>
<property name="windowTitle">
<string>Spats Mint Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>

<!-- New Supply -->
<item row="0" column="0">
<widget class="QLabel" name="labelNewSupply">
<property name="text">
<string>New Supply:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="newSupplySpinBox">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>1000000000</number>
</property>
<property name="decimals">
<number>2</number>
</property>
</widget>
</item>

<!-- Recipient -->
<item row="1" column="0">
<widget class="QLabel" name="labelRecipient">
<property name="text">
<string>Recipient:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="recipientEdit">
<property name="placeholderText">
<string>Enter recipient address</string>
</property>
</widget>
</item>

</layout>
</item>

<!-- Error Label -->
<item>
<widget class="QLabel" name="errorLabel">
<property name="text">
<string>Error: Invalid Input</string>
</property>
<property name="visible">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">QLabel { color: red; }</string>
</property>
</widget>
</item>

<!-- Buttons -->
<item>
<layout class="QHBoxLayout" name="buttonLayout">
<item>
<widget class="QPushButton" name="okButton">
<property name="text">
<string>OK</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cancelButton">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
79 changes: 61 additions & 18 deletions src/qt/myownspats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,24 @@

#include "walletmodel.h"
#include "sparkassetdialog.h"
#include "spatsmintdialog.h"
#include "myownspats.h"
#include "ui_myownspats.h"

enum MyOwnSpatsColumns {
ColumnAssetType = 0,
ColumnIdentifier,
ColumnSymbol,
ColumnName,
ColumnDescription,
ColumnTotalSupply,
ColumnFungible,
ColumnResupplyable,
ColumnPrecision,
ColumnMetadata,
ColumnCount // This keeps the count of total columns, always keep last!
};

MyOwnSpats::MyOwnSpats( const PlatformStyle *platform_style, QWidget *parent )
: QWidget( parent )
, platform_style_( platform_style )
Expand All @@ -22,6 +37,7 @@ MyOwnSpats::MyOwnSpats( const PlatformStyle *platform_style, QWidget *parent )
ui_->tableWidgetMyOwnSpats->setSelectionBehavior( QAbstractItemView::SelectRows );
ui_->tableWidgetMyOwnSpats->setSelectionMode( QAbstractItemView::SingleSelection );
connect( ui_->create_spark_asset, &QPushButton::clicked, this, &MyOwnSpats::onCreateButtonClicked );
connect( ui_->mint_spark_asset, &QPushButton::clicked, this, &MyOwnSpats::onMintButtonClicked );
connect( ui_->modify_spark_asset, &QPushButton::clicked, this, &MyOwnSpats::onModifyButtonClicked );
connect( ui_->unregister_spark_asset, &QPushButton::clicked, this, &MyOwnSpats::onUnregisterButtonClicked );
connect( this, &MyOwnSpats::displayMyOwnSpatsSignal, this, &MyOwnSpats::handleDisplayMyOwnSpatsSignal );
Expand Down Expand Up @@ -50,19 +66,19 @@ void MyOwnSpats::display_my_own_spats()
QTableWidgetItem *item;

// Fill the table with all attributes to be displayed
ui_->tableWidgetMyOwnSpats->setItem( row, 0, new QTableWidgetItem( QString::number( a.asset_type ) ) );
ui_->tableWidgetMyOwnSpats->setItem( row, 1, new QTableWidgetItem( QString::number( a.identifier ) ) );
ui_->tableWidgetMyOwnSpats->setItem( row, 2, new QTableWidgetItem( QString::fromStdString( a.symbol ) ) );
ui_->tableWidgetMyOwnSpats->setItem( row, 3, new QTableWidgetItem( QString::fromStdString( a.name ) ) );
ui_->tableWidgetMyOwnSpats->setItem( row, 4, new QTableWidgetItem( QString::fromStdString( a.description ) ) );
ui_->tableWidgetMyOwnSpats->setItem( row, 5, new QTableWidgetItem( QString::fromStdString( a.total_supply ) ) );
ui_->tableWidgetMyOwnSpats->setItem( row, 6, new QTableWidgetItem( a.fungible ? "Yes" : "No" ) );
ui_->tableWidgetMyOwnSpats->setItem( row, 7, new QTableWidgetItem( a.resupplyable ? "Yes" : "No" ) );
ui_->tableWidgetMyOwnSpats->setItem( row, 8, new QTableWidgetItem( QString::number( a.precision ) ) );
ui_->tableWidgetMyOwnSpats->setItem( row, 9, new QTableWidgetItem( QString::fromStdString( a.metadata ) ) );
ui_->tableWidgetMyOwnSpats->setItem( row, ColumnAssetType, new QTableWidgetItem( QString::number( a.asset_type ) ) );
ui_->tableWidgetMyOwnSpats->setItem( row, ColumnIdentifier, new QTableWidgetItem( QString::number( a.identifier ) ) );
ui_->tableWidgetMyOwnSpats->setItem( row, ColumnSymbol, new QTableWidgetItem( QString::fromStdString( a.symbol ) ) );
ui_->tableWidgetMyOwnSpats->setItem( row, ColumnName, new QTableWidgetItem( QString::fromStdString( a.name ) ) );
ui_->tableWidgetMyOwnSpats->setItem( row, ColumnDescription, new QTableWidgetItem( QString::fromStdString( a.description ) ) );
ui_->tableWidgetMyOwnSpats->setItem( row, ColumnTotalSupply, new QTableWidgetItem( QString::fromStdString( a.total_supply ) ) );
ui_->tableWidgetMyOwnSpats->setItem( row, ColumnFungible, new QTableWidgetItem( a.fungible ? "Yes" : "No" ) );
ui_->tableWidgetMyOwnSpats->setItem( row, ColumnResupplyable, new QTableWidgetItem( a.resupplyable ? "Yes" : "No" ) );
ui_->tableWidgetMyOwnSpats->setItem( row, ColumnPrecision, new QTableWidgetItem( QString::number( a.precision ) ) );
ui_->tableWidgetMyOwnSpats->setItem( row, ColumnMetadata, new QTableWidgetItem( QString::fromStdString( a.metadata ) ) );

// Make the table items read-only to prevent user editing
for ( int col = 0; col < 10; ++col )
for ( int col = ColumnAssetType; col < ColumnCount; ++col )
ui_->tableWidgetMyOwnSpats->item( row, col )->setFlags( ui_->tableWidgetMyOwnSpats->item( row, col )->flags() & ~Qt::ItemIsEditable );
++row;
}
Expand Down Expand Up @@ -111,7 +127,7 @@ void MyOwnSpats::adjustTextSize( int width, int height )
font.setPointSize( font_size );

// Set font size for all labels
ui_->label_filter_2->setFont( font );
ui_->label_filter_2->setFont( font ); // TODO implement the filtering
ui_->label_count_2->setFont( font );
ui_->countLabel->setFont( font );
ui_->tableWidgetMyOwnSpats->setFont( font );
Expand Down Expand Up @@ -146,15 +162,40 @@ void MyOwnSpats::onCreateButtonClicked()
}
}

void MyOwnSpats::onMintButtonClicked()
{
assert( wallet_model_ );
if ( const auto row = get_the_selected_row() ) {
try {
const bool resuppliable = ui_->tableWidgetMyOwnSpats->item( *row, ColumnResupplyable )->text() == "Yes";
if ( !resuppliable )
throw std::domain_error( "Cannot mint for a non-resuppliable asset!" );
const spats::asset_type_t asset_type{ ui_->tableWidgetMyOwnSpats->item( *row, ColumnAssetType )->text().toULongLong() };
assert( is_fungible_asset_type( asset_type ) );
const auto &asset = my_own_assets_map_.at( spats::universal_asset_id_t{ asset_type, {} } );
const auto &fungible_asset = std::get< spats::FungibleSparkAsset >( asset );
assert( fungible_asset.resupplyable() );
SpatsMintDialog dialog( platform_style_, fungible_asset, this );
if ( dialog.exec() == QDialog::Accepted )
wallet_model_->getWallet()->MintSparkAssetSupply( asset_type, dialog.getNewSupply(), dialog.getRecipient() ); // TODO user confirm callback
}
catch ( const std::exception &e ) {
QMessageBox::critical( this, tr( "Error" ), tr( "An error occurred: %1" ).arg( e.what() ) );
}
}
else
QMessageBox::critical( this, tr( "Error" ), tr( "Please select an asset to mint for." ) );
}

void MyOwnSpats::onModifyButtonClicked()
{
assert( wallet_model_ );
if ( const auto row = get_the_selected_row() ) {
try {
const spats::asset_type_t asset_type{ ui_->tableWidgetMyOwnSpats->item( *row, 0 )->text().toULongLong() };
const spats::asset_type_t asset_type{ ui_->tableWidgetMyOwnSpats->item( *row, ColumnAssetType )->text().toULongLong() };
spats::identifier_t identifier{ 0 };
if ( !is_fungible_asset_type( asset_type ) )
identifier = spats::identifier_t{ ui_->tableWidgetMyOwnSpats->item( *row, 1 )->text().toULongLong() };
identifier = spats::identifier_t{ ui_->tableWidgetMyOwnSpats->item( *row, ColumnIdentifier )->text().toULongLong() };
const auto &existing_asset = my_own_assets_map_.at( spats::universal_asset_id_t{ asset_type, identifier } );
SparkAssetDialog dialog( platform_style_, existing_asset, this );
if ( dialog.exec() == QDialog::Accepted )
Expand All @@ -173,10 +214,10 @@ void MyOwnSpats::onUnregisterButtonClicked()
assert( wallet_model_ );
if ( const auto row = get_the_selected_row() ) {
try {
const spats::asset_type_t asset_type{ ui_->tableWidgetMyOwnSpats->item( *row, 0 )->text().toULongLong() };
const spats::asset_type_t asset_type{ ui_->tableWidgetMyOwnSpats->item( *row, ColumnAssetType )->text().toULongLong() };
std::optional< spats::identifier_t > identifier;
if ( !is_fungible_asset_type( asset_type ) ) {
identifier = spats::identifier_t{ ui_->tableWidgetMyOwnSpats->item( *row, 1 )->text().toULongLong() };
identifier = spats::identifier_t{ ui_->tableWidgetMyOwnSpats->item( *row, ColumnIdentifier )->text().toULongLong() };
if ( any_other_nfts_within_same_line( asset_type, *identifier ) ) {
const QMessageBox::StandardButton reply = QMessageBox::question( this,
tr( "Unregister NFT" ),
Expand Down Expand Up @@ -209,9 +250,11 @@ void MyOwnSpats::onUnregisterButtonClicked()
void MyOwnSpats::updateButtonStates()
{
// Enable or disable buttons based on whether an item is selected in the table
const bool row_selected = get_the_selected_row().has_value();
for ( auto *const button : { ui_->mint_spark_asset, ui_->modify_spark_asset, ui_->unregister_spark_asset } )
const auto the_selected_row = get_the_selected_row();
const bool row_selected = the_selected_row.has_value();
for ( auto *const button : { ui_->modify_spark_asset, ui_->unregister_spark_asset } )
button->setEnabled( row_selected );
ui_->mint_spark_asset->setEnabled( row_selected && ui_->tableWidgetMyOwnSpats->item( *the_selected_row, ColumnResupplyable )->text() == "Yes" );
}

std::optional< int > MyOwnSpats::get_the_selected_row() const
Expand Down
1 change: 1 addition & 0 deletions src/qt/myownspats.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class MyOwnSpats : public QWidget, public spats::UpdatesObserver {

private Q_SLOTS:
void onCreateButtonClicked();
void onMintButtonClicked();
void onModifyButtonClicked();
void onUnregisterButtonClicked();
void handleDisplayMyOwnSpatsSignal() { display_my_own_spats(); }
Expand Down
8 changes: 5 additions & 3 deletions src/qt/sparkassetdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
#include "ui_sparkassetdialog.h"
#include "sparkassetdialog.h"

static spats::supply_amount_t convert_to_supply_amount( double value, unsigned precision )
// TODO move to a common location, with a header usable by spatsmintdialog too
spats::supply_amount_t convert_to_supply_amount( double value, unsigned precision )
{
const double scaled_value = value * utils::math::integral_power( std::uintmax_t( 10 ), precision );
const spats::supply_amount_t a{ boost::numeric_cast< std::uint64_t >( std::round( scaled_value ) ), precision };
Expand Down Expand Up @@ -180,8 +181,9 @@ void SparkAssetDialog::set_fields( const spats::SparkAsset &existing_asset )

std::visit( utils::overloaded{ [ & ]( const spats::FungibleSparkAsset &fungible ) {
ui_->fungibilityCheckBox->setChecked( true );
ui_->totalSupplySpin->setValue( fungible.total_supply().raw() / std::pow( 10.0, ui_->precisionSpinBox->value() ) );
ui_->precisionSpinBox->setValue( fungible.total_supply().precision() );
ui_->totalSupplySpin->setDecimals( fungible.precision() );
ui_->totalSupplySpin->setValue( fungible.total_supply().as_double() );
ui_->precisionSpinBox->setValue( fungible.precision() );
ui_->resupplyableCheckBox->setChecked( fungible.resupplyable() );
},
[ & ]( const spats::NonfungibleSparkAsset &nft ) {
Expand Down
Loading

0 comments on commit c8b60c6

Please sign in to comment.