chore(CPP): integrate tokens balance in UI POC

Integrate token count in UI
Use delete later for QML exposed items to avoid errors

Closes #6321
This commit is contained in:
Stefan 2022-07-26 13:49:03 +02:00 committed by Stefan Dunca
parent f3dcdf636a
commit 7cf0170a8a
35 changed files with 441 additions and 100 deletions

View File

@ -63,7 +63,7 @@ cmake -B build -S . -DCMAKE_PREFIX_PATH="$HOME/Qt/6.3.2/gcc_64" -DCMAKE_BUILD_TY
# Windows: cmake -B build -S . -DCMAKE_PREFIX_PATH="$HOME/Qt/6.3.2/mingw_64" -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=build/conan/conan_toolchain.cmake
cmake --build build --config Release
cmake --build build
```
### Run tests

View File

@ -58,7 +58,9 @@ public:
};
void clear() {
beginResetModel();
m_objects.clear();
endResetModel();
};
void push_back(const std::shared_ptr<T> newValue) {

View File

@ -2,7 +2,11 @@
#include "Helpers/BuildConfiguration.h"
#include <QObject>
#include <string>
#include <map>
#include <memory.h>
namespace Status::Helpers {
@ -29,4 +33,24 @@ bool iequals(const T& a, const T& b, size_t len = -1)
});
}
template<typename KeyType, typename ValT>
std::vector<KeyType> getKeys(const std::map<KeyType, ValT>& map)
{
std::vector<KeyType> keys;
keys.reserve(map.size());
for (const auto& [key, _] : map)
keys.push_back(key);
return keys;
}
static void doDeleteLater(QObject *obj) {
obj->deleteLater();
}
// TODO: use https://en.cppreference.com/w/cpp/memory/shared_ptr/allocate_shared
template<typename T, typename ...Args>
std::shared_ptr<T> makeSharedQObject(Args&& ...args) {
return std::shared_ptr<T>(new T(std::forward<Args>(args)...), doDeleteLater);
}
}

View File

@ -16,8 +16,8 @@ import Status.Onboarding
Item {
id: root
// TODO: fix error "Unable to assign Status::Onboarding::NewAccountController to Status::Onboarding::NewAccountController" then enable typed properties
required property var/*NewAccountController*/ newAccountController
/// \c NewAccountController
required property var newAccountController
signal userLoggedIn()
signal abortAccountCreation()

View File

@ -12,6 +12,7 @@ TextInput {
Rectangle {
anchors.fill: parent
border.width: 1
border.color: "#55555555"
z: parent.z - 1
}
}

View File

@ -11,7 +11,9 @@ import "base"
OnboardingPageBase {
id: root
required property var onboardingController // OnboardingController
/// \c OnboardingController
/// \todo investigate "Unable to assign Status::Onboarding::OnboardingController to Status::Onboarding::OnboardingController"
required property var onboardingController
signal setupNewAccount()
/// \param statusAccount \c UserAccount
@ -51,15 +53,6 @@ OnboardingPageBase {
id: passwordInput
Layout.preferredWidth: 328
Layout.preferredHeight: 44
// TODO: remove dev helper
text: "1234567890"
Timer {
interval: 100
running: loginButton.enabled && accountsComboBox.count
onTriggered: loginButton.clicked()
}
// END dev
}
Button {

View File

@ -1,10 +1,12 @@
import QtQuick
import Status.Onboarding
/*! Proposal on how to templetize the alignment requirement of some views
*/
OnboardingPageBase {
// TODO: fix error "Unable to assign Status::Onboarding::NewAccountController to Status::Onboarding::NewAccountController" then enable typed properties
required property var/*NewAccountController*/ newAccountController
required property var newAccountController
/// Common reference item that doesn't change between common views/pages
readonly property Item alignmentItem: alignmentBaselineItem

View File

@ -6,6 +6,8 @@
#include <StatusGo/SignalsManager.h>
#include <Helpers/helpers.h>
namespace Status::Onboarding {
namespace StatusGo = Status::StatusGo;
@ -17,9 +19,9 @@ OnboardingController::OnboardingController(AccountsServiceInterfacePtr accountsS
{ // Init accounts
std::vector<std::shared_ptr<UserAccount>> accounts;
for(auto &account : getOpenedAccounts()) {
accounts.push_back(std::make_shared<UserAccount>(std::make_unique<MultiAccount>(std::move(account))));
accounts.push_back(Helpers::makeSharedQObject<UserAccount>(std::make_unique<MultiAccount>(std::move(account))));
}
m_accounts = std::make_shared<UserAccountsModel>(std::move(accounts));
m_accounts = Helpers::makeSharedQObject<UserAccountsModel>(std::move(accounts));
}
connect(StatusGo::SignalsManager::instance(), &StatusGo::SignalsManager::nodeLogin, this, &OnboardingController::onLogin);

View File

@ -46,7 +46,7 @@ public:
UserAccountsModel *accounts() const;
Q_INVOKABLE NewAccountController *initNewAccountController();
Q_INVOKABLE Status::Onboarding::NewAccountController *initNewAccountController();
Q_INVOKABLE void terminateNewAccountController();
NewAccountController *newAccountController() const;
std::shared_ptr<AccountsServiceInterface> accountsService() const;

View File

@ -4,6 +4,7 @@
#include <ApplicationCore/UserConfiguration.h>
#include <Helpers/helpers.h>
namespace AppCore = Status::ApplicationCore;
@ -43,7 +44,7 @@ void OnboardingModule::initWithUserDataPath(const fs::path &path)
auto result = m_accountsService->init(path);
if(!result)
throw std::runtime_error(std::string("Failed to initialize OnboadingService") + path.string());
m_controller = std::make_shared<OnboardingController>(
m_controller = Helpers::makeSharedQObject<OnboardingController>(
m_accountsService);
emit controllerChanged();
}

View File

@ -8,8 +8,6 @@ project(OnboardingTestHelpers
set(QT_NO_CREATE_VERSIONLESS_FUNCTIONS true)
find_package(GTest REQUIRED)
find_package(Qt6 ${STATUS_QT_VERSION} COMPONENTS Quick Qml REQUIRED)
qt6_standard_project_setup()
@ -47,6 +45,4 @@ target_link_libraries(OnboardingTestHelpers
Status::ApplicationCore
Status::Onboarding
Status::StatusGoQt
GTest::gtest
)

View File

@ -10,9 +10,9 @@
#include <StatusGo/Accounts/Accounts.h>
#include <StatusGo/Accounts/AccountsAPI.h>
#include <QCoreApplication>
#include <Helpers/helpers.h>
#include <gtest/gtest.h>
#include <QCoreApplication>
namespace Testing = Status::Testing;
namespace Onboarding = Status::Onboarding;
@ -49,7 +49,7 @@ ScopedTestAccount::ScopedTestAccount(const std::string &tempTestSubfolderName,
//
// Beware, smartpointer is a requirement
m_onboarding = std::make_shared<Onboarding::OnboardingController>(accountsService);
m_onboarding = Helpers::makeSharedQObject<Onboarding::OnboardingController>(accountsService);
if(m_onboarding->getOpenedAccounts().size() != 0)
throw std::runtime_error("ScopedTestAccount - already have opened account");

View File

@ -7,6 +7,8 @@
#include <Onboarding/Accounts/AccountsService.h>
#include <Onboarding/OnboardingController.h>
#include <Helpers/helpers.h>
#include <gtest/gtest.h>
#include <memory>
@ -49,7 +51,7 @@ std::shared_ptr<AccountsServiceMock> LoginTest::m_accountsServiceMock;
TEST_F(LoginTest, DISABLED_TestLoginController)
{
// Controller hides as a regular class but at runtime it must be a shared pointer; TODO: refactor
auto controller = std::make_shared<Onboarding::OnboardingController>(m_accountsServiceMock);
auto controller = Helpers::makeSharedQObject<Onboarding::OnboardingController>(m_accountsServiceMock);
}
} // namespace

View File

@ -28,9 +28,10 @@ struct Token
ChainID chainId;
};
using TokenPtr = std::shared_ptr<Token>;
using Tokens = std::vector<Token>;
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Token, address, name,symbol,
color, decimals, chainId);
}
}

View File

@ -28,12 +28,13 @@ NetworkConfigurations getEthereumChains(bool onlyEnabled);
/// \note status-go's GetEthereumChains@api.go which calls
/// NetworkManager@client.go -> network.Manager.get()
/// \throws \c CallPrivateRpcError
/// \throws \c CallPrivateRpcError.errorResponse().error with \c "no tokens for this network" in case of missing tokens for the network
/// \throws \c CallPrivateRpcError for general RPC call failure
NetworkConfigurations getEthereumChains(bool onlyEnabled);
/// \note status-go's GetTokens@api.go -> TokenManager.getTokens@token.go
/// \throws \c CallPrivateRpcError
/// \throws \c CallPrivateRpcError with
Tokens getTokens(const ChainID &chainId);
using TokenBalances = std::map<Accounts::EOAddress, std::map<Accounts::EOAddress, BigInt>>;

View File

@ -66,13 +66,17 @@ install(
target_sources(Wallet
PRIVATE
include/Status/Wallet/AccountAssetsController.h
src/AccountAssetsController.cpp
include/Status/Wallet/DerivedWalletAddress.h
src/DerivedWalletAddress.cpp
include/Status/Wallet/NewWalletAccountController.h
src/NewWalletAccountController.cpp
include/Status/Wallet/WalletAccount.h
# Move to Accounts module
include/Status/Wallet/WalletAccount.h
src/WalletAccount.cpp
include/Status/Wallet/WalletAsset.h
src/WalletAsset.cpp
include/Status/Wallet/WalletController.h
src/WalletController.cpp
)

View File

@ -0,0 +1,54 @@
#pragma once
#include "Status/Wallet/WalletAccount.h"
#include "Status/Wallet/WalletAsset.h"
#include <Helpers/QObjectVectorModel.h>
#include <QtQmlIntegration>
namespace Status::Wallet {
/// Controlls asset for an account using hardcoded network and token lists
///
/// \todo add static configuration component to provide networks, tokens and currency list
/// \todo impliement \c AccountsBalanceService to fetch and cache realtime balance (or better implement this in status-go)
/// \todo implement native token fetching
/// \todo double responsibility, split functionality in asset management and balance
class AccountAssetsController: public QObject
{
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("C++ only")
Q_PROPERTY(QAbstractItemModel* assetModel READ assetsModel CONSTANT)
Q_PROPERTY(float totalValue READ totalValue NOTIFY totalValueChanged)
Q_PROPERTY(bool assetsReady READ assetsReady NOTIFY assetsReadyChanged)
public:
AccountAssetsController(WalletAccount* address, QObject* parent = nullptr);
QAbstractItemModel* assetsModel() const;
float totalValue() const;
bool assetsReady() const;
signals:
void totalValueChanged();
void assetsReadyChanged();
private:
void updateBalances();
bool isTokenEnabled(const StatusGo::Wallet::Token& token) const;
WalletAccount* m_address;
const std::vector<QString> m_enabledTokens;
using AssetModel = Helpers::QObjectVectorModel<WalletAsset>;
std::shared_ptr<AssetModel> m_assets;
float m_totalValue{};
bool m_assetsReady{};
};
} // namespace Status::Wallet

View File

@ -2,30 +2,32 @@
#include <StatusGo/Wallet/DerivedAddress.h>
#include <QObject>
#include <QtQmlIntegration>
namespace GoWallet = Status::StatusGo::Wallet;
namespace WalletGo = Status::StatusGo::Wallet;
namespace Status::Wallet {
class DerivedWalletAddress : public QObject
{
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("C++ only")
Q_PROPERTY(QString address READ address CONSTANT)
Q_PROPERTY(bool alreadyCreated READ alreadyCreated CONSTANT)
public:
explicit DerivedWalletAddress(GoWallet::DerivedAddress address, QObject *parent = nullptr);
explicit DerivedWalletAddress(WalletGo::DerivedAddress address, QObject *parent = nullptr);
QString address() const;
const GoWallet::DerivedAddress &data() const { return m_derivedAddress; };
const WalletGo::DerivedAddress &data() const { return m_derivedAddress; };
bool alreadyCreated() const;
private:
const GoWallet::DerivedAddress m_derivedAddress;
const WalletGo::DerivedAddress m_derivedAddress;
};
using DerivedWalletAddressPtr = std::shared_ptr<DerivedWalletAddress>;

View File

@ -5,19 +5,16 @@
#include <Helpers/QObjectVectorModel.h>
#include <QQmlListProperty>
#include <QtQmlIntegration>
class QQmlEngine;
class QJSEngine;
namespace Status::Wallet {
/// \note the folowing values are kept in sync \c selectedDerivedAddress, \c derivedAddressIndex and \c derivationPath
/// \note the following values are kept in sync \c selectedDerivedAddress, \c derivedAddressIndex and \c derivationPath
/// and \c customDerivationPath; \see connascence.io/value
class NewWalletAccountController: public QObject
{
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("C++ only")
Q_PROPERTY(QAbstractListModel* mainAccountsModel READ mainAccountsModel CONSTANT)
@ -35,10 +32,6 @@ public:
/// \note On account creation \c accounts are updated with the newly created wallet account
NewWalletAccountController(std::shared_ptr<AccountsModel> accounts);
/// Called by QML engine to register the instance. QML takes ownership of the instance
static NewWalletAccountController *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine);
QAbstractListModel *mainAccountsModel();
QAbstractItemModel *currentDerivedAddressModel();

View File

@ -2,7 +2,7 @@
#include "Accounts/ChatOrWalletAccount.h"
#include <QObject>
#include <QtQmlIntegration>
namespace GoAccounts = Status::StatusGo::Accounts;
@ -11,9 +11,11 @@ namespace Status::Wallet {
class WalletAccount: public QObject
{
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("C++ only, for now")
Q_PROPERTY(QString name READ name CONSTANT)
Q_PROPERTY(QString address READ address CONSTANT)
Q_PROPERTY(QString address READ strAddress CONSTANT)
Q_PROPERTY(QColor color READ color CONSTANT)
public:
@ -21,7 +23,7 @@ public:
const QString &name() const;
const QString &address() const;
const QString &strAddress() const;
QColor color() const;

View File

@ -0,0 +1,45 @@
#pragma once
#include <StatusGo/Wallet/Token.h>
#include <StatusGo/Wallet/BigInt.h>
#include <QtQmlIntegration>
namespace WalletGo = Status::StatusGo::Wallet;
namespace Status::Wallet {
class WalletAsset : public QObject
{
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("C++ only, for now")
Q_PROPERTY(QString name READ name CONSTANT)
Q_PROPERTY(QString symbol READ symbol CONSTANT)
Q_PROPERTY(QColor color READ color CONSTANT)
Q_PROPERTY(quint64 count READ count CONSTANT)
Q_PROPERTY(float value READ value CONSTANT)
public:
explicit WalletAsset(const WalletGo::TokenPtr token, StatusGo::Wallet::BigInt balance, QObject *parent = nullptr);
const QString name() const;
const QString symbol() const;
const QColor color() const;
quint64 count() const;
float value() const;
private:
const WalletGo::TokenPtr m_token;
// const GoWallet::NativeToken m_nativeToken;
StatusGo::Wallet::BigInt m_balance;
int m_count;
};
}

View File

@ -5,7 +5,6 @@
#include <Helpers/QObjectVectorModel.h>
#include <QQmlListProperty>
#include <QtQmlIntegration>
#include <memory>
@ -16,6 +15,7 @@ class QJSEngine;
namespace Status::Wallet {
class NewWalletAccountController;
class AccountAssetsController;
/// \todo move account creation to its own controller
class WalletController: public QObject
@ -44,6 +44,9 @@ public:
WalletAccount *currentAccount() const;
Q_INVOKABLE void setCurrentAccountIndex(int index);
/// \note caller (QML) takes ownership of the returned instance
Q_INVOKABLE Status::Wallet::AccountAssetsController* createAccountAssetsController(Status::Wallet::WalletAccount* account);
signals:
void currentAccountChanged();

View File

@ -1,14 +1,51 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Status.Wallet
import Status.Containers
Item {
id: root
/// WalletAccount
required property var asset
required property AccountAssetsController assetController
required property WalletAccount account
Label {
anchors.centerIn: parent
text: asset.name
ListView {
id: listView
anchors.fill: parent
model: root.assetController.assetModel
delegate: RowLayout {
required property WalletAsset asset
Label {
text: asset.name
Layout.preferredWidth: listView.width * 0.4
}
RowLayout {
Layout.preferredWidth: listView.width * 0.4
Label {
text: asset.count
}
Label {
text: asset.symbol
}
LayoutSpacer{}
}
RowLayout {
Label {
text: asset.value
}
Label {
//text: asset.currency
}
}
LayoutSpacer{}
}
}
}

View File

@ -1,6 +1,7 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQml
import Status.Wallet
@ -13,6 +14,7 @@ Item {
/// WalletController
required property WalletController controller
readonly property AccountAssetsController currentAssetController: listView.currentItem ? listView.currentItem.assetController : null
ColumnLayout {
anchors.left: leftLine.right
@ -25,7 +27,8 @@ Item {
}
Label {
id: totalValueLabel
text: "" // TODO: Aggregate or API!?
// TODO: source it from the last total cached value of balance service,
text: "-"
}
Label {
text: qsTr("Total value")
@ -37,6 +40,8 @@ Item {
}
ListView {
id: listView
Layout.fillWidth: true
Layout.fillHeight: true
@ -47,10 +52,17 @@ Item {
clip: true
delegate: ItemDelegate {
required property int index
// Enabling type generates 'Writing to "account" broke the binding to the underlying model'
required property var/*WalletAccount*/ account
readonly property AccountAssetsController assetController: account && WalletController.createAccountAssetsController(account)
highlighted: ListView.isCurrentItem
width: ListView.view.width
onClicked: ListView.view.currentIndex = index
contentItem: ColumnLayout {
@ -79,17 +91,19 @@ Item {
elide: Label.ElideRight
}
}
Label {
RowLayout {
Layout.leftMargin: 10
Layout.rightMargin: 10
Layout.bottomMargin: 5
text: "$"
color: "grey"
verticalAlignment: Qt.AlignVCenter
elide: Label.ElideRight
Label {
text: assetController.assetsReady ? assetController.totalValue : "-"
}
Label {
text: "$"
color: "grey"
}
}
}
}
@ -137,13 +151,13 @@ Item {
}
model: ObjectModel {
NewAccountEntry {
title: "New Account"
title: qsTr("New Account")
sourceComponent: NewWalletAccountView {
controller: root.controller.createNewWalletAccountController()
}
}
NewAccountEntry {
title: "Watch Only Account"
title: qsTr("Watch Only Account")
sourceComponent: AddWatchOnlyAccountView {
controller: root.controller.createNewWalletAccountController()
}

View File

@ -11,8 +11,7 @@ import Status.Containers
Item {
id: root
/// NewWalletAccountController
required property var controller
required property NewWalletAccountController controller
signal accountCreated()
signal cancel()

View File

@ -9,7 +9,7 @@ import Status.Containers
Item {
id: root
/// NewWalletAccountController
// Using the type NewWalletAccountController generates "/bin/sh: line 1: 12832 Segmentation fault: 11 qmlcachegen --resource-path /Status/Wallet/qml/Status/Wallet/NewAccount/NewWalletAccountView.qml"
required property var controller
signal accountCreated()
@ -135,7 +135,7 @@ Item {
}
highlighted: derivedAddressCombo.highlightedIndex === index
required property var derivedAddress
required property DerivedWalletAddress derivedAddress
required property int index
}
}
@ -174,7 +174,7 @@ Item {
}
highlighted: derivedFromCombo.highlightedIndex === index
required property var account
required property WalletAccount account
required property int index
}
}

View File

@ -8,17 +8,17 @@ import Status.Wallet
Item {
id: root
/// WalletAccount
required property var asset
required property WalletAccount account
required property AccountAssetsController assetController
ColumnLayout {
anchors.fill: parent
Label {
text: asset.name
text: account.name
}
Label {
text: asset.address
text: account.address
}
TabBar {
id: tabBar
@ -37,6 +37,7 @@ Item {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.margins: 10
currentIndex: tabBar.currentIndex
@ -44,21 +45,24 @@ Item {
clip: true
Loader {
active: SwipeView.isCurrentItem
active: SwipeView.isCurrentItem && root.assetController && root.account
sourceComponent: AssetView {
Layout.fillWidth: true
Layout.fillHeight: true
Rectangle {
anchors.fill: parent
color: "#66000066"
border.width: 1
}
width: swipeView.width
height: swipeView.height
asset: root.asset
assetController: root.assetController
account: root.account
}
}
Loader {
active: SwipeView.isCurrentItem
sourceComponent: Item {
Layout.fillWidth: true
Layout.fillHeight: true
Label {
anchors.centerIn: parent
text: "TODO"

View File

@ -36,7 +36,8 @@ PanelAndContentBase {
Layout.fillWidth: true
Layout.fillHeight: true
asset: WalletController.currentAccount
account: WalletController.currentAccount
assetController: panel.currentAssetController
}
}
}

View File

@ -0,0 +1,100 @@
#include "Status/Wallet/AccountAssetsController.h"
#include <StatusGo/Wallet/WalletApi.h>
#include <StatusGo/Wallet/BigInt.h>
#include <StatusGo/Metadata/api_response.h>
#include <Helpers/helpers.h>
#include <QtConcurrent>
namespace WalletGo = Status::StatusGo::Wallet;
namespace Status::Wallet {
AccountAssetsController::AccountAssetsController(WalletAccount* address, QObject* parent)
: QObject(parent)
, m_address(address)
, m_enabledTokens({u"SNT"_qs, u"ETH"_qs, u"STT"_qs, u"DAI"_qs})
, m_assets(Helpers::makeSharedQObject<AssetModel>("asset"))
{
QtConcurrent::run([this, address]{ updateBalances(); })
.then([this] {
m_assetsReady = true;
emit assetsReadyChanged();
m_totalValue = 0;
for(const auto& asset : m_assets->objects())
m_totalValue += asset->value();
emit totalValueChanged();
})
.onFailed([this] {
emit assetsReadyChanged();
qWarning() << "Unexpected failure while executing updateBalances for account" << m_address->data().address.get();
});
}
void AccountAssetsController::updateBalances()
{
const StatusGo::Accounts::EOAddress& address = m_address->data().address;
if(m_assets->size() > 0)
m_assets->clear();
// TODO: this should be moved to status-go and exposed as "get balances for account and tokens with currency"
std::map<Accounts::EOAddress, StatusGo::Wallet::Token> tokens;
std::vector<WalletGo::ChainID> chainIds;
auto allNets = WalletGo::getEthereumChains(false);
for(const auto &net : allNets) {
if(net.enabled && !net.isTest) {
try {
const auto allTokens = WalletGo::getTokens(net.chainId);
for(const auto& tokenToMove : allTokens) {
if(isTokenEnabled(tokenToMove)) {
auto address = tokenToMove.address;
tokens.emplace(std::move(address), std::move(tokenToMove));
}
}
chainIds.push_back(net.chainId);
}
catch (const StatusGo::CallPrivateRpcError& e) {
// Most probably "no tokens for this network"
if(e.errorResponse().error.message.compare("no tokens for this network") != 0)
qWarning() << "Failed retrieving tokens for network" << net.chainId.get() << "; error" << e.errorResponse().error.message.c_str();
continue;
}
}
}
auto accountBalances = WalletGo::getTokensBalancesForChainIDs(chainIds, { address }, std::move(Helpers::getKeys(tokens)));
if(accountBalances.size() == 1) {
for(const auto& accountAndBalance : accountBalances.begin()->second) {
auto asset = Helpers::makeSharedQObject<WalletAsset>(std::make_shared<WalletGo::Token>(tokens.at(accountAndBalance.first)), accountAndBalance.second);
m_assets->push_back(asset);
}
}
else
qWarning() << "Failed fetching balances for account" << address.get() << "; balances count" << accountBalances.size();
}
bool AccountAssetsController::isTokenEnabled(const StatusGo::Wallet::Token& token) const
{
return find_if(m_enabledTokens.begin(), m_enabledTokens.end(), [&token](const auto& symbol) {
return token.symbol == symbol;
}) != m_enabledTokens.end();
}
QAbstractItemModel* AccountAssetsController::assetsModel() const
{
return m_assets.get();
}
float AccountAssetsController::totalValue() const
{
return m_totalValue;
}
bool AccountAssetsController::assetsReady() const
{
return m_assetsReady;
}
}

View File

@ -2,7 +2,7 @@
namespace Status::Wallet {
DerivedWalletAddress::DerivedWalletAddress(GoWallet::DerivedAddress address, QObject *parent)
DerivedWalletAddress::DerivedWalletAddress(WalletGo::DerivedAddress address, QObject *parent)
: QObject{parent}
, m_derivedAddress{std::move(address)}
{

View File

@ -17,7 +17,7 @@
#include <ranges>
namespace GoAccounts = Status::StatusGo::Accounts;
namespace GoWallet = Status::StatusGo::Wallet;
namespace WalletGo = Status::StatusGo::Wallet;
namespace UtilsSG = Status::StatusGo::Utils;
namespace StatusGo = Status::StatusGo;
@ -31,7 +31,6 @@ NewWalletAccountController::NewWalletAccountController(std::shared_ptr<Helpers::
{
}
QAbstractListModel* NewWalletAccountController::mainAccountsModel()
{
return &m_mainAccounts;
@ -107,7 +106,7 @@ bool NewWalletAccountController::retrieveAndUpdateDerivedAddresses(const QString
auto maxPageCount = static_cast<int>(std::ceil(static_cast<double>(m_maxDerivedAddresses)/static_cast<double>(m_derivedAddressesPageSize)));
std::shared_ptr<DerivedWalletAddress> foundEntry;
while(currentPage <= maxPageCount && foundIndex < 0) {
auto all = GoWallet::getDerivedAddressesForPath(StatusGo::HashedPassword(UtilsSG::hashPassword(password)),
auto all = WalletGo::getDerivedAddressesForPath(StatusGo::HashedPassword(UtilsSG::hashPassword(password)),
derivedFrom->data().derivedFrom.value(),
Status::Constants::General::PathWalletRoot,
m_derivedAddressesPageSize, currentPage);
@ -115,7 +114,7 @@ bool NewWalletAccountController::retrieveAndUpdateDerivedAddresses(const QString
m_derivedAddress.resize(currentIndex + all.size());
for(auto newDerived : all) {
auto newEntry = std::make_shared<DerivedWalletAddress>(std::move(newDerived));
auto newEntry = Helpers::makeSharedQObject<DerivedWalletAddress>(std::move(newDerived));
m_derivedAddress.set(currentIndex, newEntry);
if(foundIndex < 0 && !newEntry->data().alreadyCreated) {
foundIndex = currentIndex;
@ -147,7 +146,7 @@ WalletAccountPtr NewWalletAccountController::findMissingAccount()
return std::none_of(m_accounts->objects().begin(), m_accounts->objects().end(),
[&a](const auto &eA) { return a.address == eA->data().address; });
});
return it != accounts.end() ? std:: make_shared<WalletAccount>(*it) : nullptr;
return it != accounts.end() ? Helpers::makeSharedQObject<WalletAccount>(*it) : nullptr;
}
NewWalletAccountController::AccountsModel::ObjectContainer

View File

@ -13,7 +13,7 @@ const QString &WalletAccount::name() const
return m_data.name;
}
const QString &WalletAccount::address() const
const QString &WalletAccount::strAddress() const
{
return m_data.address.get();
}

View File

@ -0,0 +1,47 @@
#include "WalletAsset.h"
#include <boost/multiprecision/number.hpp>
namespace Status::Wallet {
WalletAsset::WalletAsset(const WalletGo::TokenPtr token, StatusGo::Wallet::BigInt balance, QObject *parent)
: QObject{parent}
, m_token(token)
, m_balance(std::move(balance))
{
}
const QString WalletAsset::name() const
{
return m_token ? m_token->name : u""_qs;
}
const QString WalletAsset::symbol() const
{
return m_token ? m_token->symbol : u""_qs;
}
const QColor WalletAsset::color() const
{
return m_token ? m_token->color : QColor();
}
quint64 WalletAsset::count() const
{
return (m_token)
? static_cast<quint64>(m_balance/boost::multiprecision::pow(WalletGo::BigInt(10), m_token->decimals))
: 0;
}
float WalletAsset::value() const
{
if(m_token) {
const int mantissaDigits = m_token->decimals > 3 ? 3 : 0;
const auto scale = 10 * mantissaDigits;
return static_cast<float>((m_balance*scale)/boost::multiprecision::pow(WalletGo::BigInt(10), m_token->decimals))/scale;
}
else
return 0;
}
}

View File

@ -1,5 +1,7 @@
#include "Status/Wallet/WalletController.h"
#include "NewWalletAccountController.h"
#include "AccountAssetsController.h"
#include <StatusGo/Wallet/WalletApi.h>
@ -16,14 +18,14 @@
#include <QJSEngine>
namespace GoAccounts = Status::StatusGo::Accounts;
namespace GoWallet = Status::StatusGo::Wallet;
namespace WalletGo = Status::StatusGo::Wallet;
namespace UtilsSG = Status::StatusGo::Utils;
namespace StatusGo = Status::StatusGo;
namespace Status::Wallet {
WalletController::WalletController()
: m_accounts(std::make_shared<AccountsModel>(std::move(getWalletAccounts()), "account"))
: m_accounts(Helpers::makeSharedQObject<AccountsModel>(std::move(getWalletAccounts()), "account"))
, m_currentAccount(m_accounts->get(0))
{
}
@ -60,13 +62,18 @@ void WalletController::setCurrentAccountIndex(int index)
emit currentAccountChanged();
}
AccountAssetsController *WalletController::createAccountAssetsController(WalletAccount *account)
{
return new AccountAssetsController(account);
}
std::vector<WalletAccountPtr> WalletController::getWalletAccounts(bool rootWalletAccountsOnly) const
{
auto all = GoAccounts::getAccounts();
std::vector<WalletAccountPtr> result;
for(auto account : all) {
if(!account.isChat && (!rootWalletAccountsOnly || account.isWallet))
result.push_back(std::make_shared<WalletAccount>(std::move(account)));
result.push_back(Helpers::makeSharedQObject<WalletAccount>(std::move(account)));
}
return result;
}

View File

@ -1,5 +1,7 @@
#include <StatusGo/Accounts/AccountsAPI.h>
#include <StatusGo/Wallet/WalletApi.h>
#include <StatusGo/Metadata/api_response.h>
#include <Onboarding/Accounts/AccountsServiceInterface.h>
@ -223,14 +225,14 @@ TEST(WalletApi, TestGetTokensBalancesForChainIDs)
ASSERT_GT(balances.size(), 0);
ASSERT_TRUE(balances.contains(testAddress));
const auto &addressBallance = balances[testAddress];
ASSERT_GT(addressBallance.size(), 0);
const auto &addressBalance = balances[testAddress];
ASSERT_GT(addressBalance.size(), 0);
ASSERT_TRUE(addressBallance.contains(sntMain.address));
ASSERT_EQ(toQString(addressBallance.at(sntMain.address)), "0");
ASSERT_TRUE(addressBalance.contains(sntMain.address));
ASSERT_EQ(toQString(addressBalance.at(sntMain.address)), "0");
ASSERT_TRUE(addressBallance.contains(sntTest.address));
ASSERT_EQ(toQString(addressBallance.at(sntTest.address)), "0");
ASSERT_TRUE(addressBalance.contains(sntTest.address));
ASSERT_EQ(toQString(addressBalance.at(sntTest.address)), "0");
}
TEST(WalletApi, TestGetTokensBalancesForChainIDs_WatchOnlyAccount)
@ -267,11 +269,14 @@ TEST(WalletApi, TestGetTokensBalancesForChainIDs_WatchOnlyAccount)
ASSERT_GT(balances.size(), 0);
ASSERT_TRUE(balances.contains(newAccount.address));
const auto &addressBallance = balances[newAccount.address];
ASSERT_GT(addressBallance.size(), 0);
const auto &addressBalance = balances[newAccount.address];
ASSERT_GT(addressBalance.size(), 0);
ASSERT_TRUE(addressBallance.contains(sntMain.address));
ASSERT_GT(addressBallance.at(sntMain.address), 0);
ASSERT_TRUE(addressBalance.contains(sntMain.address));
ASSERT_GT(addressBalance.at(sntMain.address), 0);
}
// TODO
// "{"jsonrpc":"2.0","id":0,"error":{"code":-32000,"message":"no tokens for this network"}}"
} // namespace