feat(Wallet) cache fetched balance history to DB for efficiency
- Bump status-go head that include the required specific changes - fetch token balance (native or ERC20) and cache historical token quantity data - fetch FIAT currency - Extend presentation layer (NIM and QML) to account for API changes - Remove timed request and other optimizations from the time of fetching balance history every time instead of querying cache - Add C++ integration debugging tests and update network chain configuration (outdated) Closes: #8175
This commit is contained in:
parent
7ebe5488bd
commit
3bb667bb7a
|
@ -60,8 +60,13 @@ ctest --test-dir ./build
|
|||
|
||||
### Build with QtCreator
|
||||
|
||||
If go is installed with brew use the following configuration otherwise adapt the configuration.
|
||||
Go to QtCreator's preferences navigate to Environment -> System -> Environment -> Change and paste
|
||||
Go to QtCreator's preferences navigate to Environment -> System -> Environment -> Change add GOBIN to the PATH
|
||||
|
||||
### MacOS instructions
|
||||
|
||||
If go is installed with `brew` use the following configuration otherwise adapt it to your environment. Also this will allow access to conan if installed by brew
|
||||
|
||||
Use this in the Environment section of the QtCreator's preferences:
|
||||
|
||||
```ini
|
||||
GOBIN=${GOPATH}/bin
|
||||
|
|
|
@ -359,6 +359,7 @@ QJsonArray getNodes(const QJsonObject& fleet, const QString& nodeType)
|
|||
return result;
|
||||
}
|
||||
|
||||
/// Mirrors and in sync with getDefaultNodeConfig@service.nim
|
||||
QJsonObject AccountsService::getDefaultNodeConfig(const QString& installationId) const
|
||||
{
|
||||
try
|
||||
|
@ -391,22 +392,14 @@ QJsonObject AccountsService::getDefaultNodeConfig(const QString& installationId)
|
|||
|
||||
QJsonObject nodeConfigJson = QJsonDocument::fromJson(nodeConfigJsonStr.toUtf8()).object();
|
||||
|
||||
QJsonObject clusterConfig = nodeConfigJson["ClusterConfig"].toObject();
|
||||
QJsonObject fleetsJson = QJsonDocument::fromJson(fleetJson.toUtf8()).object()["fleets"].toObject();
|
||||
auto clusterConfig = nodeConfigJson["ClusterConfig"].toObject();
|
||||
auto fleetsJson = QJsonDocument::fromJson(fleetJson.toUtf8()).object()["fleets"].toObject();
|
||||
auto fleet = fleetsJson[Constants::Fleet::Prod].toObject();
|
||||
clusterConfig["Fleet"] = Constants::Fleet::Prod;
|
||||
clusterConfig["BootNodes"] = getNodes(fleet, Constants::FleetNodes::Bootnodes);
|
||||
clusterConfig["TrustedMailServers"] = getNodes(fleet, Constants::FleetNodes::Mailservers);
|
||||
clusterConfig["StaticNodes"] = getNodes(fleet, Constants::FleetNodes::Whisper);
|
||||
clusterConfig["RendezvousNodes"] = getNodes(fleet, Constants::FleetNodes::Rendezvous);
|
||||
clusterConfig["DiscV5BootstrapNodes"] = QJsonArray();
|
||||
clusterConfig["RelayNodes"] = getNodes(fleet, Constants::FleetNodes::Waku);
|
||||
clusterConfig["StoreNodes"] = getNodes(fleet, Constants::FleetNodes::Waku);
|
||||
clusterConfig["FilterNodes"] = getNodes(fleet, Constants::FleetNodes::Waku);
|
||||
clusterConfig["LightpushNodes"] = getNodes(fleet, Constants::FleetNodes::Waku);
|
||||
|
||||
nodeConfigJson["ClusterConfig"] = clusterConfig;
|
||||
|
||||
nodeConfigJson["NetworkId"] = defaultNetworksJson[0].toObject()["chainId"];
|
||||
nodeConfigJson["DataDir"] = "ethereum";
|
||||
auto upstreamConfig = nodeConfigJson["UpstreamConfig"].toObject();
|
||||
|
@ -417,6 +410,27 @@ QJsonObject AccountsService::getDefaultNodeConfig(const QString& installationId)
|
|||
shhextConfig["InstallationID"] = installationId;
|
||||
nodeConfigJson["ShhextConfig"] = shhextConfig;
|
||||
nodeConfigJson["Networks"] = defaultNetworksJson;
|
||||
nodeConfigJson["NoDiscovery"] = true;
|
||||
nodeConfigJson["Rendezvous"] = false;
|
||||
QJsonArray dnsDiscoveryURL = {"enrtree://AOGECG2SPND25EEFMAJ5WF3KSGJNSGV356DSTL2YVLLZWIV6SAYBM@prod.nodes.status.im"};
|
||||
clusterConfig["WakuNodes"] = dnsDiscoveryURL;
|
||||
clusterConfig["DiscV5BootstrapNodes"] = dnsDiscoveryURL;
|
||||
|
||||
auto wakuv2Config = nodeConfigJson["WakuV2Config"].toObject();
|
||||
wakuv2Config["EnableDiscV5"] = true;
|
||||
wakuv2Config["DiscoveryLimit"] = 20;
|
||||
wakuv2Config["Rendezvous"] = true;
|
||||
wakuv2Config["Enabled"] = true;
|
||||
|
||||
wakuv2Config["Enabled"] = false;
|
||||
nodeConfigJson["WakuV2Config"] = wakuv2Config;
|
||||
|
||||
clusterConfig["RelayNodes"] = getNodes(fleet, Constants::FleetNodes::Waku);
|
||||
clusterConfig["StoreNodes"] = getNodes(fleet, Constants::FleetNodes::Waku);
|
||||
clusterConfig["FilterNodes"] = getNodes(fleet, Constants::FleetNodes::Waku);
|
||||
clusterConfig["LightpushNodes"] = getNodes(fleet, Constants::FleetNodes::Waku);
|
||||
|
||||
nodeConfigJson["ClusterConfig"] = clusterConfig;
|
||||
|
||||
nodeConfigJson["KeyStoreDir"] = toQString(fs::relative(m_keyStoreDir, m_statusgoDataDir));
|
||||
return nodeConfigJson;
|
||||
|
|
|
@ -9,4 +9,9 @@ const EventType Events::RecentHistoryReady{"recent-history-ready"};
|
|||
const EventType Events::FetchingHistoryError{"fetching-history-error"};
|
||||
const EventType Events::NonArchivalNodeDetected{"non-archival-node-detected"};
|
||||
|
||||
} // namespace Status::StatusGo::Wallet
|
||||
const EventType Events::WalletTickReload{"wallet-tick-reload"};
|
||||
const EventType Events::EventBalanceHistoryUpdateStarted{"wallet-balance-history-update-started"};
|
||||
const EventType Events::EventBalanceHistoryUpdateFinished{"wallet-balance-history-update-finished"};
|
||||
const EventType Events::EventBalanceHistoryUpdateFinishedWithError{"wallet-balance-history-update-finished-with-error"};
|
||||
|
||||
} // namespace Status::StatusGo::Wallet::Transfer
|
||||
|
|
|
@ -22,6 +22,11 @@ struct Events
|
|||
static const EventType RecentHistoryReady;
|
||||
static const EventType FetchingHistoryError;
|
||||
static const EventType NonArchivalNodeDetected;
|
||||
|
||||
static const EventType WalletTickReload;
|
||||
static const EventType EventBalanceHistoryUpdateStarted;
|
||||
static const EventType EventBalanceHistoryUpdateFinished;
|
||||
static const EventType EventBalanceHistoryUpdateFinishedWithError;
|
||||
};
|
||||
|
||||
/// \see status-go's Event@events.go in services/wallet/transfer module
|
||||
|
@ -36,4 +41,4 @@ struct Event
|
|||
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Event, type, blockNumber, accounts, message);
|
||||
|
||||
} // namespace Status
|
||||
} // namespace Status::StatusGo::Wallet::Transfer
|
||||
|
|
|
@ -108,10 +108,12 @@ TokenBalances getTokensBalancesForChainIDs(const std::vector<ChainID>& chainIds,
|
|||
return resultData;
|
||||
}
|
||||
|
||||
std::vector<TokenBalanceHistory>
|
||||
getBalanceHistory(const ChainID& chainID, Accounts::EOAddress account, BalanceHistoryTimeInterval timeInterval)
|
||||
std::vector<TokenBalanceHistory> getBalanceHistory(const std::vector<ChainID>& chainIds,
|
||||
Accounts::EOAddress account,
|
||||
const QString& currency,
|
||||
BalanceHistoryTimeInterval timeInterval)
|
||||
{
|
||||
std::vector<json> params = {chainID, account, timeInterval};
|
||||
std::vector<json> params = {chainIds, account, currency, timeInterval};
|
||||
json inputJson = {{"jsonrpc", "2.0"}, {"method", "wallet_getBalanceHistory"}, {"params", params}};
|
||||
|
||||
auto result = Utils::statusGoCallPrivateRPC(inputJson.dump().c_str());
|
||||
|
@ -121,6 +123,18 @@ getBalanceHistory(const ChainID& chainID, Accounts::EOAddress account, BalanceHi
|
|||
return resultJson.get<CallPrivateRpcResponse>().result;
|
||||
}
|
||||
|
||||
bool updateBalanceHistoryForAllEnabledNetworks()
|
||||
{
|
||||
json inputJson = {
|
||||
{"jsonrpc", "2.0"}, {"method", "wallet_updateBalanceHistoryForAllEnabledNetworks"}, {"params", {}}};
|
||||
|
||||
auto result = Utils::statusGoCallPrivateRPC(inputJson.dump().c_str());
|
||||
const auto resultJson = json::parse(result);
|
||||
checkPrivateRpcCallResultAndReportError(resultJson);
|
||||
|
||||
return resultJson.get<CallPrivateRpcResponse>().result;
|
||||
}
|
||||
|
||||
void checkRecentHistory(const std::vector<Accounts::EOAddress>& accounts)
|
||||
{
|
||||
std::vector<json> params = {accounts};
|
||||
|
@ -130,4 +144,29 @@ void checkRecentHistory(const std::vector<Accounts::EOAddress>& accounts)
|
|||
checkPrivateRpcCallResultAndReportError(resultJson);
|
||||
}
|
||||
|
||||
void startWallet()
|
||||
{
|
||||
json inputJson = {{"jsonrpc", "2.0"}, {"method", "wallet_startWallet"}, {"params", {}}};
|
||||
auto result = Utils::statusGoCallPrivateRPC(inputJson.dump().c_str());
|
||||
const auto resultJson = json::parse(result);
|
||||
checkPrivateRpcCallResultAndReportError(resultJson);
|
||||
}
|
||||
|
||||
void stopWallet()
|
||||
{
|
||||
json inputJson = {{"jsonrpc", "2.0"}, {"method", "wallet_stopWallet"}, {"params", {}}};
|
||||
auto result = Utils::statusGoCallPrivateRPC(inputJson.dump().c_str());
|
||||
const auto resultJson = json::parse(result);
|
||||
checkPrivateRpcCallResultAndReportError(resultJson);
|
||||
}
|
||||
|
||||
void updateVisibleTokens(std::vector<QString> symbols)
|
||||
{
|
||||
json inputJson = {
|
||||
{"jsonrpc", "2.0"}, {"method", "wallet_updateVisibleTokens"}, {"params", std::vector<json>{symbols}}};
|
||||
auto result = Utils::statusGoCallPrivateRPC(inputJson.dump().c_str());
|
||||
const auto resultJson = json::parse(result);
|
||||
checkPrivateRpcCallResultAndReportError(resultJson);
|
||||
}
|
||||
|
||||
} // namespace Status::StatusGo::Wallet
|
||||
|
|
|
@ -66,9 +66,10 @@ struct TokenBalanceHistory
|
|||
{
|
||||
BigInt value;
|
||||
QDateTime time;
|
||||
BigInt blockNumber;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(TokenBalanceHistory, value, time)
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(TokenBalanceHistory, value, time, blockNumber)
|
||||
|
||||
/// @see status-go's services/wallet/transfer/controller.go BalanceHistoryTimeInterval
|
||||
enum BalanceHistoryTimeInterval
|
||||
|
@ -86,11 +87,30 @@ enum BalanceHistoryTimeInterval
|
|||
/// \see checkRecentHistory
|
||||
/// \note status-go's API -> GetBalanceHistory@api.go
|
||||
/// \throws \c CallPrivateRpcError
|
||||
std::vector<TokenBalanceHistory>
|
||||
getBalanceHistory(const ChainID& chainID, Accounts::EOAddress account, BalanceHistoryTimeInterval timeInterval);
|
||||
std::vector<TokenBalanceHistory> getBalanceHistory(const std::vector<ChainID>& chainIds,
|
||||
Accounts::EOAddress account,
|
||||
const QString& currency,
|
||||
BalanceHistoryTimeInterval timeInterval);
|
||||
|
||||
/// \note status-go's API -> updateBalanceHistoryForAllEnabledNetworks@api.go
|
||||
///
|
||||
/// \throws \c CallPrivateRpcError
|
||||
bool updateBalanceHistoryForAllEnabledNetworks();
|
||||
|
||||
/// \note status-go's API -> CheckRecentHistory@api.go
|
||||
/// \throws \c CallPrivateRpcError
|
||||
void checkRecentHistory(const std::vector<Accounts::EOAddress>& accounts);
|
||||
|
||||
/// \note status-go's API -> StartWallet@api.go
|
||||
/// \throws \c CallPrivateRpcError
|
||||
void startWallet();
|
||||
|
||||
/// \note status-go's API -> StopWallet@api.go
|
||||
/// \throws \c CallPrivateRpcError
|
||||
void stopWallet();
|
||||
|
||||
/// \note status-go's API -> UpdateVisibleTokens@api.go
|
||||
/// \throws \c CallPrivateRpcError
|
||||
void updateVisibleTokens(std::vector<QString> symbols);
|
||||
|
||||
} // namespace Status::StatusGo::Wallet
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[
|
||||
{
|
||||
"chainId": 1,
|
||||
"chainName": "Mainnet",
|
||||
"chainName": "Ethereum Mainnet",
|
||||
"rpcUrl": "https://mainnet.infura.io/v3/%INFURA_TOKEN_RESOLVED%",
|
||||
"blockExplorerUrl": "https://etherscan.io/",
|
||||
"iconUrl": "network/Network=Ethereum",
|
||||
|
@ -15,49 +15,19 @@
|
|||
"enabled": true
|
||||
},
|
||||
{
|
||||
"chainId": 3,
|
||||
"chainName": "Ropsten",
|
||||
"rpcUrl": "https://ropsten.infura.io/v3%INFURA_TOKEN_RESOLVED%",
|
||||
"blockExplorerUrl": "https://ropsten.etherscan.io/",
|
||||
"iconUrl": "network/Network=Tetnet",
|
||||
"chainColor": "#939BA1",
|
||||
"shortName": "ropEth",
|
||||
"nativeCurrencyName": "Ether",
|
||||
"nativeCurrencySymbol": "ETH",
|
||||
"nativeCurrencyDecimals": 18,
|
||||
"isTest": true,
|
||||
"layer": 1,
|
||||
"enabled": false
|
||||
},
|
||||
{
|
||||
"chainId": 4,
|
||||
"chainName": "Rinkeby",
|
||||
"rpcUrl": "https://rinkeby.infura.io/v3/%INFURA_TOKEN_RESOLVED%",
|
||||
"blockExplorerUrl": "https://rinkeby.etherscan.io/",
|
||||
"iconUrl": "network/Network=Tetnet",
|
||||
"chainColor": "#939BA1",
|
||||
"shortName": "rinEth",
|
||||
"nativeCurrencyName": "Ether",
|
||||
"nativeCurrencySymbol": "ETH",
|
||||
"nativeCurrencyDecimals": 18,
|
||||
"isTest": true,
|
||||
"layer": 1,
|
||||
"enabled": false
|
||||
},
|
||||
{
|
||||
"chainId": 5,
|
||||
"chainName": "Goerli",
|
||||
"rpcUrl": "http://goerli.blockscout.com/",
|
||||
"blockExplorerUrl": "https://goerli.etherscan.io/",
|
||||
"iconUrl": "network/Network=Tetnet",
|
||||
"chainColor": "#939BA1",
|
||||
"shortName": "goeEth",
|
||||
"nativeCurrencyName": "Ether",
|
||||
"nativeCurrencySymbol": "ETH",
|
||||
"nativeCurrencyDecimals": 18,
|
||||
"isTest": true,
|
||||
"layer": 1,
|
||||
"enabled": true
|
||||
"chainId": 5,
|
||||
"chainName": "Goerli",
|
||||
"rpcUrl": "https://goerli.infura.io/v3/%INFURA_TOKEN_RESOLVED%",
|
||||
"blockExplorerUrl": "https://goerli.etherscan.io/",
|
||||
"iconUrl": "network/Network=Testnet",
|
||||
"chainColor": "#939BA1",
|
||||
"shortName": "goeEth",
|
||||
"nativeCurrencyName": "Ether",
|
||||
"nativeCurrencySymbol": "ETH",
|
||||
"nativeCurrencyDecimals": 18,
|
||||
"isTest": true,
|
||||
"layer": 1,
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"chainId": 10,
|
||||
|
@ -75,13 +45,13 @@
|
|||
"enabled": true
|
||||
},
|
||||
{
|
||||
"chainId": 69,
|
||||
"chainName": "Optimism Kovan",
|
||||
"rpcUrl": "https://optimism-kovan.infura.io/v3/%INFURA_TOKEN_RESOLVED%",
|
||||
"blockExplorerUrl": "https://kovan-optimistic.etherscan.io",
|
||||
"iconUrl": "network/Network=Tetnet",
|
||||
"chainId": 420,
|
||||
"chainName": "Optimism Goerli Testnet",
|
||||
"rpcUrl": "https://optimism-goerli.infura.io/v3/%INFURA_TOKEN_RESOLVED%",
|
||||
"blockExplorerUrl": "https://goerli-optimism.etherscan.io/",
|
||||
"iconUrl": "network/Network=Testnet",
|
||||
"chainColor": "#939BA1",
|
||||
"shortName": "kovOpt",
|
||||
"shortName": "goerOpt",
|
||||
"nativeCurrencyName": "Ether",
|
||||
"nativeCurrencySymbol": "ETH",
|
||||
"nativeCurrencyDecimals": 18,
|
||||
|
@ -105,11 +75,11 @@
|
|||
"enabled": true
|
||||
},
|
||||
{
|
||||
"chainId": 421611,
|
||||
"chainName": "Arbitrum Rinkeby",
|
||||
"rpcUrl": "https://arbitrum-rinkeby.infura.io/v3/%INFURA_TOKEN_RESOLVED%",
|
||||
"blockExplorerUrl": " https://testnet.arbiscan.io",
|
||||
"iconUrl": "network/Network=Tetnet",
|
||||
"chainId": 421613,
|
||||
"chainName": "Arbitrum Goerli",
|
||||
"rpcUrl": "https://arbitrum-goerli.infura.io/v3/%INFURA_TOKEN_RESOLVED%",
|
||||
"blockExplorerUrl": "https://goerli.arbiscan.io/",
|
||||
"iconUrl": "network/Network=Testnet",
|
||||
"chainColor": "#939BA1",
|
||||
"shortName": "rinArb",
|
||||
"nativeCurrencyName": "Ether",
|
||||
|
@ -119,4 +89,4 @@
|
|||
"layer": 2,
|
||||
"enabled": false
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
|
@ -111,6 +111,35 @@
|
|||
"node-01.gc-us-central1-a.go-waku.test": "/dns4/node-01.gc-us-central1-a.go-waku.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAmPz63Xc6AuVkDeujz7YeZta18rcdau3Y1BzaxKAfDrBqz"
|
||||
}
|
||||
},
|
||||
"status.prod": {
|
||||
"tcp/p2p/waku": {
|
||||
"node-01.ac-cn-hongkong-c.status.prod": "/dns4/node-01.ac-cn-hongkong-c.status.prod.statusim.net/tcp/30303/p2p/16Uiu2HAkvEZgh3KLwhLwXg95e5ojM8XykJ4Kxi2T7hk22rnA7pJC",
|
||||
"node-01.do-ams3.status.prod": "/dns4/node-01.do-ams3.status.prod.statusim.net/tcp/30303/p2p/16Uiu2HAm6HZZr7aToTvEBPpiys4UxajCTU97zj5v7RNR2gbniy1D",
|
||||
"node-01.gc-us-central1-a.status.prod": "/dns4/node-01.gc-us-central1-a.status.prod.statusim.net/tcp/30303/p2p/16Uiu2HAkwBp8T6G77kQXSNMnxgaMky1JeyML5yqoTHRM8dbeCBNb",
|
||||
"node-02.ac-cn-hongkong-c.status.prod": "/dns4/node-02.ac-cn-hongkong-c.status.prod.statusim.net/tcp/30303/p2p/16Uiu2HAmFy8BrJhCEmCYrUfBdSNkrPw6VHExtv4rRp1DSBnCPgx8",
|
||||
"node-02.do-ams3.status.prod": "/dns4/node-02.do-ams3.status.prod.statusim.net/tcp/30303/p2p/16Uiu2HAmSve7tR5YZugpskMv2dmJAsMUKmfWYEKRXNUxRaTCnsXV",
|
||||
"node-02.gc-us-central1-a.status.prod": "/dns4/node-02.gc-us-central1-a.status.prod.statusim.net/tcp/30303/p2p/16Uiu2HAmDQugwDHM3YeUp86iGjrUvbdw3JPRgikC7YoGBsT2ymMg"
|
||||
}
|
||||
},
|
||||
"status.test": {
|
||||
"tcp/p2p/waku": {
|
||||
"node-01.ac-cn-hongkong-c.status.test": "/dns4/node-01.ac-cn-hongkong-c.status.test.statusim.net/tcp/30303/p2p/16Uiu2HAm2BjXxCp1sYFJQKpLLbPbwd5juxbsYofu3TsS3auvT9Yi",
|
||||
"node-01.do-ams3.status.test": "/dns4/node-01.do-ams3.status.test.statusim.net/tcp/30303/p2p/16Uiu2HAkukebeXjTQ9QDBeNDWuGfbaSg79wkkhK4vPocLgR6QFDf",
|
||||
"node-01.gc-us-central1-a.status.test": "/dns4/node-01.gc-us-central1-a.status.test.statusim.net/tcp/30303/p2p/16Uiu2HAmGDX3iAFox93PupVYaHa88kULGqMpJ7AEHGwj3jbMtt76"
|
||||
}
|
||||
},
|
||||
"waku.connect": {
|
||||
"tcp/p2p/waku": {
|
||||
"nim-01.ac-cn-hongkong-c.waku.connect": "/ip4/47.242.185.35/tcp/30303/p2p/16Uiu2HAm75XUMGev2Ti74G3wUzhyxCtbaDKVWzNwbq3tn5WfzRd4",
|
||||
"nim-01.do-ams3.waku.connect": "/ip4/206.189.242.0/tcp/30303/p2p/16Uiu2HAm9VLETt1xBwDAwfKxj2XvAZDw73Bn4HQf11U26JGDxqZD",
|
||||
"nim-01.gc-us-central1-a.waku.connect": "/ip4/35.193.87.35/tcp/30303/p2p/16Uiu2HAmMi8xaj9W22a67shGg5wtw1nZDNtfrTPHkgKA5Uhvnvbn"
|
||||
},
|
||||
"wss/p2p/waku": {
|
||||
"nim-01.ac-cn-hongkong-c.waku.connect": "/dns4/nim-01.ac-cn-hongkong-c.waku.connect.statusim.net/tcp/443/wss/p2p/16Uiu2HAm75XUMGev2Ti74G3wUzhyxCtbaDKVWzNwbq3tn5WfzRd4",
|
||||
"nim-01.do-ams3.waku.connect": "/dns4/nim-01.do-ams3.waku.connect.statusim.net/tcp/443/wss/p2p/16Uiu2HAm9VLETt1xBwDAwfKxj2XvAZDw73Bn4HQf11U26JGDxqZD",
|
||||
"nim-01.gc-us-central1-a.waku.connect": "/dns4/nim-01.gc-us-central1-a.waku.connect.statusim.net/tcp/443/wss/p2p/16Uiu2HAmMi8xaj9W22a67shGg5wtw1nZDNtfrTPHkgKA5Uhvnvbn"
|
||||
}
|
||||
},
|
||||
"wakuv2.prod": {
|
||||
"waku": {
|
||||
"node-01.ac-cn-hongkong-c.wakuv2.prod": "/ip4/8.210.222.231/tcp/30303/p2p/16Uiu2HAm4v86W3bmT1BiH6oSPzcsSr24iDQpSN5Qa992BCjjwgrD",
|
||||
|
@ -125,19 +154,19 @@
|
|||
},
|
||||
"wakuv2.test": {
|
||||
"waku": {
|
||||
"node-01.ac-cn-hongkong-c.wakuv2.test": "/ip4/47.242.210.73/tcp/30303/p2p/16Uiu2HAkvWiyFsgRhuJEb9JfjYxEkoHLgnUQmr1N5mKWnYjxYRVm",
|
||||
"node-01.do-ams3.wakuv2.test": "/ip4/134.209.139.210/tcp/30303/p2p/16Uiu2HAmPLe7Mzm8TsYUubgCAW1aJoeFScxrLj8ppHFivPo97bUZ",
|
||||
"node-01.gc-us-central1-a.wakuv2.test": "/ip4/104.154.239.128/tcp/30303/p2p/16Uiu2HAmJb2e28qLXxT5kZxVUUoJt72EMzNGXB47Rxx5hw3q4YjS"
|
||||
"node-01.ac-cn-hongkong-c.wakuv2.test": "/dns4/node-01.ac-cn-hongkong-c.wakuv2.test.statusim.net/tcp/30303/p2p/16Uiu2HAkvWiyFsgRhuJEb9JfjYxEkoHLgnUQmr1N5mKWnYjxYRVm",
|
||||
"node-01.do-ams3.wakuv2.test": "/dns4/node-01.do-ams3.wakuv2.test.statusim.net/tcp/30303/p2p/16Uiu2HAmPLe7Mzm8TsYUubgCAW1aJoeFScxrLj8ppHFivPo97bUZ",
|
||||
"node-01.gc-us-central1-a.wakuv2.test": "/dns4/node-01.gc-us-central1-a.wakuv2.test.statusim.net/tcp/30303/p2p/16Uiu2HAmJb2e28qLXxT5kZxVUUoJt72EMzNGXB47Rxx5hw3q4YjS"
|
||||
},
|
||||
"waku-websocket": {
|
||||
"node-01.ac-cn-hongkong-c.wakuv2.test": "/dns4/node-01.ac-cn-hongkong-c.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAkvWiyFsgRhuJEb9JfjYxEkoHLgnUQmr1N5mKWnYjxYRVm",
|
||||
"node-01.do-ams3.wakuv2.test": "/dns4/node-01.do-ams3.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAmPLe7Mzm8TsYUubgCAW1aJoeFScxrLj8ppHFivPo97bUZ",
|
||||
"node-01.gc-us-central1-a.wakuv2.test": "/dns4/node-01.gc-us-central1-a.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAmJb2e28qLXxT5kZxVUUoJt72EMzNGXB47Rxx5hw3q4YjS"
|
||||
"node-01.ac-cn-hongkong-c.wakuv2.test": "/dns4/node-01.ac-cn-hongkong-c.wakuv2.test.statusim.net/tcp/8000/wss/p2p/16Uiu2HAkvWiyFsgRhuJEb9JfjYxEkoHLgnUQmr1N5mKWnYjxYRVm",
|
||||
"node-01.do-ams3.wakuv2.test": "/dns4/node-01.do-ams3.wakuv2.test.statusim.net/tcp/8000/wss/p2p/16Uiu2HAmPLe7Mzm8TsYUubgCAW1aJoeFScxrLj8ppHFivPo97bUZ",
|
||||
"node-01.gc-us-central1-a.wakuv2.test": "/dns4/node-01.gc-us-central1-a.wakuv2.test.statusim.net/tcp/8000/wss/p2p/16Uiu2HAmJb2e28qLXxT5kZxVUUoJt72EMzNGXB47Rxx5hw3q4YjS"
|
||||
}
|
||||
}
|
||||
},
|
||||
"meta": {
|
||||
"hostname": "node-01.do-ams3.sites.misc",
|
||||
"timestamp": "2021-10-19T00:00:15.465044"
|
||||
"timestamp": "2022-03-10T11:32:40.427357",
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
"KeyStoreDir": "./keystore",
|
||||
"IPFSDir": "./ipfs",
|
||||
"LogEnabled": true,
|
||||
"LogFile": "./geth.log",
|
||||
"LogFile": "geth.log",
|
||||
"LogLevel": "INFO",
|
||||
"MailserversConfig": {
|
||||
"Enabled": true
|
||||
|
@ -76,9 +76,6 @@
|
|||
"Enabled": true,
|
||||
"OpenseaAPIKey": ""
|
||||
},
|
||||
"EnsConfig": {
|
||||
"Enabled": true
|
||||
},
|
||||
"Networks": [],
|
||||
"TorrentConfig": {
|
||||
"Enabled": false,
|
||||
|
|
|
@ -33,11 +33,15 @@ proc init*(self: Controller) =
|
|||
let args = TokenHistoricalDataArgs(e)
|
||||
self.delegate.tokenHistoricalDataResolved(args.result)
|
||||
|
||||
self.events.on(SIGNAL_BALANCE_HISTORY_DATA_READY) do(e:Args):
|
||||
let args = TokenBalanceHistoryDataArgs(e)
|
||||
self.delegate.tokenBalanceHistoryDataResolved(args.result)
|
||||
|
||||
method findTokenSymbolByAddress*(self: Controller, address: string): string =
|
||||
return self.walletAccountService.findTokenSymbolByAddress(address)
|
||||
|
||||
method getHistoricalDataForToken*(self: Controller, symbol: string, currency: string, range: int) =
|
||||
self.tokenService.getHistoricalDataForToken(symbol, currency, range)
|
||||
|
||||
method fetchHistoricalBalanceForTokenAsJson*(self: Controller, address: string, symbol: string, timeIntervalEnum: int) =
|
||||
self.tokenService.fetchHistoricalBalanceForTokenAsJson(address, symbol, BalanceHistoryTimeInterval(timeIntervalEnum))
|
||||
method fetchHistoricalBalanceForTokenAsJson*(self: Controller, address: string, tokenSymbol: string, currencySymbol: string, timeIntervalEnum: int) =
|
||||
self.tokenService.fetchHistoricalBalanceForTokenAsJson(address, tokenSymbol, currencySymbol, BalanceHistoryTimeInterval(timeIntervalEnum))
|
|
@ -20,7 +20,7 @@ method getHistoricalDataForToken*(self: AccessInterface, symbol: string, currenc
|
|||
method tokenHistoricalDataResolved*(self: AccessInterface, tokenDetails: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method fetchHistoricalBalanceForTokenAsJson*(self: AccessInterface, address: string, symbol: string, timeIntervalEnum: int) {.base.} =
|
||||
method fetchHistoricalBalanceForTokenAsJson*(self: AccessInterface, address: string, tokenSymbol: string, currencySymbol: string, timeIntervalEnum: int) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method tokenBalanceHistoryDataResolved*(self: AccessInterface, balanceHistoryJson: string) {.base.} =
|
||||
|
|
|
@ -63,8 +63,8 @@ method tokenHistoricalDataResolved*(self: Module, tokenDetails: string) =
|
|||
self.view.setTokenHistoricalDataReady(tokenDetails)
|
||||
|
||||
|
||||
method fetchHistoricalBalanceForTokenAsJson*(self: Module, address: string, symbol: string, timeIntervalEnum: int) =
|
||||
self.controller.fetchHistoricalBalanceForTokenAsJson(address, symbol, timeIntervalEnum)
|
||||
method fetchHistoricalBalanceForTokenAsJson*(self: Module, address: string, tokenSymbol: string, currencySymbol: string, timeIntervalEnum: int) =
|
||||
self.controller.fetchHistoricalBalanceForTokenAsJson(address, tokenSymbol, currencySymbol,timeIntervalEnum)
|
||||
|
||||
method tokenBalanceHistoryDataResolved*(self: Module, balanceHistoryJson: string) =
|
||||
self.view.setTokenBalanceHistoryDataReady(balanceHistoryJson)
|
||||
|
|
|
@ -65,9 +65,9 @@ QtObject:
|
|||
self.setMarketHistoryIsLoading(false)
|
||||
self.tokenHistoricalDataReady(tokenDetails)
|
||||
|
||||
proc fetchHistoricalBalanceForTokenAsJson*(self: View, address: string, symbol: string, timeIntervalEnum: int) {.slot.} =
|
||||
proc fetchHistoricalBalanceForTokenAsJson*(self: View, address: string, tokenSymbol: string, currencySymbol: string, timeIntervalEnum: int) {.slot.} =
|
||||
self.setBalanceHistoryIsLoading(true)
|
||||
self.delegate.fetchHistoricalBalanceForTokenAsJson(address, symbol, timeIntervalEnum)
|
||||
self.delegate.fetchHistoricalBalanceForTokenAsJson(address, tokenSymbol, currencySymbol, timeIntervalEnum)
|
||||
|
||||
proc tokenBalanceHistoryDataReady*(self: View, balanceHistoryJson: string) {.signal.}
|
||||
|
||||
|
|
|
@ -66,9 +66,10 @@ type
|
|||
|
||||
type
|
||||
GetTokenBalanceHistoryDataTaskArg = ref object of QObjectTaskArg
|
||||
chainId: int
|
||||
chainIds: seq[int]
|
||||
address: string
|
||||
symbol: string
|
||||
tokenSymbol: string
|
||||
currencySymbol: string
|
||||
timeInterval: BalanceHistoryTimeInterval
|
||||
|
||||
const getTokenBalanceHistoryDataTask*: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||
|
@ -76,12 +77,13 @@ const getTokenBalanceHistoryDataTask*: Task = proc(argEncoded: string) {.gcsafe,
|
|||
var response = %*{}
|
||||
try:
|
||||
# status-go time intervals are starting from 1
|
||||
response = backend.getBalanceHistory(arg.chainId, arg.address, int(arg.timeInterval) + 1).result
|
||||
response = backend.getBalanceHistory(arg.chainIds, arg.address, arg.tokenSymbol, arg.currencySymbol, int(arg.timeInterval) + 1).result
|
||||
|
||||
let output = %* {
|
||||
"chainId": arg.chainId,
|
||||
"chainIds": arg.chainIds,
|
||||
"address": arg.address,
|
||||
"symbol": arg.symbol,
|
||||
"tokenSymbol": arg.tokenSymbol,
|
||||
"currencySymbol": arg.currencySymbol,
|
||||
"timeInterval": int(arg.timeInterval),
|
||||
"historicalData": response
|
||||
}
|
||||
|
@ -90,10 +92,11 @@ const getTokenBalanceHistoryDataTask*: Task = proc(argEncoded: string) {.gcsafe,
|
|||
return
|
||||
except Exception as e:
|
||||
let output = %* {
|
||||
"chainId": arg.chainId,
|
||||
"chainIds": arg.chainIds,
|
||||
"address": arg.address,
|
||||
"symbol": arg.symbol,
|
||||
"tokenSymbol": arg.tokenSymbol,
|
||||
"currencySymbol": arg.currencySymbol,
|
||||
"timeInterval": int(arg.timeInterval),
|
||||
"error": "Balance history value not found",
|
||||
"error": e.msg,
|
||||
}
|
||||
arg.finish(output)
|
|
@ -117,7 +117,7 @@ QtObject:
|
|||
return token
|
||||
except Exception as e:
|
||||
error "Error finding token by symbol", msg = e.msg
|
||||
|
||||
|
||||
proc findTokenByAddress*(self: Service, network: NetworkDto, address: Address): TokenDto =
|
||||
for token in self.tokens[network.chainId]:
|
||||
if token.address == address:
|
||||
|
@ -242,31 +242,43 @@ QtObject:
|
|||
)
|
||||
self.threadpool.start(arg)
|
||||
|
||||
# Historical Balance
|
||||
# Callback to process the response of fetchHistoricalBalanceForTokenAsJson call
|
||||
proc tokenBalanceHistoryDataResolved*(self: Service, response: string) {.slot.} =
|
||||
# TODO
|
||||
let responseObj = response.parseJson
|
||||
if (responseObj.kind != JObject):
|
||||
info "blance history response is not a json object"
|
||||
warn "blance history response is not a json object"
|
||||
return
|
||||
|
||||
self.events.emit(SIGNAL_BALANCE_HISTORY_DATA_READY, TokenBalanceHistoryDataArgs(
|
||||
result: response
|
||||
))
|
||||
|
||||
proc fetchHistoricalBalanceForTokenAsJson*(self: Service, address: string, symbol: string, timeInterval: BalanceHistoryTimeInterval) =
|
||||
proc fetchHistoricalBalanceForTokenAsJson*(self: Service, address: string, tokenSymbol: string, currencySymbol: string, timeInterval: BalanceHistoryTimeInterval) =
|
||||
# create an empty list of chain ids
|
||||
var chainIds: seq[int] = @[]
|
||||
let networks = self.networkService.getNetworks()
|
||||
for network in networks:
|
||||
if network.enabled and network.nativeCurrencySymbol == symbol:
|
||||
let arg = GetTokenBalanceHistoryDataTaskArg(
|
||||
tptr: cast[ByteAddress](getTokenBalanceHistoryDataTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: "tokenBalanceHistoryDataResolved",
|
||||
chainId: network.chainId,
|
||||
address: address,
|
||||
symbol: symbol,
|
||||
timeInterval: timeInterval
|
||||
)
|
||||
self.threadpool.start(arg)
|
||||
return
|
||||
error "faild to find a network with the symbol", symbol
|
||||
if network.enabled:
|
||||
if network.nativeCurrencySymbol == tokenSymbol:
|
||||
chainIds.add(network.chainId)
|
||||
else:
|
||||
for token in self.tokens[network.chainId]:
|
||||
if token.symbol == tokenSymbol:
|
||||
chainIds.add(network.chainId)
|
||||
|
||||
if chainIds.len == 0:
|
||||
error "faild to find a network with the symbol", tokenSymbol
|
||||
return
|
||||
|
||||
let arg = GetTokenBalanceHistoryDataTaskArg(
|
||||
tptr: cast[ByteAddress](getTokenBalanceHistoryDataTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: "tokenBalanceHistoryDataResolved",
|
||||
chainIds: chainIds,
|
||||
address: address,
|
||||
tokenSymbol: tokenSymbol,
|
||||
currencySymbol: currencySymbol,
|
||||
timeInterval: timeInterval
|
||||
)
|
||||
self.threadpool.start(arg)
|
||||
return
|
|
@ -75,8 +75,6 @@ type AccountDeleted* = ref object of Args
|
|||
|
||||
type CurrencyUpdated = ref object of Args
|
||||
|
||||
type TokenVisibilityToggled = ref object of Args
|
||||
|
||||
type NetwordkEnabledToggled = ref object of Args
|
||||
|
||||
type WalletAccountUpdated* = ref object of Args
|
||||
|
@ -237,7 +235,7 @@ QtObject:
|
|||
self.checkRecentHistory()
|
||||
of "wallet-tick-check-connected":
|
||||
self.checkConnected()
|
||||
|
||||
|
||||
proc getWalletAccount*(self: Service, accountIndex: int): WalletAccountDto =
|
||||
let accounts = self.getWalletAccounts()
|
||||
if accountIndex < 0 or accountIndex >= accounts.len:
|
||||
|
@ -259,7 +257,7 @@ QtObject:
|
|||
proc checkConnected(self: Service) =
|
||||
if(not singletonInstance.localAccountSensitiveSettings.getIsWalletEnabled()):
|
||||
return
|
||||
|
||||
|
||||
try:
|
||||
# TODO: add event for UI (Waiting for design)
|
||||
discard backend.checkConnected()
|
||||
|
@ -272,7 +270,7 @@ QtObject:
|
|||
proc checkRecentHistory*(self: Service) =
|
||||
if(not singletonInstance.localAccountSensitiveSettings.getIsWalletEnabled()):
|
||||
return
|
||||
|
||||
|
||||
try:
|
||||
let addresses = self.getWalletAccounts().map(a => a.address)
|
||||
let chainIds = self.networkService.getNetworks().map(a => a.chainId)
|
||||
|
@ -523,6 +521,9 @@ QtObject:
|
|||
|
||||
proc onAllTokensBuilt*(self: Service, response: string) {.slot.} =
|
||||
try:
|
||||
var visibleSymbols: seq[string]
|
||||
let chainIds = self.networkService.getNetworks().map(n => n.chainId)
|
||||
|
||||
let responseObj = response.parseJson
|
||||
var data = TokensPerAccountArgs()
|
||||
if responseObj.kind == JObject:
|
||||
|
@ -534,7 +535,13 @@ QtObject:
|
|||
data.accountsTokens[wAddress] = tokens
|
||||
self.storeTokensForAccount(wAddress, tokens)
|
||||
self.tokenService.updateTokenPrices(tokens) # For efficiency. Will be removed when token info fetching gets moved to the tokenService
|
||||
# Gather symbol for visible tokens
|
||||
for token in tokens:
|
||||
if token.getVisibleForNetworkWithPositiveBalance(chainIds) and find(visibleSymbols, token.symbol) == -1:
|
||||
visibleSymbols.add(token.symbol)
|
||||
self.events.emit(SIGNAL_WALLET_ACCOUNT_TOKENS_REBUILT, data)
|
||||
|
||||
discard backend.updateVisibleTokens(visibleSymbols)
|
||||
except Exception as e:
|
||||
error "error: ", procName="onAllTokensBuilt", errName = e.name, errDesription = e.msg
|
||||
|
||||
|
|
|
@ -97,6 +97,9 @@ rpc(getWalletToken, "wallet"):
|
|||
rpc(startWallet, "wallet"):
|
||||
discard
|
||||
|
||||
rpc(updateVisibleTokens, "wallet"):
|
||||
symbols: seq[string]
|
||||
|
||||
rpc(getTransactionEstimatedTime, "wallet"):
|
||||
chainId: int
|
||||
maxFeePerGas: float
|
||||
|
@ -288,8 +291,10 @@ rpc(getName, "ens"):
|
|||
address: string
|
||||
|
||||
rpc(getBalanceHistory, "wallet"):
|
||||
chainId: int
|
||||
chainIds: seq[int]
|
||||
address: string
|
||||
tokenSymbol: string
|
||||
currencySymbol: string
|
||||
timeInterval: int
|
||||
|
||||
rpc(isCurrencyFiat, "wallet"):
|
||||
|
|
|
@ -207,7 +207,7 @@ TEST(WalletApi, TestGetTokens)
|
|||
auto networks = Wallet::getEthereumChains(false);
|
||||
ASSERT_GT(networks.size(), 0);
|
||||
auto mainNetIt =
|
||||
std::find_if(networks.begin(), networks.end(), [](const auto& n) { return n.chainName == "Mainnet"; });
|
||||
std::find_if(networks.begin(), networks.end(), [](const auto& n) { return n.chainName == "Ethereum Mainnet"; });
|
||||
ASSERT_NE(mainNetIt, networks.end());
|
||||
const auto& mainNet = *mainNetIt;
|
||||
|
||||
|
@ -228,7 +228,7 @@ TEST(WalletApi, TestGetTokensBalancesForChainIDs)
|
|||
ASSERT_GT(networks.size(), 1);
|
||||
|
||||
auto mainNetIt =
|
||||
std::find_if(networks.begin(), networks.end(), [](const auto& n) { return n.chainName == "Mainnet"; });
|
||||
std::find_if(networks.begin(), networks.end(), [](const auto& n) { return n.chainName == "Ethereum Mainnet"; });
|
||||
ASSERT_NE(mainNetIt, networks.end());
|
||||
const auto& mainNet = *mainNetIt;
|
||||
|
||||
|
@ -265,22 +265,69 @@ TEST(WalletApi, TestGetTokensBalancesForChainIDs)
|
|||
ASSERT_EQ(toQString(addressBalance.at(sntTest.address)), "0");
|
||||
}
|
||||
|
||||
struct TestNetwork
|
||||
{
|
||||
QString name;
|
||||
bool isTest;
|
||||
};
|
||||
|
||||
struct TestParams
|
||||
{
|
||||
Accounts::EOAddress walletAddress;
|
||||
std::vector<TestNetwork> networks;
|
||||
QString token;
|
||||
QString newTestAccountName;
|
||||
};
|
||||
|
||||
static const std::vector<TestParams> allTestParams{
|
||||
// [0] - Goerli ERC20 token test account set
|
||||
TestParams{Accounts::EOAddress("0x586e3bd2c40b0f243162ea563a1f43ae9ef25ef9"),
|
||||
{{QString("Goerli"), true}},
|
||||
QString("USDC"),
|
||||
u"test_watch_only-name"_qs},
|
||||
// [1] - Main net ERC20 token test account set
|
||||
TestParams{Accounts::EOAddress("0xae0c364acb9b105766fea91cfa5aaea31a1821c1"),
|
||||
{{QString("Ethereum Mainnet"), false}},
|
||||
QString("SNT"),
|
||||
u"test_watch_only-name"_qs},
|
||||
// [2] - Main net native token account set
|
||||
TestParams{Accounts::EOAddress("0x0182e671dfd5f7d21b13714cbe9f92d26b59eeb9"),
|
||||
{{QString("Ethereum Mainnet"), false}},
|
||||
QString("USDC"),
|
||||
u"test_watch_only-name"_qs},
|
||||
// [3] - Main net ERC20 test account set
|
||||
TestParams{Accounts::EOAddress("0x473780deAF4a2Ac070BBbA936B0cdefe7F267dFc"),
|
||||
{{QString("Ethereum Mainnet"), false}},
|
||||
QString("ETH"),
|
||||
u"test_watch_only-name"_qs},
|
||||
// [4] - Arbitrum Goerli native token test account set
|
||||
TestParams{Accounts::EOAddress("0xE2d622C817878dA5143bBE06866ca8E35273Ba8a"),
|
||||
{{QString("Arbitrum Goerli"), true}, {QString("Goerli"), true}},
|
||||
QString("ETH"),
|
||||
u"test_watch_only-name"_qs},
|
||||
};
|
||||
|
||||
TestParams getTestParams()
|
||||
{
|
||||
return allTestParams[4];
|
||||
}
|
||||
|
||||
TEST(WalletApi, TestGetTokensBalancesForChainIDs_WatchOnlyAccount)
|
||||
{
|
||||
ScopedTestAccount testAccount(test_info_->name());
|
||||
|
||||
const auto newTestAccountName = u"test_watch_only-name"_qs;
|
||||
const auto params = getTestParams();
|
||||
|
||||
Accounts::addAccountWatch(Accounts::EOAddress("0xdb5ac1a559b02e12f29fc0ec0e37be8e046def49"),
|
||||
newTestAccountName,
|
||||
params.newTestAccountName,
|
||||
QColor("fuchsia"),
|
||||
u""_qs);
|
||||
const auto updatedAccounts = Accounts::getAccounts();
|
||||
ASSERT_EQ(updatedAccounts.size(), 3);
|
||||
|
||||
const auto newAccountIt =
|
||||
std::find_if(updatedAccounts.begin(), updatedAccounts.end(), [&newTestAccountName](const auto& a) {
|
||||
return a.name == newTestAccountName;
|
||||
});
|
||||
const auto newAccountIt = std::find_if(updatedAccounts.begin(), updatedAccounts.end(), [¶ms](const auto& a) {
|
||||
return a.name == params.newTestAccountName;
|
||||
});
|
||||
ASSERT_NE(newAccountIt, updatedAccounts.end());
|
||||
const auto& newAccount = *newAccountIt;
|
||||
|
||||
|
@ -288,7 +335,7 @@ TEST(WalletApi, TestGetTokensBalancesForChainIDs_WatchOnlyAccount)
|
|||
ASSERT_GT(networks.size(), 1);
|
||||
|
||||
auto mainNetIt =
|
||||
std::find_if(networks.begin(), networks.end(), [](const auto& n) { return n.chainName == "Mainnet"; });
|
||||
std::find_if(networks.begin(), networks.end(), [](const auto& n) { return n.chainName == "Ethereum Mainnet"; });
|
||||
ASSERT_NE(mainNetIt, networks.end());
|
||||
const auto& mainNet = *mainNetIt;
|
||||
|
||||
|
@ -309,24 +356,24 @@ TEST(WalletApi, TestGetTokensBalancesForChainIDs_WatchOnlyAccount)
|
|||
ASSERT_GT(addressBalance.at(sntMain.address), 0);
|
||||
}
|
||||
|
||||
// TODO: this is a debugging test. Augment it with local Ganache environment to have a reliable test
|
||||
// TODO: this is a debugging test. Augment it with local Ganache environment to have a reliable integration test
|
||||
TEST(WalletApi, TestCheckRecentHistory)
|
||||
{
|
||||
ScopedTestAccount testAccount(test_info_->name());
|
||||
|
||||
const auto params = getTestParams();
|
||||
|
||||
// Add watch account
|
||||
const auto newTestAccountName = u"test_watch_only-name"_qs;
|
||||
Accounts::addAccountWatch(Accounts::EOAddress("0xe74E17D586227691Cb7b64ed78b1b7B14828B034"),
|
||||
newTestAccountName,
|
||||
params.newTestAccountName,
|
||||
QColor("fuchsia"),
|
||||
u""_qs);
|
||||
const auto updatedAccounts = Accounts::getAccounts();
|
||||
ASSERT_EQ(updatedAccounts.size(), 3);
|
||||
|
||||
const auto newAccountIt =
|
||||
std::find_if(updatedAccounts.begin(), updatedAccounts.end(), [newTestAccountName](const auto& a) {
|
||||
return a.name == newTestAccountName;
|
||||
});
|
||||
const auto newAccountIt = std::find_if(updatedAccounts.begin(), updatedAccounts.end(), [¶ms](const auto& a) {
|
||||
return a.name == params.newTestAccountName;
|
||||
});
|
||||
ASSERT_NE(newAccountIt, updatedAccounts.end());
|
||||
const auto& newAccount = *newAccountIt;
|
||||
|
||||
|
@ -352,33 +399,44 @@ TEST(WalletApi, TestCheckRecentHistory)
|
|||
ASSERT_TRUE(historyReady);
|
||||
}
|
||||
|
||||
// TODO: this is a debugging test. Augment it with local Ganache environment to have a reliable test
|
||||
// TODO: this is a debugging test. Augment it with local Ganache environment to have a reliable integration test
|
||||
TEST(WalletApi, TestGetBalanceHistory)
|
||||
{
|
||||
ScopedTestAccount testAccount(test_info_->name());
|
||||
|
||||
// Add watch account
|
||||
const auto newTestAccountName = u"test_watch_only-name"_qs;
|
||||
Accounts::addAccountWatch(Accounts::EOAddress("0x473780deAF4a2Ac070BBbA936B0cdefe7F267dFc"),
|
||||
newTestAccountName,
|
||||
QColor("fuchsia"),
|
||||
u""_qs);
|
||||
const auto params = getTestParams();
|
||||
|
||||
Accounts::addAccountWatch(params.walletAddress, params.newTestAccountName, QColor("fuchsia"), u""_qs);
|
||||
const auto updatedAccounts = Accounts::getAccounts();
|
||||
ASSERT_EQ(updatedAccounts.size(), 3);
|
||||
|
||||
auto networks = Wallet::getEthereumChains(false);
|
||||
ASSERT_GT(networks.size(), 0);
|
||||
auto mainNetIt =
|
||||
std::find_if(networks.begin(), networks.end(), [](const auto& n) { return n.chainName == "Mainnet"; });
|
||||
ASSERT_NE(mainNetIt, networks.end());
|
||||
const auto& mainNet = *mainNetIt;
|
||||
|
||||
auto tokens = Wallet::getTokens(mainNet.chainId);
|
||||
|
||||
const auto newAccountIt =
|
||||
std::find_if(updatedAccounts.begin(), updatedAccounts.end(), [newTestAccountName](const auto& a) {
|
||||
return a.name == newTestAccountName;
|
||||
std::vector<Wallet::ChainID> chainIDs;
|
||||
for(const auto& net : params.networks)
|
||||
{
|
||||
auto netIt = std::find_if(networks.begin(), networks.end(), [&net](const auto& n) {
|
||||
return n.chainName == net.name && n.isTest == net.isTest;
|
||||
});
|
||||
ASSERT_NE(netIt, networks.end());
|
||||
chainIDs.push_back(netIt->chainId);
|
||||
|
||||
auto nativeIt = std::find_if(networks.begin(), networks.end(), [¶ms](const auto& n) {
|
||||
return n.nativeCurrencySymbol == params.token;
|
||||
});
|
||||
if(nativeIt == networks.end())
|
||||
{
|
||||
auto tokens = Wallet::getTokens(netIt->chainId);
|
||||
auto tokenIt = std::find_if(
|
||||
tokens.begin(), tokens.end(), [¶ms](const auto& t) { return t.symbol == params.token; });
|
||||
ASSERT_NE(tokenIt, tokens.end());
|
||||
}
|
||||
}
|
||||
|
||||
const auto newAccountIt = std::find_if(updatedAccounts.begin(), updatedAccounts.end(), [¶ms](const auto& a) {
|
||||
return a.name == params.newTestAccountName;
|
||||
});
|
||||
ASSERT_NE(newAccountIt, updatedAccounts.end());
|
||||
const auto& newAccount = *newAccountIt;
|
||||
|
||||
|
@ -395,11 +453,59 @@ TEST(WalletApi, TestGetBalanceHistory)
|
|||
{Wallet::BalanceHistoryTimeInterval::BalanceHistory1Year, "1Y"},
|
||||
{Wallet::BalanceHistoryTimeInterval::BalanceHistoryAllTime, "All"}};
|
||||
|
||||
// Fetch first and watch for finished signal of newAccount.address
|
||||
int bhStartedReceivedCount = 0;
|
||||
int bhEndedReceivedCount = 0;
|
||||
int bhErrorReceivedCount = 0;
|
||||
bool targetAccountDone = false;
|
||||
QObject::connect(
|
||||
StatusGo::SignalsManager::instance(),
|
||||
&StatusGo::SignalsManager::wallet,
|
||||
testAccount.app(),
|
||||
[&targetAccountDone, &bhStartedReceivedCount, &bhEndedReceivedCount, &bhErrorReceivedCount, &newAccount](
|
||||
QSharedPointer<StatusGo::EventData> data) {
|
||||
Wallet::Transfer::Event event = data->eventInfo();
|
||||
if(event.type == Wallet::Transfer::Events::EventBalanceHistoryUpdateStarted)
|
||||
{
|
||||
bhStartedReceivedCount++;
|
||||
}
|
||||
else if(event.type == Wallet::Transfer::Events::EventBalanceHistoryUpdateFinished)
|
||||
{
|
||||
bhEndedReceivedCount++;
|
||||
if(event.accounts)
|
||||
{
|
||||
auto found = std::find_if(event.accounts.value().begin(),
|
||||
event.accounts.value().end(),
|
||||
[&event, &newAccount](const auto& a) { return a == newAccount.address; });
|
||||
if(found != event.accounts.value().end())
|
||||
{
|
||||
targetAccountDone = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(event.type == Wallet::Transfer::Events::EventBalanceHistoryUpdateFinishedWithError)
|
||||
{
|
||||
bhErrorReceivedCount++;
|
||||
}
|
||||
});
|
||||
|
||||
std::vector<QString> symbols{params.token};
|
||||
Wallet::updateVisibleTokens(symbols);
|
||||
|
||||
Wallet::startWallet();
|
||||
|
||||
testAccount.processMessages(2400000, [&targetAccountDone, &bhEndedReceivedCount, &bhErrorReceivedCount]() {
|
||||
return !targetAccountDone && bhEndedReceivedCount < 2 && bhErrorReceivedCount < 1;
|
||||
});
|
||||
ASSERT_GT(bhStartedReceivedCount, 0);
|
||||
ASSERT_EQ(bhErrorReceivedCount, 0);
|
||||
ASSERT_GT(bhEndedReceivedCount, 0);
|
||||
ASSERT_TRUE(targetAccountDone);
|
||||
|
||||
for(const auto& historyInterval : testIntervals)
|
||||
{
|
||||
// TODO: next `mainNet.nativeCurrencySymbol`, later `tokens.symbol`
|
||||
auto balanceHistory = Wallet::getBalanceHistory(mainNet.chainId, newAccount.address, historyInterval);
|
||||
ASSERT_TRUE(balanceHistory.size() > 0);
|
||||
auto balanceHistory = Wallet::getBalanceHistory(chainIDs, newAccount.address, params.token, historyInterval);
|
||||
ASSERT_GT(balanceHistory.size(), 0);
|
||||
|
||||
auto weiToEth = [](const StatusGo::Wallet::BigInt& wei) -> double {
|
||||
StatusGo::Wallet::BigInt q; // wei / eth
|
||||
|
@ -413,19 +519,145 @@ TEST(WalletApi, TestGetBalanceHistory)
|
|||
return q.convert_to<double>() + (qSzabos.convert_to<double>() / ((weiD / szaboD).convert_to<double>()));
|
||||
};
|
||||
|
||||
QFile file(QString("/tmp/balance_history-%s.csv").arg(testIntervalsStrs[historyInterval]));
|
||||
auto fileInfo = QFileInfo(
|
||||
QString("/tmp/StatusTests/balance/balance_history-%1.csv").arg(testIntervalsStrs[historyInterval]));
|
||||
QFile file(fileInfo.absoluteFilePath());
|
||||
QDir().mkpath(fileInfo.absolutePath());
|
||||
if(file.open(QIODevice::WriteOnly | QIODevice::Text))
|
||||
{
|
||||
QTextStream out(&file);
|
||||
out << "Balance, Timestamp" << Qt::endl;
|
||||
out << "Balance, Timestamp, Block Number, Formatted Date" << Qt::endl;
|
||||
for(int i = 0; i < balanceHistory.size(); ++i)
|
||||
{
|
||||
out << weiToEth(balanceHistory[i].value) << "," << balanceHistory[i].time.toSecsSinceEpoch()
|
||||
<< Qt::endl;
|
||||
out << weiToEth(balanceHistory[i].value) << "," << balanceHistory[i].time.toSecsSinceEpoch() << ","
|
||||
<< balanceHistory[i].blockNumber.str().c_str() << ","
|
||||
<< balanceHistory[i].time.toString("dd.MM.yyyy hh:mm:ss") << Qt::endl;
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: this is a debugging test. Augment it with local Ganache environment to have a reliable integration test
|
||||
TEST(WalletApi, TestStartWallet)
|
||||
{
|
||||
ScopedTestAccount testAccount(test_info_->name());
|
||||
|
||||
auto params = getTestParams();
|
||||
|
||||
// Add watch account
|
||||
Accounts::addAccountWatch(Accounts::EOAddress("0xF38D2CD3C6Ad02dD6f8B68E0A7b2f959819954b6"),
|
||||
params.newTestAccountName,
|
||||
QColor("fuchsia"),
|
||||
u""_qs);
|
||||
const auto updatedAccounts = Accounts::getAccounts();
|
||||
ASSERT_EQ(updatedAccounts.size(), 3);
|
||||
|
||||
int bhStartedReceivedCount = 0;
|
||||
int bhEndedReceivedCount = 0;
|
||||
int bhErrorReceivedCount = 0;
|
||||
QObject::connect(StatusGo::SignalsManager::instance(),
|
||||
&StatusGo::SignalsManager::wallet,
|
||||
testAccount.app(),
|
||||
[&bhStartedReceivedCount, &bhEndedReceivedCount, &bhErrorReceivedCount](
|
||||
QSharedPointer<StatusGo::EventData> data) {
|
||||
Wallet::Transfer::Event event = data->eventInfo();
|
||||
if(event.type == Wallet::Transfer::Events::EventBalanceHistoryUpdateStarted)
|
||||
{
|
||||
bhStartedReceivedCount++;
|
||||
}
|
||||
else if(event.type == Wallet::Transfer::Events::EventBalanceHistoryUpdateFinished)
|
||||
{
|
||||
bhEndedReceivedCount++;
|
||||
}
|
||||
else if(event.type == Wallet::Transfer::Events::EventBalanceHistoryUpdateFinishedWithError)
|
||||
{
|
||||
bhErrorReceivedCount++;
|
||||
}
|
||||
});
|
||||
|
||||
std::vector<QString> symbols{params.token};
|
||||
Wallet::updateVisibleTokens(symbols);
|
||||
|
||||
Wallet::startWallet();
|
||||
|
||||
testAccount.processMessages(240000, [&bhEndedReceivedCount, &bhErrorReceivedCount]() {
|
||||
return bhEndedReceivedCount < 2 && bhErrorReceivedCount < 1;
|
||||
});
|
||||
ASSERT_EQ(bhStartedReceivedCount, 2);
|
||||
ASSERT_EQ(bhErrorReceivedCount, 0);
|
||||
ASSERT_EQ(bhEndedReceivedCount, 2);
|
||||
}
|
||||
|
||||
// TODO: this is a debugging test. Augment it with local Ganache environment to have a reliable integration test
|
||||
TEST(WalletApi, TestStopBalanceHistory)
|
||||
{
|
||||
ScopedTestAccount testAccount(test_info_->name());
|
||||
|
||||
const auto updatedAccounts = Accounts::getAccounts();
|
||||
ASSERT_EQ(updatedAccounts.size(), 2);
|
||||
|
||||
int bhStartedReceivedCount = 0;
|
||||
int bhEndedReceivedCount = 0;
|
||||
int bhErrorReceivedCount = 0;
|
||||
QString stopMessage;
|
||||
QObject::connect(StatusGo::SignalsManager::instance(),
|
||||
&StatusGo::SignalsManager::wallet,
|
||||
testAccount.app(),
|
||||
[&stopMessage, &bhStartedReceivedCount, &bhEndedReceivedCount, &bhErrorReceivedCount](
|
||||
QSharedPointer<StatusGo::EventData> data) {
|
||||
Wallet::Transfer::Event event = data->eventInfo();
|
||||
if(event.type == Wallet::Transfer::Events::EventBalanceHistoryUpdateStarted)
|
||||
{
|
||||
bhStartedReceivedCount++;
|
||||
}
|
||||
else if(event.type == Wallet::Transfer::Events::EventBalanceHistoryUpdateFinished)
|
||||
{
|
||||
stopMessage = event.message;
|
||||
bhEndedReceivedCount++;
|
||||
}
|
||||
else if(event.type == Wallet::Transfer::Events::EventBalanceHistoryUpdateFinishedWithError)
|
||||
{
|
||||
bhErrorReceivedCount++;
|
||||
}
|
||||
});
|
||||
Wallet::updateVisibleTokens({QString("ETH")});
|
||||
|
||||
Wallet::startWallet();
|
||||
|
||||
bool stopCalled = false;
|
||||
testAccount.processMessages(5000,
|
||||
[&stopCalled, &bhStartedReceivedCount, &bhEndedReceivedCount, &bhErrorReceivedCount]() {
|
||||
if(bhStartedReceivedCount == 1 && !stopCalled)
|
||||
{
|
||||
stopCalled = true;
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
Wallet::stopWallet();
|
||||
}
|
||||
return bhEndedReceivedCount < 1 && bhErrorReceivedCount < 1;
|
||||
});
|
||||
ASSERT_EQ(bhStartedReceivedCount, 1);
|
||||
ASSERT_EQ(bhErrorReceivedCount, 0);
|
||||
ASSERT_EQ(bhEndedReceivedCount, 1);
|
||||
ASSERT_EQ("Service canceled", stopMessage);
|
||||
|
||||
stopMessage = "";
|
||||
|
||||
// Do an empty run
|
||||
Wallet::updateVisibleTokens({});
|
||||
|
||||
Wallet::stopWallet();
|
||||
|
||||
stopCalled = false;
|
||||
testAccount.processMessages(1000,
|
||||
[&stopCalled, &bhStartedReceivedCount, &bhEndedReceivedCount, &bhErrorReceivedCount]() {
|
||||
return bhEndedReceivedCount < 2 && bhErrorReceivedCount < 2;
|
||||
});
|
||||
|
||||
ASSERT_EQ(bhStartedReceivedCount, 2);
|
||||
ASSERT_EQ(bhErrorReceivedCount, 0);
|
||||
ASSERT_EQ(bhEndedReceivedCount, 2);
|
||||
ASSERT_EQ("", stopMessage);
|
||||
}
|
||||
|
||||
} // namespace Status::Testing
|
||||
|
|
|
@ -10,7 +10,9 @@ QtObject {
|
|||
if (Number.isInteger(num))
|
||||
return 0
|
||||
|
||||
return num.toString().split('.')[1].length
|
||||
let parts = num.toString().split('.')
|
||||
// Decimal trick doesn't work for numbers represented in scientific notation, hence the hardcoded fallback
|
||||
return (parts.length > 1 && parts[1].indexOf("e") == -1) ? parts[1].length : 2
|
||||
}
|
||||
|
||||
function stripTrailingZeroes(numStr, locale) {
|
||||
|
|
|
@ -20,18 +20,12 @@ import shared.stores 1.0
|
|||
Item {
|
||||
id: root
|
||||
|
||||
property var token
|
||||
property var token: {}
|
||||
/*required*/ property string address: ""
|
||||
|
||||
function createStore(address) {
|
||||
return balanceHistoryComponent.createObject(null, {address: address})
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
property var marketValueStore : RootStore.marketValueStore
|
||||
// TODO: Should be temporary until non native tokens are supported by balance history
|
||||
property bool isNativeToken: typeof token !== "undefined" && token ? token.symbol === "ETH" : false
|
||||
}
|
||||
|
||||
Connections {
|
||||
|
@ -115,11 +109,7 @@ Item {
|
|||
|
||||
graphsModel: [
|
||||
{text: qsTr("Price"), enabled: true, id: AssetsDetailView.GraphType.Price},
|
||||
{
|
||||
text: qsTr("Balance"),
|
||||
enabled: false, // TODO: Enable after adding ECR20 token support and DB cache. Current prototype implementation works only for d.isNativeToken
|
||||
id: AssetsDetailView.GraphType.Balance
|
||||
},
|
||||
{text: qsTr("Balance"), enabled: true, id: AssetsDetailView.GraphType.Balance},
|
||||
]
|
||||
defaultTimeRangeIndexShown: ChartStoreBase.TimeRange.All
|
||||
timeRangeModel: dataReady() && selectedStore.timeRangeTabsModel
|
||||
|
@ -129,11 +119,7 @@ Item {
|
|||
}
|
||||
|
||||
if(graphDetail.selectedGraphType === AssetsDetailView.GraphType.Balance) {
|
||||
let selectedTimeRangeEnum = balanceStore.timeRangeStrToEnum(graphDetail.selectedTimeRange)
|
||||
if(balanceStore.isTimeToRequest(selectedTimeRangeEnum)) {
|
||||
RootStore.fetchHistoricalBalanceForTokenAsJson(root.address, token.symbol, selectedTimeRangeEnum)
|
||||
balanceStore.updateRequestTime(selectedTimeRangeEnum)
|
||||
}
|
||||
graphDetail.updateBalanceStore()
|
||||
}
|
||||
|
||||
if(!isTimeRange) {
|
||||
|
@ -241,17 +227,33 @@ Item {
|
|||
active: RootStore.marketHistoryIsLoading
|
||||
}
|
||||
|
||||
function updateBalanceStore() {
|
||||
let selectedTimeRangeEnum = balanceStore.timeRangeStrToEnum(graphDetail.selectedTimeRange)
|
||||
|
||||
let currencySymbol = RootStore.currencyStore.currentCurrency
|
||||
if(!balanceStore.hasData(root.address, token.symbol, currencySymbol, selectedTimeRangeEnum)) {
|
||||
RootStore.fetchHistoricalBalanceForTokenAsJson(root.address, token.symbol, currencySymbol, selectedTimeRangeEnum)
|
||||
}
|
||||
}
|
||||
|
||||
TokenBalanceHistoryStore {
|
||||
id: balanceStore
|
||||
|
||||
address: root.address
|
||||
|
||||
onNewDataReady: (timeRange) => {
|
||||
let selectedTimeRange = timeRangeStrToEnum(graphDetail.selectedTimeRange)
|
||||
if (timeRange === selectedTimeRange && address === root.address) {
|
||||
onNewDataReady: (address, tokenSymbol, currencySymbol, timeRange) => {
|
||||
if (timeRange === timeRangeStrToEnum(graphDetail.selectedTimeRange)) {
|
||||
chart.updateToNewData()
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
function onAddressChanged() { graphDetail.updateBalanceStore() }
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: token
|
||||
function onSymbolChanged() { graphDetail.updateBalanceStore() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,11 +15,11 @@ Item {
|
|||
}
|
||||
|
||||
readonly property var timeRangeTabsModel: [
|
||||
{text: qsTr("7D"), enabled: true, timeRange: ChartStoreBase.TimeRange.Weekly},
|
||||
{text: qsTr("1M"), enabled: true, timeRange: ChartStoreBase.TimeRange.Monthly},
|
||||
{text: qsTr("6M"), enabled: true, timeRange: ChartStoreBase.TimeRange.HalfYearly},
|
||||
{text: qsTr("1Y"), enabled: true, timeRange: ChartStoreBase.TimeRange.Yearly},
|
||||
{text: qsTr("ALL"), enabled: true, timeRange: ChartStoreBase.TimeRange.All}]
|
||||
{text: qsTr("7D"), enabled: true, timeRange: ChartStoreBase.TimeRange.Weekly, timeIndex: 0},
|
||||
{text: qsTr("1M"), enabled: true, timeRange: ChartStoreBase.TimeRange.Monthly, timeIndex: 1},
|
||||
{text: qsTr("6M"), enabled: true, timeRange: ChartStoreBase.TimeRange.HalfYearly, timeIndex: 2},
|
||||
{text: qsTr("1Y"), enabled: true, timeRange: ChartStoreBase.TimeRange.Yearly, timeIndex: 3},
|
||||
{text: qsTr("ALL"), enabled: true, timeRange: ChartStoreBase.TimeRange.All, timeIndex: 4}]
|
||||
|
||||
property var weeklyData: []
|
||||
property var monthlyData: []
|
||||
|
@ -66,61 +66,39 @@ Item {
|
|||
]
|
||||
|
||||
/// \timeRange is the time range of the data that was updated
|
||||
signal newDataReady(int timeRange)
|
||||
signal newDataReady(string address, string tokenSymbol, string currencySymbol, int timeRange)
|
||||
|
||||
function timeRangeEnumToStr(enumVal) {
|
||||
return d.timeRangeTabsModel.get(enumVal)
|
||||
return d.timeRangeEnumToPropertiesMap.get(enumVal).text
|
||||
}
|
||||
function timeRangeEnumToTimeIndex(enumVal) {
|
||||
return d.timeRangeEnumToPropertiesMap.get(enumVal).timeIndex
|
||||
}
|
||||
function timeRangeStrToEnum(str) {
|
||||
return d.timeRangeStrToEnumMap.get(str)
|
||||
}
|
||||
|
||||
/// \arg timeRange: of type ChartStoreBase.TimeRange
|
||||
function updateRequestTime(timeRange) {
|
||||
d.requestTimes.set(timeRange, new Date())
|
||||
}
|
||||
|
||||
function resetRequestTime() {
|
||||
d.requestTimes.set(timeRange, new Date(0))
|
||||
}
|
||||
|
||||
/// \arg timeRange: of type ChartStoreBase.TimeRange
|
||||
function isTimeToRequest(timeRange) {
|
||||
if(d.requestTimes.has(timeRange)) {
|
||||
const hoursToIgnore = 12
|
||||
let existing = d.requestTimes.get(timeRange)
|
||||
let willBeMs = new Date(existing.getTime() + (hoursToIgnore * 3600000))
|
||||
return new Date(willBeMs) < new Date()
|
||||
}
|
||||
else
|
||||
return true
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
readonly property int hoursInADay: 24
|
||||
readonly property int avgLengthOfMonth: 30
|
||||
property var timeRangeEnumToStrMap: null
|
||||
property var timeRangeEnumToPropertiesMap: null
|
||||
property var timeRangeStrToEnumMap: null
|
||||
property var requestTimes: null
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if(d.timeRangeEnumToStrMap === null) {
|
||||
d.timeRangeEnumToStrMap = new Map()
|
||||
if(d.timeRangeEnumToPropertiesMap === null) {
|
||||
d.timeRangeEnumToPropertiesMap = new Map()
|
||||
for (const x of timeRangeTabsModel) {
|
||||
d.timeRangeEnumToStrMap.set(x.timeRange, x.text)
|
||||
d.timeRangeEnumToPropertiesMap.set(x.timeRange, x)
|
||||
}
|
||||
d.timeRangeStrToEnumMap = new Map()
|
||||
for (const x of d.timeRangeEnumToStrMap.entries()) {
|
||||
for (const x of d.timeRangeEnumToPropertiesMap.entries()) {
|
||||
let key = x[0]
|
||||
let val = x[1]
|
||||
d.timeRangeStrToEnumMap.set(val, key)
|
||||
d.timeRangeStrToEnumMap.set(val.text, key)
|
||||
}
|
||||
}
|
||||
if(d.requestTimes === null) {
|
||||
d.requestTimes = new Map()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -229,8 +229,8 @@ QtObject {
|
|||
property bool marketHistoryIsLoading: walletSectionAllTokens.marketHistoryIsLoading
|
||||
|
||||
// TODO: range until we optimize to cache the data and abuse the requests
|
||||
function fetchHistoricalBalanceForTokenAsJson(address, symbol, timeIntervalEnum) {
|
||||
walletSectionAllTokens.fetchHistoricalBalanceForTokenAsJson(address, symbol, timeIntervalEnum)
|
||||
function fetchHistoricalBalanceForTokenAsJson(address, tokenSymbol, currencySymbol, timeIntervalEnum) {
|
||||
walletSectionAllTokens.fetchHistoricalBalanceForTokenAsJson(address, tokenSymbol, currencySymbol, timeIntervalEnum)
|
||||
}
|
||||
|
||||
property bool balanceHistoryIsLoading: walletSectionAllTokens.balanceHistoryIsLoading
|
||||
|
|
|
@ -7,10 +7,27 @@ import utils 1.0
|
|||
ChartStoreBase {
|
||||
id: root
|
||||
|
||||
/*required*/ property string address: ""
|
||||
readonly property alias address: d.address
|
||||
readonly property alias tokenSymbol: d.tokenSymbol
|
||||
readonly property alias currencySymbol: d.currencySymbol
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
// Data identity received from backend
|
||||
property var chainIds: []
|
||||
property string address
|
||||
property string tokenSymbol
|
||||
property string currencySymbol
|
||||
}
|
||||
|
||||
function hasData(address, tokenSymbol, currencySymbol, timeRangeEnum) {
|
||||
return address === d.address && tokenSymbol === d.tokenSymbol && currencySymbol === d.currencySymbol
|
||||
&& root.dataRange[root.timeRangeEnumToTimeIndex(timeRangeEnum)][root.timeRangeEnumToStr(timeRangeEnum)].length > 0
|
||||
}
|
||||
|
||||
/// \arg timeRange: of type ChartStoreBase.TimeRange
|
||||
function setData(timeRange, timeRangeData, balanceData) {
|
||||
function setData(address, tokenSymbol, currencySymbol, timeRange, timeRangeData, balanceData) {
|
||||
switch(timeRange) {
|
||||
case ChartStoreBase.TimeRange.Weekly:
|
||||
root.weeklyData = balanceData
|
||||
|
@ -39,38 +56,42 @@ ChartStoreBase {
|
|||
break;
|
||||
default:
|
||||
console.warn("Invalid or unsupported time range")
|
||||
break;
|
||||
return
|
||||
}
|
||||
root.newDataReady(timeRange)
|
||||
|
||||
d.address = address
|
||||
d.tokenSymbol = tokenSymbol
|
||||
d.currencySymbol = currencySymbol
|
||||
|
||||
root.newDataReady(address, tokenSymbol, currencySymbol, timeRange)
|
||||
}
|
||||
|
||||
/// \arg timeRange: of type ChartStoreBase.TimeRange
|
||||
function resetData(timeRange) {
|
||||
root.setData(timeRange, [], [])
|
||||
function resetAllData(address, tokenSymbol, currencySymbol) {
|
||||
for (let tR = ChartStoreBase.TimeRange.Weekly; tR <= ChartStoreBase.TimeRange.All; tR++) {
|
||||
root.setData(address, tokenSymbol, currencySymbol, tR, [], [])
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: walletSectionAllTokens
|
||||
function onTokenBalanceHistoryDataReady(balanceHistory: string) {
|
||||
// chainId, address, symbol, timeInterval
|
||||
let response = JSON.parse(balanceHistory)
|
||||
if (response === null) {
|
||||
console.warn("error parsing balance history json message data")
|
||||
root.resetRequestTime()
|
||||
|
||||
function onTokenBalanceHistoryDataReady(balanceHistoryJson: string) {
|
||||
// chainIds, address, tokenSymbol, currencySymbol, timeInterval
|
||||
let response = JSON.parse(balanceHistoryJson)
|
||||
if(typeof response.error !== "undefined") {
|
||||
console.warn("error in balance history: " + response.error)
|
||||
return
|
||||
}
|
||||
|
||||
if(d.address != response.address || d.tokenSymbol != response.tokenSymbol || d.currencySymbol != response.currencySymbol) {
|
||||
root.resetAllData(response.address, response.tokenSymbol, response.currencySymbol)
|
||||
}
|
||||
|
||||
if(typeof response.historicalData === "undefined" || response.historicalData === null || response.historicalData.length == 0) {
|
||||
console.warn("error no data in balance history. Must be an error from status-go")
|
||||
root.resetRequestTime()
|
||||
return
|
||||
} else if(response.address !== root.address) {
|
||||
// Ignore data for other addresses. Will be handled by other instances of this store
|
||||
console.info("no data in balance history")
|
||||
return
|
||||
}
|
||||
|
||||
root.resetData(response.timeInterval)
|
||||
|
||||
var tmpTimeRange = []
|
||||
var tmpDataValues = []
|
||||
for(let i = 0; i < response.historicalData.length; i++) {
|
||||
|
@ -81,11 +102,10 @@ ChartStoreBase {
|
|||
: LocaleUtils.getMonthYear(dataEntry.time * 1000)
|
||||
tmpTimeRange.push(dateString)
|
||||
|
||||
tmpDataValues.push(parseFloat(globalUtils.wei2Eth(dataEntry.value, 18)))
|
||||
tmpDataValues.push(dataEntry.value)
|
||||
}
|
||||
|
||||
root.setData(response.timeInterval, tmpTimeRange, tmpDataValues)
|
||||
root.updateRequestTime(response.timeInterval)
|
||||
root.setData(response.address, response.tokenSymbol, response.currencySymbol, response.timeInterval, tmpTimeRange, tmpDataValues)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,6 @@ singleton RootStore 1.0 RootStore.qml
|
|||
CurrenciesStore 1.0 CurrenciesStore.qml
|
||||
TransactionStore 1.0 TransactionStore.qml
|
||||
BIP39_en 1.0 BIP39_en.qml
|
||||
ChartStoreBase 1.0 ChartStoreBase.qml
|
||||
TokenBalanceHistoryStore 1.0 TokenBalanceHistoryStore.qml
|
||||
TokenMarketValuesStore 1.0 TokenMarketValuesStore.qml
|
||||
ChartStoreBase 1.0 ChartStoreBase.qml
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit f6b4721c4a74f05049b1309c3250ed803d936c59
|
||||
Subproject commit f4f6b253022275dbe08f27d3a29b6c86da21b35b
|
Loading…
Reference in New Issue