// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020, The Monero Project.
# include "MorphTokenWidget.h"
# include "ui_MorphTokenWidget.h"
# include "mainwindow.h"
# include "qrcode/QrCode.h"
# include "dialog/qrcodedialog.h"
# include <QMessageBox>
MorphTokenWidget : : MorphTokenWidget ( QWidget * parent ) :
QWidget ( parent ) ,
ui ( new Ui : : MorphTokenWidget )
{
ui - > setupUi ( this ) ;
m_ctx = MainWindow : : getContext ( ) ;
m_network = new UtilsNetworking ( this - > m_ctx - > network ) ;
m_api = new MorphTokenApi ( this , m_network ) ;
connect ( ui - > btnCreateTrade , & QPushButton : : clicked , this , & MorphTokenWidget : : createTrade ) ;
connect ( ui - > btn_lookupTrade , & QPushButton : : clicked , this , & MorphTokenWidget : : lookupTrade ) ;
connect ( ui - > btn_getRates , & QPushButton : : clicked , this , & MorphTokenWidget : : getRates ) ;
connect ( m_api , & MorphTokenApi : : ApiResponse , this , & MorphTokenWidget : : onApiResponse ) ;
connect ( ui - > combo_From , QOverload < int > : : of ( & QComboBox : : currentIndexChanged ) , [ this ] ( int index ) {
this - > displayRate ( ) ;
ui - > label_refundAddress - > setText ( QString ( " Refund address (%1): " ) . arg ( ui - > combo_From - > currentText ( ) ) ) ;
} ) ;
connect ( ui - > combo_To , QOverload < int > : : of ( & QComboBox : : currentIndexChanged ) , [ this ] ( int index ) {
this - > displayRate ( ) ;
ui - > label_destinationAddress - > setText ( QString ( " Destination address (%1): " ) . arg ( ui - > combo_To - > currentText ( ) ) ) ;
} ) ;
connect ( ui - > check_autorefresh , & QCheckBox : : toggled , [ this ] ( bool toggled ) {
m_countdown = 30 ;
toggled ? m_countdownTimer . start ( 1000 ) : m_countdownTimer . stop ( ) ;
ui - > check_autorefresh - > setText ( " Autorefresh " ) ;
} ) ;
connect ( & m_countdownTimer , & QTimer : : timeout , this , & MorphTokenWidget : : onCountdown ) ;
connect ( ui - > line_Id , & QLineEdit : : textChanged , [ this ] ( const QString & text ) {
ui - > btn_lookupTrade - > setEnabled ( ! text . isEmpty ( ) ) ;
ui - > check_autorefresh - > setEnabled ( ! text . isEmpty ( ) ) ;
} ) ;
// Default to BTC -> XMR
ui - > combo_From - > setCurrentIndex ( 1 ) ;
ui - > combo_To - > setCurrentIndex ( 0 ) ;
ui - > label_rate - > setVisible ( false ) ;
m_ratesTimer . setSingleShot ( true ) ;
connect ( & m_ratesTimer , & QTimer : : timeout , [ this ] {
ui - > label_rate - > setVisible ( false ) ;
} ) ;
ui - > qrCode - > setVisible ( false ) ;
ui - > label_depositAddress - > setVisible ( false ) ;
connect ( ui - > qrCode , & ClickableLabel : : clicked , this , & MorphTokenWidget : : showQrCodeDialog ) ;
ui - > tabWidget - > setTabVisible ( 2 , false ) ;
}
void MorphTokenWidget : : createTrade ( ) {
QString inputAsset = ui - > combo_From - > currentText ( ) ;
QString outputAsset = ui - > combo_To - > currentText ( ) ;
QString refundAddress = ui - > line_refundAddress - > text ( ) ;
QString destinationAddress = ui - > line_destinationAddress - > text ( ) ;
m_api - > createTrade ( inputAsset , outputAsset , refundAddress , destinationAddress ) ;
}
void MorphTokenWidget : : lookupTrade ( ) {
QString morphId = ui - > line_Id - > text ( ) ;
if ( ! morphId . isEmpty ( ) )
m_api - > getTrade ( morphId ) ;
}
void MorphTokenWidget : : getRates ( ) {
m_api - > getRates ( ) ;
}
void MorphTokenWidget : : onApiResponse ( const MorphTokenApi : : MorphTokenResponse & resp ) {
if ( ! resp . ok ) {
ui - > check_autorefresh - > setChecked ( false ) ;
QMessageBox : : warning ( this , " MorphToken error " , QString ( " Request failed: \n \n %1 " ) . arg ( resp . message ) ) ;
return ;
}
ui - > debugInfo - > setPlainText ( QJsonDocument ( resp . obj ) . toJson ( QJsonDocument : : Indented ) ) ;
bool shouldShowQr = ( resp . endpoint = = MorphTokenApi : : Endpoint : : CREATE_TRADE | | resp . endpoint = = MorphTokenApi : : Endpoint : : GET_TRADE ) ;
ui - > qrCode - > setVisible ( shouldShowQr ) ;
ui - > label_depositAddress - > setVisible ( shouldShowQr ) ;
if ( resp . endpoint = = MorphTokenApi : : Endpoint : : CREATE_TRADE | | resp . endpoint = = MorphTokenApi : : Endpoint : : GET_TRADE ) {
ui - > tabWidget - > setCurrentIndex ( 1 ) ;
ui - > line_Id - > setText ( resp . obj . value ( " id " ) . toString ( ) ) ;
auto obj = resp . obj ;
auto input = obj [ " input " ] . toObject ( ) ;
auto output = obj [ " output " ] . toArray ( ) [ 0 ] . toObject ( ) ;
QString state = obj . value ( " state " ) . toString ( ) ;
QString statusText ;
ui - > trade - > setTitle ( QString ( " Trade (%1) " ) . arg ( state ) ) ;
statusText + = QString ( " Morph ID: %1 \n \n " ) . arg ( obj [ " id " ] . toString ( ) ) ;
if ( state = = " PENDING " ) {
statusText + = QString ( " Waiting for a deposit, send %1 to %2 \n " ) . arg ( input [ " asset " ] . toString ( ) ,
input [ " deposit_address " ] . toString ( ) ) ;
statusText + = QString ( " Rate: 1 %1 -> %2 %3 \n \n " ) . arg ( input [ " asset " ] . toString ( ) ,
output [ " seen_rate " ] . toString ( ) ,
output [ " asset " ] . toString ( ) ) ;
statusText + = " Limits: \n " ;
statusText + = QString ( " Minimum amount accepted: %1 %2 \n " ) . arg ( formatAmount ( input [ " asset " ] . toString ( ) , input [ " limits " ] . toObject ( ) [ " min " ] . toDouble ( ) ) ,
input [ " asset " ] . toString ( ) ) ;
statusText + = QString ( " Maximum amount accepted: %1 %2 \n " ) . arg ( formatAmount ( input [ " asset " ] . toString ( ) , input [ " limits " ] . toObject ( ) [ " max " ] . toDouble ( ) ) ,
input [ " asset " ] . toString ( ) ) ;
statusText + = QString ( " \n Send a single deposit. If the amount is outside the limits, a refund will happen. " ) ;
m_depositAddress = input [ " deposit_address " ] . toString ( ) ;
const QrCode qrc ( m_depositAddress , QrCode : : Version : : AUTO , QrCode : : ErrorCorrectionLevel : : MEDIUM ) ;
int width = ui - > qrCode - > width ( ) ;
if ( qrc . isValid ( ) ) {
ui - > qrCode - > setPixmap ( qrc . toPixmap ( 1 ) . scaled ( width , width , Qt : : KeepAspectRatio ) ) ;
}
} else if ( state = = " PROCESSING " | | state = = " TRADING " | | state = = " CONFIRMING " ) {
if ( state = = " CONFIRMING " ) {
statusText + = QString ( " Waiting for confirmations \n " ) ;
} else if ( state = = " TRADING " ) {
statusText + = QString ( " Your transaction has been received and is confirmed. MorphToken is now executing your trade. \n "
" Usually this step takes no longer than a minute, "
" but in rare cases it can take a couple hours. \n "
" Wait a bit before contacting support. \n " ) ;
}
statusText + = QString ( " Converting %1 to %2 \n " ) . arg ( input [ " asset " ] . toString ( ) , output [ " asset " ] . toString ( ) ) ;
statusText + = QString ( " Sending to %1 \n " ) . arg ( output [ " address " ] . toString ( ) ) ;
statusText + = QString ( " Stuck? Contact support at contact@morphtoken.com " ) ;
} else if ( state = = " COMPLETE " ) {
if ( output [ " txid " ] . toString ( ) . isEmpty ( ) ) {
statusText + = QString ( " MorphToken is sending your transaction. \n " ) ;
statusText + = QString ( " MorphToken will send %1 %2 to %2 " ) . arg ( this - > formatAmount ( output [ " asset " ] . toString ( ) , output [ " converted_amount " ] . toDouble ( ) - output [ " network_fee " ] . toObject ( ) [ " fee " ] . toDouble ( ) ) ,
output [ " asset " ] . toString ( ) ,
output [ " address " ] . toString ( ) ) ;
} else {
statusText + = QString ( " Sent %1 %2 to %3 \n txid: {} " ) . arg ( this - > formatAmount ( output [ " asset " ] . toString ( ) , output [ " converted_amount " ] . toDouble ( ) - output [ " network_fee " ] . toObject ( ) [ " fee " ] . toDouble ( ) ) ,
output [ " asset " ] . toString ( ) ,
output [ " address " ] . toString ( ) ,
output [ " txid " ] . toString ( ) ) ;
}
} else if ( state = = " PROCESSING_REFUND " | | state = = " COMPLETE_WITH_REFUND " ) {
statusText + = QString ( " MorphToken will refund %1 %2 \n Reason: %3 \n " ) . arg ( obj [ " final_amount " ] . toString ( ) ,
obj [ " asset " ] . toString ( ) ,
obj [ " reason " ] . toString ( ) ) ;
if ( obj . contains ( " txid " ) ) {
statusText + = QString ( " txid: %1 " ) . arg ( obj [ " txid " ] . toString ( ) ) ;
}
} else if ( state = = " COMPLETE_WITHOUT_REFUND " ) {
statusText + = " Deposit amount below network fee, too small to refund. " ;
}
ui - > label_status - > setText ( statusText ) ;
} else if ( resp . endpoint = = MorphTokenApi : : Endpoint : : GET_RATES ) {
m_rates = resp . obj . value ( " data " ) . toObject ( ) ;
this - > displayRate ( ) ;
ui - > label_rate - > setVisible ( true ) ;
m_ratesTimer . start ( 120 * 1000 ) ;
}
if ( resp . endpoint = = MorphTokenApi : : Endpoint : : CREATE_TRADE ) {
QMessageBox : : information ( this , " MorphToken " , " Trade created! \n \n Make sure to save your Morph ID. You may need it in case something goes wrong. " ) ;
}
}
void MorphTokenWidget : : onCountdown ( ) {
if ( m_countdown > 0 ) {
m_countdown - = 1 ;
} else {
this - > lookupTrade ( ) ;
m_countdown = 30 ;
}
ui - > check_autorefresh - > setText ( QString ( " Autorefresh (%1) " ) . arg ( m_countdown ) ) ;
}
void MorphTokenWidget : : displayRate ( ) {
QString inputAsset = ui - > combo_From - > currentText ( ) ;
QString outputAsset = ui - > combo_To - > currentText ( ) ;
QString outputRate = m_rates . value ( inputAsset ) . toObject ( ) . value ( outputAsset ) . toString ( " 1 " ) ;
QString rateStr = QString ( " 1 %1 -> %2 %3 " ) . arg ( inputAsset , outputRate , outputAsset ) ;
ui - > label_rate - > setText ( rateStr ) ;
}
void MorphTokenWidget : : showQrCodeDialog ( ) {
QrCode qr ( m_depositAddress , QrCode : : Version : : AUTO , QrCode : : ErrorCorrectionLevel : : HIGH ) ;
auto * dialog = new QrCodeDialog ( this , qr , " Deposit address " ) ;
dialog - > exec ( ) ;
dialog - > deleteLater ( ) ;
}
QString MorphTokenWidget : : formatAmount ( const QString & asset , double amount ) {
double displayAmount ;
double div ;
if ( asset = = " ETH " )
div = 1e18 ;
else if ( asset = = " XMR " )
div = 1e12 ;
else
div = 1e8 ;
displayAmount = amount / div ;
return QString : : number ( displayAmount , ' f ' , 8 ) ;
}
MorphTokenWidget : : ~ MorphTokenWidget ( ) {
delete ui ;
}