Compare commits
3 Commits
b0bd473e96
...
a41b13f3b2
Author | SHA1 | Date | |
---|---|---|---|
a41b13f3b2 | |||
71cfe71f9d | |||
046dc79a04 |
|
@ -38,6 +38,7 @@ set(LIBS ${LIBS} "-lcpprest")
|
|||
include_directories(src)
|
||||
include_directories(src/wallets)
|
||||
include_directories(src/exchanges)
|
||||
include_directories(src/datasources)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ Cryptocurrencies portfolio manager.
|
|||
|
||||
* MySQL
|
||||
* C++ REST SDK : https://github.com/Microsoft/cpprestsdk
|
||||
* OpenSSL
|
||||
* OpenSSL (1.1.0+)
|
||||
* GMP
|
||||
* Boost
|
||||
* sqlpp11 (modified to handle decimal type) : https://github.com/evilny0/sqlpp11/tree/decimal
|
||||
|
|
|
@ -29,7 +29,7 @@ if (SQLPP11_MYSQL_MAIN_HEADER)
|
|||
)
|
||||
|
||||
if("${check_result}" STREQUAL "")
|
||||
string(APPEND SQLPP11_NOT_FOUND_MESSAGE "\nRejecting found '${SQLPP11_MYSQL_MAIN_HEADER}', it seems to be invalid.")
|
||||
string(APPEND SQLPP11_MYSQL_NOT_FOUND_MESSAGE "\nRejecting found '${SQLPP11_MYSQL_MAIN_HEADER}', it seems to be invalid.")
|
||||
unset(SQLPP11_MYSQL_INCLUDE_DIR CACHE)
|
||||
else()
|
||||
# Check succeeded, create target
|
||||
|
|
|
@ -7,6 +7,37 @@ SET time_zone = "+00:00";
|
|||
/*!40101 SET NAMES utf8mb4 */;
|
||||
|
||||
|
||||
CREATE TABLE `blockchain_btc_raw_tx` (
|
||||
`raw_tx_id` int(11) NOT NULL,
|
||||
`blockchain_id` int(11) NOT NULL,
|
||||
`hash` varchar(200) NOT NULL,
|
||||
`unix_time` int(11) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE `blockchain_btc_raw_tx_details` (
|
||||
`raw_tx_detail_id` int(11) NOT NULL,
|
||||
`raw_tx_id` int(11) NOT NULL,
|
||||
`amount` decimal(40,18) NOT NULL,
|
||||
`address` varchar(100) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE `blockchain_eth_raw_tx` (
|
||||
`raw_tx_id` int(11) NOT NULL,
|
||||
`blockchain_id` int(11) NOT NULL,
|
||||
`hash` varchar(200) NOT NULL,
|
||||
`unix_time` int(11) NOT NULL,
|
||||
`fee` decimal(40,18) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE `blockchain_eth_raw_tx_details` (
|
||||
`raw_tx_detail_id` int(11) NOT NULL,
|
||||
`raw_tx_id` int(11) NOT NULL,
|
||||
`address_from` varchar(200) NOT NULL,
|
||||
`address_to` varchar(200) NOT NULL,
|
||||
`amount` decimal(40,18) NOT NULL,
|
||||
`amount_coin_id` int(11) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE `coins` (
|
||||
`coin_id` int(11) NOT NULL,
|
||||
`coin_name` varchar(50) NOT NULL,
|
||||
|
@ -32,7 +63,7 @@ CREATE TABLE `exchanges_ledgers` (
|
|||
`account_id` int(11) NOT NULL,
|
||||
`exchange_ledger_id` varchar(50) NOT NULL,
|
||||
`exchange_reference_id` varchar(50) NOT NULL,
|
||||
`timestamp` int(11) NOT NULL,
|
||||
`unix_time` int(11) NOT NULL,
|
||||
`operation_type` int(11) NOT NULL,
|
||||
`coin_id` int(11) NOT NULL,
|
||||
`amount` decimal(40,18) NOT NULL,
|
||||
|
@ -46,7 +77,7 @@ CREATE TABLE `exchanges_trades` (
|
|||
`exchange_order_id` varchar(50) NOT NULL,
|
||||
`base_coin_id` int(11) NOT NULL,
|
||||
`quote_coin_id` int(11) NOT NULL,
|
||||
`timestamp` int(11) NOT NULL,
|
||||
`unix_time` int(11) NOT NULL,
|
||||
`type` int(11) NOT NULL,
|
||||
`order_type` int(11) NOT NULL,
|
||||
`price` decimal(40,18) NOT NULL,
|
||||
|
@ -60,7 +91,8 @@ CREATE TABLE `wallets` (
|
|||
`wallet_id` int(11) NOT NULL,
|
||||
`user_id` int(11) NOT NULL,
|
||||
`type_id` int(11) NOT NULL,
|
||||
`wallet_name` varchar(50) NOT NULL
|
||||
`wallet_name` varchar(50) NOT NULL,
|
||||
`active` tinyint(1) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE `wallets_addresses` (
|
||||
|
@ -77,16 +109,40 @@ CREATE TABLE `wallets_balances` (
|
|||
`balance` decimal(48,18) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE `wallets_btc_raw_tx` (
|
||||
`raw_tx_id` int(11) NOT NULL,
|
||||
`wallet_id` int(11) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE `wallets_eth_raw_tx` (
|
||||
`raw_tx_id` int(11) NOT NULL,
|
||||
`wallet_id` int(11) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE `wallets_tx` (
|
||||
`tx_id` int(11) NOT NULL,
|
||||
`blockchain_tx_id` varchar(200) NOT NULL,
|
||||
`wallet_id` int(11) NOT NULL,
|
||||
`amount` decimal(40,18) NOT NULL,
|
||||
`fee` decimal(40,18) NOT NULL,
|
||||
`timestamp` int(11) NOT NULL
|
||||
`amount_coin_id` int(11) NOT NULL,
|
||||
`fee_coin_id` int(11) NOT NULL,
|
||||
`unix_time` int(11) NOT NULL,
|
||||
`operation_type` int(11) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
|
||||
ALTER TABLE `blockchain_btc_raw_tx`
|
||||
ADD PRIMARY KEY (`raw_tx_id`);
|
||||
|
||||
ALTER TABLE `blockchain_btc_raw_tx_details`
|
||||
ADD PRIMARY KEY (`raw_tx_detail_id`);
|
||||
|
||||
ALTER TABLE `blockchain_eth_raw_tx`
|
||||
ADD PRIMARY KEY (`raw_tx_id`);
|
||||
|
||||
ALTER TABLE `blockchain_eth_raw_tx_details`
|
||||
ADD PRIMARY KEY (`raw_tx_detail_id`);
|
||||
|
||||
ALTER TABLE `exchanges_accounts`
|
||||
ADD PRIMARY KEY (`account_id`);
|
||||
|
||||
|
@ -103,12 +159,20 @@ ALTER TABLE `wallets_addresses`
|
|||
ADD PRIMARY KEY (`address_id`);
|
||||
|
||||
ALTER TABLE `wallets_balances`
|
||||
ADD PRIMARY KEY (`wallet_id`);
|
||||
ADD PRIMARY KEY (`wallet_id`,`coin_id`);
|
||||
|
||||
ALTER TABLE `wallets_tx`
|
||||
ADD PRIMARY KEY (`tx_id`);
|
||||
|
||||
|
||||
ALTER TABLE `blockchain_btc_raw_tx`
|
||||
MODIFY `raw_tx_id` int(11) NOT NULL AUTO_INCREMENT;
|
||||
ALTER TABLE `blockchain_btc_raw_tx_details`
|
||||
MODIFY `raw_tx_detail_id` int(11) NOT NULL AUTO_INCREMENT;
|
||||
ALTER TABLE `blockchain_eth_raw_tx`
|
||||
MODIFY `raw_tx_id` int(11) NOT NULL AUTO_INCREMENT;
|
||||
ALTER TABLE `blockchain_eth_raw_tx_details`
|
||||
MODIFY `raw_tx_detail_id` int(11) NOT NULL AUTO_INCREMENT;
|
||||
ALTER TABLE `exchanges_accounts`
|
||||
MODIFY `account_id` int(11) NOT NULL AUTO_INCREMENT;
|
||||
ALTER TABLE `exchanges_ledgers`
|
||||
|
@ -123,4 +187,4 @@ ALTER TABLE `wallets_tx`
|
|||
MODIFY `tx_id` int(11) NOT NULL AUTO_INCREMENT;
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
|
@ -1,6 +1,7 @@
|
|||
aux_source_directory(. SOURCE_LIST)
|
||||
aux_source_directory(wallets SOURCE_LIST_WALLETS)
|
||||
aux_source_directory(exchanges SOURCE_LIST_EXCHANGES)
|
||||
aux_source_directory(datasources SOURCE_LIST_DATASOURCES)
|
||||
|
||||
add_executable(${EXECUTABLE} ${SOURCE_LIST} ${SOURCE_LIST_WALLETS} ${SOURCE_LIST_EXCHANGES})
|
||||
add_executable(${EXECUTABLE} ${SOURCE_LIST} ${SOURCE_LIST_WALLETS} ${SOURCE_LIST_EXCHANGES} ${SOURCE_LIST_DATASOURCES})
|
||||
target_link_libraries(${EXECUTABLE} ${LIBS})
|
||||
|
|
137
src/datasources/blockchain_info.cpp
Normal file
137
src/datasources/blockchain_info.cpp
Normal file
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* Copyright (c) 2021, evilny0
|
||||
*
|
||||
* This file is part of cpfm.
|
||||
*
|
||||
* cpfm is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* cpm is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with cpfm. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "datasources/blockchain_info.h"
|
||||
|
||||
list<BlockchainTxDetailsTypeBTC> BlockchainDataSourceBTC_BlockchainInfo::getTxDetailsListForAddress(string address)
|
||||
{
|
||||
linfo << "Blockchain.info : Analyzing address: " << address;
|
||||
|
||||
list<BlockchainTxDetailsTypeBTC> l;
|
||||
|
||||
if (!bfs::exists(getCacheFilenameForAddress(address)))
|
||||
{
|
||||
saveBlockchainAddressDataToCacheFile(address);
|
||||
}
|
||||
|
||||
if (!bfs::exists(getCacheFilenameForAddress(address)))
|
||||
{
|
||||
lerr << "Blockchain.info : cache file could not be found. Address data was not retrieved.";
|
||||
return l;
|
||||
}
|
||||
|
||||
ifstream f;
|
||||
f.open(getCacheFilenameForAddress(address));
|
||||
json::value jvalue = json::value::parse(f);
|
||||
f.close();
|
||||
|
||||
linfo << "Blockchain.info : analyzing address: " << jvalue["address"].as_string();
|
||||
|
||||
for (int i=0;i<jvalue["txs"].size();i++)
|
||||
{
|
||||
BlockchainTxDetailsTypeBTC tx;
|
||||
|
||||
tx.hash = jvalue["txs"][i]["hash"].as_string();
|
||||
tx.time.setFromUnixTime(jvalue["txs"][i]["time"].as_integer());
|
||||
|
||||
for (int j=0;j<jvalue["txs"][i]["inputs"].size();j++)
|
||||
{
|
||||
string addr = jvalue["txs"][i]["inputs"][j]["prev_out"]["addr"].as_string();
|
||||
__int64 amount = jvalue["txs"][i]["inputs"][j]["prev_out"]["value"].as_integer();
|
||||
|
||||
Money m = amount;
|
||||
Money mdecimals = m/100000000;
|
||||
|
||||
// The same address can be multiple time in the inputs, so we need to sum the amounts.
|
||||
tx.inputs[addr] += mdecimals;
|
||||
}
|
||||
|
||||
for (int j=0;j<jvalue["txs"][i]["out"].size();j++)
|
||||
{
|
||||
string addr = jvalue["txs"][i]["out"][j]["addr"].as_string();
|
||||
__int64 amount = jvalue["txs"][i]["out"][j]["value"].as_integer();
|
||||
|
||||
Money m = amount;
|
||||
Money mdecimals = m/100000000;
|
||||
|
||||
// The same address can only be once in the outputs.
|
||||
tx.outputs[addr] = mdecimals;
|
||||
}
|
||||
|
||||
l.push_back(tx);
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
string BlockchainDataSourceBTC_BlockchainInfo::getCacheFilenameForAddress(string address)
|
||||
{
|
||||
string cacheFilename("data/cache/blockchain.info/btc/" + address);
|
||||
return cacheFilename;
|
||||
}
|
||||
|
||||
void BlockchainDataSourceBTC_BlockchainInfo::saveBlockchainAddressDataToCacheFile(string address)
|
||||
{
|
||||
try
|
||||
{
|
||||
linfo << "Blockchain.info : querying API about " << address;
|
||||
|
||||
string sRequestURL = "/rawaddr/";
|
||||
sRequestURL += address;
|
||||
http_client apiclient("https://blockchain.info/");
|
||||
|
||||
m_currentRequestCacheFilename = getCacheFilenameForAddress(address);
|
||||
|
||||
apiclient.request(methods::GET,sRequestURL).then([](http_response response)
|
||||
{
|
||||
if (response.status_code() == status_codes::OK)
|
||||
{
|
||||
ldebug << "Blockchain.info : response OK.";
|
||||
return response.extract_json();
|
||||
}
|
||||
else if (response.status_code() == status_codes::TooManyRequests)
|
||||
{
|
||||
lwarn << "Blockchain.info : too many queries! We are being rate limited.";
|
||||
pplx::task_from_result(json::value());
|
||||
}
|
||||
return pplx::task_from_result(json::value());
|
||||
})
|
||||
.then([this](pplx::task<json::value> previousTask)
|
||||
{
|
||||
if (previousTask.get() != json::value::null())
|
||||
{
|
||||
linfo << "Blockchain.info : saving query result to " << m_currentRequestCacheFilename;
|
||||
ofstream f;
|
||||
f.open(m_currentRequestCacheFilename);
|
||||
f << previousTask.get();
|
||||
f.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
lerr << "Blockchain.info : query result is empty. Nothing will be saved to the cache file.";
|
||||
}
|
||||
})
|
||||
.wait();
|
||||
}
|
||||
catch(const http::http_exception& e)
|
||||
{
|
||||
lerr << "Blockchain.info : failed to query API about " << address;
|
||||
}
|
||||
}
|
39
src/datasources/blockchain_info.h
Normal file
39
src/datasources/blockchain_info.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) 2021, evilny0
|
||||
*
|
||||
* This file is part of cpfm.
|
||||
*
|
||||
* cpfm is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* cpm is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with cpfm. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CPFM_DATASOURCE_BLOCKCHAIN_INFO_H_INCLUDED
|
||||
#define CPFM_DATASOURCE_BLOCKCHAIN_INFO_H_INCLUDED
|
||||
|
||||
#include "datasources/datasource.h"
|
||||
|
||||
|
||||
class BlockchainDataSourceBTC_BlockchainInfo
|
||||
{
|
||||
public:
|
||||
list<BlockchainTxDetailsTypeBTC> getTxDetailsListForAddress(string address);
|
||||
|
||||
private:
|
||||
string getCacheFilenameForAddress(string address);
|
||||
void saveBlockchainAddressDataToCacheFile(string address);
|
||||
|
||||
string m_currentRequestCacheFilename;
|
||||
};
|
||||
|
||||
#endif // CPFM_DATASOURCE_BLOCKCHAIN_INFO_H_INCLUDED
|
245
src/datasources/blockchair.cpp
Normal file
245
src/datasources/blockchair.cpp
Normal file
|
@ -0,0 +1,245 @@
|
|||
/*
|
||||
* Copyright (c) 2021, evilny0
|
||||
*
|
||||
* This file is part of cpfm.
|
||||
*
|
||||
* cpfm is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* cpm is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with cpfm. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "datasources/blockchair.h"
|
||||
|
||||
|
||||
Time BlockchainDataSourceTypeBTC_Blockchair::getTimeFromString(string s)
|
||||
{
|
||||
std::istringstream in{s};
|
||||
date::sys_time<std::chrono::seconds> tp;
|
||||
in >> date::parse("%F %T", tp);
|
||||
|
||||
Time t;
|
||||
t.setFromUnixTime(std::chrono::system_clock::to_time_t(tp));
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
list<BlockchainTxDetailsTypeBTC> BlockchainDataSourceTypeBTC_Blockchair::getTxDetailsListForAddresses(list<string> addresses)
|
||||
{
|
||||
linfo << "Blockchair : analyzing address list";
|
||||
|
||||
list<BlockchainTxDetailsTypeBTC> l;
|
||||
|
||||
if (!bfs::exists(getCacheFilenameForAddresses(addresses)))
|
||||
{
|
||||
saveBlockchainAddressesDataToCacheFile(addresses);
|
||||
}
|
||||
|
||||
if (!bfs::exists(getCacheFilenameForAddresses(addresses)))
|
||||
{
|
||||
lerr << "Blockchair : cache file could not be found. Addresses data was not retrieved.";
|
||||
return l;
|
||||
}
|
||||
|
||||
ifstream f;
|
||||
f.open(getCacheFilenameForAddresses(addresses));
|
||||
json::value jvalue = json::value::parse(f);
|
||||
f.close();
|
||||
|
||||
for (int i=0;i<jvalue["data"]["transactions"].size();i++)
|
||||
{
|
||||
string txHash = jvalue["data"]["transactions"][i].as_string();
|
||||
|
||||
linfo << "Blockchair : found tx " << txHash << ". Starting analysis.";
|
||||
|
||||
if (!bfs::exists(getCacheFilenameForTx(txHash)))
|
||||
{
|
||||
saveBlockchainTxDataToCacheFile(txHash);
|
||||
}
|
||||
|
||||
if (!bfs::exists(getCacheFilenameForTx(txHash)))
|
||||
{
|
||||
lerr << "Blockchair : cache file for tx " << txHash << " could not be found. Tx data was not retrieved.";
|
||||
}
|
||||
|
||||
ifstream fTx;
|
||||
fTx.open(getCacheFilenameForTx(txHash));
|
||||
json::value jvalueTx = json::value::parse(fTx);
|
||||
fTx.close();
|
||||
|
||||
BlockchainTxDetailsTypeBTC tx;
|
||||
|
||||
tx.hash = txHash;
|
||||
tx.time = getTimeFromString (jvalueTx["data"][txHash]["transaction"]["time"].as_string());
|
||||
|
||||
for (int j=0;j<jvalueTx["data"][txHash]["inputs"].size();j++)
|
||||
{
|
||||
string addr = jvalueTx["data"][txHash]["inputs"][j]["recipient"].as_string();
|
||||
__int64 amount = jvalueTx["data"][txHash]["inputs"][j]["value"].as_integer();
|
||||
|
||||
Money m = amount;
|
||||
Money mdecimals = m/100000000;
|
||||
|
||||
// The same address can be multiple time in the inputs, so we need to sum the amounts.
|
||||
tx.inputs[addr] += mdecimals;
|
||||
}
|
||||
|
||||
for (int j=0;j<jvalueTx["data"][txHash]["outputs"].size();j++)
|
||||
{
|
||||
string addr = jvalueTx["data"][txHash]["outputs"][j]["recipient"].as_string();
|
||||
__int64 amount = jvalueTx["data"][txHash]["outputs"][j]["value"].as_integer();
|
||||
|
||||
Money m = amount;
|
||||
Money mdecimals = m/100000000;
|
||||
|
||||
// The same address can only be once in the outputs.
|
||||
tx.outputs[addr] = mdecimals;
|
||||
}
|
||||
|
||||
l.push_back(tx);
|
||||
|
||||
linfo << "Blockchair : finished analysis for tx " << txHash << ".";
|
||||
}
|
||||
|
||||
linfo << "Blockchair : finished analysis for address list.";
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
string BlockchainDataSourceTypeBTC_Blockchair::getCacheFilenameForAddresses(list<string> addresses)
|
||||
{
|
||||
string s;
|
||||
|
||||
for (const auto a: addresses)
|
||||
s = s+a;
|
||||
|
||||
string cacheFilename("data/cache/blockchair/"+m_blockchainName+"/addresses/" + s);
|
||||
return cacheFilename;
|
||||
}
|
||||
|
||||
string BlockchainDataSourceTypeBTC_Blockchair::getCacheFilenameForTx(string txHash)
|
||||
{
|
||||
string cacheFilename("data/cache/blockchair/"+m_blockchainName+"/tx/" + txHash);
|
||||
return cacheFilename;
|
||||
}
|
||||
|
||||
void BlockchainDataSourceTypeBTC_Blockchair::saveBlockchainAddressesDataToCacheFile(list<string> addresses)
|
||||
{
|
||||
try
|
||||
{
|
||||
string s;
|
||||
|
||||
for (const auto a: addresses)
|
||||
{
|
||||
if (s.length())
|
||||
s = s+","+a;
|
||||
else
|
||||
s = a;
|
||||
}
|
||||
|
||||
linfo << "Blockchair : querying API about addresses : " << s;
|
||||
|
||||
string sRequestURL = "/"+m_blockchainName+"/dashboards/addresses/";
|
||||
sRequestURL += s;
|
||||
http_client apiclient("https://api.blockchair.com/");
|
||||
|
||||
m_currentRequestCacheFilename = getCacheFilenameForAddresses(addresses);
|
||||
|
||||
ltrace << "Blockchair : query : " << sRequestURL;
|
||||
|
||||
apiclient.request(methods::GET,sRequestURL).then([](http_response response)
|
||||
{
|
||||
if (response.status_code() == status_codes::OK)
|
||||
{
|
||||
ldebug << "Blockchair : response OK.";
|
||||
return response.extract_json();
|
||||
}
|
||||
else if (response.status_code() == status_codes::TooManyRequests)
|
||||
{
|
||||
lwarn << "Blockchair : too many queries! We are being rate limited.";
|
||||
pplx::task_from_result(json::value());
|
||||
}
|
||||
return pplx::task_from_result(json::value());
|
||||
})
|
||||
.then([this](pplx::task<json::value> previousTask)
|
||||
{
|
||||
if (previousTask.get() != json::value::null())
|
||||
{
|
||||
linfo << "Blockchair : saving query result to " << m_currentRequestCacheFilename;
|
||||
ofstream f;
|
||||
f.open(m_currentRequestCacheFilename);
|
||||
f << previousTask.get();
|
||||
f.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
lerr << "Blockchair : query result is empty. Nothing will be saved to the cache file.";
|
||||
}
|
||||
})
|
||||
.wait();
|
||||
}
|
||||
catch(const http::http_exception& e)
|
||||
{
|
||||
lerr << "Blockchair : failed to query API";
|
||||
}
|
||||
}
|
||||
|
||||
void BlockchainDataSourceTypeBTC_Blockchair::saveBlockchainTxDataToCacheFile(string txHash)
|
||||
{
|
||||
try
|
||||
{
|
||||
linfo << "Blockchair : querying API about tx : " << txHash;
|
||||
|
||||
string sRequestURL = "/"+m_blockchainName+"/dashboards/transaction/";
|
||||
sRequestURL += txHash;
|
||||
http_client apiclient("https://api.blockchair.com/");
|
||||
|
||||
m_currentRequestCacheFilename = getCacheFilenameForTx(txHash);
|
||||
|
||||
ltrace << "Blockchair : query : " << sRequestURL;
|
||||
|
||||
apiclient.request(methods::GET,sRequestURL).then([](http_response response)
|
||||
{
|
||||
if (response.status_code() == status_codes::OK)
|
||||
{
|
||||
ldebug << "Blockchair : response OK.";
|
||||
return response.extract_json();
|
||||
}
|
||||
else if (response.status_code() == status_codes::TooManyRequests)
|
||||
{
|
||||
lwarn << "Blockchair : too many queries! We are being rate limited.";
|
||||
pplx::task_from_result(json::value());
|
||||
}
|
||||
return pplx::task_from_result(json::value());
|
||||
})
|
||||
.then([this](pplx::task<json::value> previousTask)
|
||||
{
|
||||
if (previousTask.get() != json::value::null())
|
||||
{
|
||||
linfo << "Blockchair : saving query result to " << m_currentRequestCacheFilename;
|
||||
ofstream f;
|
||||
f.open(m_currentRequestCacheFilename);
|
||||
f << previousTask.get();
|
||||
f.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
lerr << "Blockchair : query result is empty. Nothing will be saved to the cache file.";
|
||||
}
|
||||
})
|
||||
.wait();
|
||||
}
|
||||
catch(const http::http_exception& e)
|
||||
{
|
||||
lerr << "Blockchair : failed to query API about " << txHash;
|
||||
}
|
||||
}
|
67
src/datasources/blockchair.h
Normal file
67
src/datasources/blockchair.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (c) 2021, evilny0
|
||||
*
|
||||
* This file is part of cpfm.
|
||||
*
|
||||
* cpfm is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* cpm is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with cpfm. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CPFM_DATASOURCE_BLOCKCHAIR_H_INCLUDED
|
||||
#define CPFM_DATASOURCE_BLOCKCHAIR_H_INCLUDED
|
||||
|
||||
#include "datasources/datasource.h"
|
||||
|
||||
|
||||
class BlockchainDataSourceTypeBTC_Blockchair
|
||||
{
|
||||
public:
|
||||
list<BlockchainTxDetailsTypeBTC> getTxDetailsListForAddresses(list<string> addresses);
|
||||
|
||||
private:
|
||||
string getCacheFilenameForAddresses(list<string> address);
|
||||
string getCacheFilenameForTx(string txHash);
|
||||
|
||||
void saveBlockchainAddressesDataToCacheFile(list<string> address);
|
||||
void saveBlockchainTxDataToCacheFile(string txHash);
|
||||
|
||||
Time getTimeFromString(string s);
|
||||
|
||||
string m_currentRequestCacheFilename;
|
||||
|
||||
protected:
|
||||
string m_blockchainName;
|
||||
|
||||
virtual void dummyMakeAbstract() = 0;
|
||||
};
|
||||
|
||||
class BlockchainDataSourceBTC_Blockchair : public BlockchainDataSourceTypeBTC_Blockchair
|
||||
{
|
||||
public:
|
||||
BlockchainDataSourceBTC_Blockchair() { m_blockchainName = "bitcoin"; }
|
||||
|
||||
protected:
|
||||
virtual void dummyMakeAbstract(){};
|
||||
};
|
||||
|
||||
class BlockchainDataSourceBCH_Blockchair : public BlockchainDataSourceTypeBTC_Blockchair
|
||||
{
|
||||
public:
|
||||
BlockchainDataSourceBCH_Blockchair() { m_blockchainName = "bitcoin-cash"; }
|
||||
|
||||
protected:
|
||||
virtual void dummyMakeAbstract(){};
|
||||
};
|
||||
|
||||
#endif // CPFM_DATASOURCE_BLOCKCHAIR_H_INCLUDED
|
77
src/datasources/datasource.cpp
Normal file
77
src/datasources/datasource.cpp
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright (c) 2021, evilny0
|
||||
*
|
||||
* This file is part of cpfm.
|
||||
*
|
||||
* cpfm is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* cpm is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with cpfm. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "datasources/datasource.h"
|
||||
#include "datasources/blockchain_info.h"
|
||||
#include "datasources/blockchair.h"
|
||||
#include "datasources/etherscan.h"
|
||||
|
||||
list<BlockchainTxDetailsTypeBTC> BlockchainDataSourceBTC::getTxDetailsListForAddresses(list<string> addresses)
|
||||
{
|
||||
list<BlockchainTxDetailsTypeBTC> l;
|
||||
|
||||
// Blockchain.info
|
||||
/*
|
||||
BlockchainDataSourceBTC_BlockchainInfo bci;
|
||||
list<string> hashList;
|
||||
|
||||
for (auto const address: addresses)
|
||||
{
|
||||
list<BlockchainTxDetailsTypeBTC> listForSingleAddress = bci.getTxDetailsListForAddress(address);
|
||||
|
||||
for (auto const tx: listForSingleAddress)
|
||||
{
|
||||
if (std::find(hashList.begin(), hashList.end(), tx.hash) == hashList.end())
|
||||
{
|
||||
hashList.push_back(tx.hash);
|
||||
l.push_back(tx);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Blockchair
|
||||
BlockchainDataSourceBTC_Blockchair bc;
|
||||
l = bc.getTxDetailsListForAddresses(addresses);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
list<BlockchainTxDetailsTypeBTC> BlockchainDataSourceBCH::getTxDetailsListForAddresses(list<string> addresses)
|
||||
{
|
||||
list<BlockchainTxDetailsTypeBTC> l;
|
||||
|
||||
// Blockchair
|
||||
BlockchainDataSourceBCH_Blockchair bc;
|
||||
l = bc.getTxDetailsListForAddresses(addresses);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
list<BlockchainTxDetailsTypeETH> BlockchainDataSourceETH::getTxDetailsListForAddress(string address)
|
||||
{
|
||||
list<BlockchainTxDetailsTypeETH> l;
|
||||
|
||||
// Etherscan
|
||||
BlockchainDataSourceETH_Etherscan bc;
|
||||
l = bc.getTxDetailsListForAddress(address);
|
||||
|
||||
return l;
|
||||
}
|
71
src/datasources/datasource.h
Normal file
71
src/datasources/datasource.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright (c) 2021, evilny0
|
||||
*
|
||||
* This file is part of cpfm.
|
||||
*
|
||||
* cpfm is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* cpm is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with cpfm. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CPFM_DATASOURCE_H_INCLUDED
|
||||
#define CPFM_DATASOURCE_H_INCLUDED
|
||||
|
||||
#include "pf.h"
|
||||
|
||||
class BlockchainTxDetailsTypeBTC
|
||||
{
|
||||
public:
|
||||
string hash;
|
||||
Time time;
|
||||
map <string,Money> inputs;
|
||||
map <string,Money> outputs;
|
||||
};
|
||||
|
||||
class BlockchainTxOperationTypeETH
|
||||
{
|
||||
public:
|
||||
string addressFrom;
|
||||
string addressTo;
|
||||
Money amount;
|
||||
int amountCoinId;
|
||||
};
|
||||
|
||||
class BlockchainTxDetailsTypeETH
|
||||
{
|
||||
public:
|
||||
string hash;
|
||||
Time time;
|
||||
list<BlockchainTxOperationTypeETH> operations;
|
||||
Money fee;
|
||||
};
|
||||
|
||||
class BlockchainDataSourceBTC
|
||||
{
|
||||
public:
|
||||
list<BlockchainTxDetailsTypeBTC> getTxDetailsListForAddresses(list<string> addresses);
|
||||
};
|
||||
|
||||
class BlockchainDataSourceBCH
|
||||
{
|
||||
public:
|
||||
list<BlockchainTxDetailsTypeBTC> getTxDetailsListForAddresses(list<string> addresses);
|
||||
};
|
||||
|
||||
class BlockchainDataSourceETH
|
||||
{
|
||||
public:
|
||||
list<BlockchainTxDetailsTypeETH> getTxDetailsListForAddress(string address);
|
||||
};
|
||||
|
||||
#endif // CPFM_DATASOURCE_H_INCLUDED
|
323
src/datasources/etherscan.cpp
Normal file
323
src/datasources/etherscan.cpp
Normal file
|
@ -0,0 +1,323 @@
|
|||
/*
|
||||
* Copyright (c) 2021, evilny0
|
||||
*
|
||||
* This file is part of cpfm.
|
||||
*
|
||||
* cpfm is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* cpm is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with cpfm. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "datasources/etherscan.h"
|
||||
|
||||
#define TXLIST_NORMAL "txlist"
|
||||
#define TXLIST_INTERNAL "txlistinternal"
|
||||
#define TXLIST_TOKENS "tokentx"
|
||||
|
||||
list<BlockchainTxDetailsTypeETH> BlockchainDataSourceETH_Etherscan::getTxDetailsListForAddress(string address)
|
||||
{
|
||||
linfo << "Etherscan : Analyzing address: " << address;
|
||||
|
||||
if (!retrieveCacheFilesForAddress(address))
|
||||
{
|
||||
list<BlockchainTxDetailsTypeETH> l;
|
||||
lerr << "Etherscan : cache files could not be found. Address data was not retrieved.";
|
||||
return l;
|
||||
}
|
||||
|
||||
list<string> hashListNormal;
|
||||
list<string> hashListInternal;
|
||||
list<string> hashListToken;
|
||||
|
||||
list<BlockchainTxDetailsTypeETH> lNormal = getTxDetailsListForAddressTxType(address,TXLIST_NORMAL,hashListNormal);
|
||||
list<BlockchainTxDetailsTypeETH> lInternal = getTxDetailsListForAddressTxType(address,TXLIST_INTERNAL,hashListInternal);
|
||||
list<BlockchainTxDetailsTypeETH> lToken = getTxDetailsListForAddressTxType(address,TXLIST_TOKENS,hashListToken);
|
||||
|
||||
list<BlockchainTxDetailsTypeETH> l = lNormal;
|
||||
|
||||
// Add internals tx to the list
|
||||
for (auto const tx: lInternal)
|
||||
{
|
||||
BlockchainTxDetailsTypeETH txNew = tx;
|
||||
|
||||
list<BlockchainTxDetailsTypeETH>::iterator it;
|
||||
for (it=l.begin();it!=l.end();it++)
|
||||
{
|
||||
if (it->hash == tx.hash)
|
||||
{
|
||||
txNew = (*it);
|
||||
for (const auto op: tx.operations)
|
||||
{
|
||||
txNew.operations.push_back(op);
|
||||
}
|
||||
|
||||
l.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
l.push_back(txNew);
|
||||
}
|
||||
|
||||
// Add tokens tx to the list
|
||||
for (auto const tx: lToken)
|
||||
{
|
||||
BlockchainTxDetailsTypeETH txNew = tx;
|
||||
|
||||
list<BlockchainTxDetailsTypeETH>::iterator it;
|
||||
for (it=l.begin();it!=l.end();it++)
|
||||
{
|
||||
if (it->hash == tx.hash)
|
||||
{
|
||||
txNew = (*it);
|
||||
for (const auto op: tx.operations)
|
||||
{
|
||||
txNew.operations.push_back(op);
|
||||
}
|
||||
|
||||
l.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
l.push_back(txNew);
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
list<BlockchainTxDetailsTypeETH> BlockchainDataSourceETH_Etherscan::getTxDetailsListForAddressTxType(string address, string type, list<string>& hashList)
|
||||
{
|
||||
list<BlockchainTxDetailsTypeETH> l;
|
||||
|
||||
ifstream f;
|
||||
f.open(getCacheFilenameForAddress(address,type));
|
||||
json::value jvalue = json::value::parse(f);
|
||||
f.close();
|
||||
|
||||
linfo << "Etherscan : analyzing " << type << " for address: " << address;
|
||||
|
||||
if (!jvalue["status"].as_string().compare("1"))
|
||||
{
|
||||
for (int i=0;i<jvalue["result"].size();i++)
|
||||
{
|
||||
ltrace << "Found tx " << jvalue["result"][i]["hash"].as_string();
|
||||
|
||||
BlockchainTxDetailsTypeETH tx;
|
||||
|
||||
// Search if the hash is already in the list. If yes, remove the element.
|
||||
// It will be added after the operation list is updated.
|
||||
list<BlockchainTxDetailsTypeETH>::iterator it;
|
||||
for (it=l.begin();it!=l.end();it++)
|
||||
{
|
||||
if (it->hash == jvalue["result"][i]["hash"].as_string())
|
||||
{
|
||||
ltrace << "Tx already existed. Removing from list so it can be updated with new values.";
|
||||
tx = (*it);
|
||||
l.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tx.hash = jvalue["result"][i]["hash"].as_string();
|
||||
tx.time.setFromUnixTime(strtol(jvalue["result"][i]["timeStamp"].as_string().c_str(),NULL,10));
|
||||
|
||||
Money gasUsed (jvalue["result"][i]["gasUsed"]);
|
||||
Money gasPrice (jvalue["result"][i]["gasPrice"]);
|
||||
Money fee = gasPrice*gasUsed;
|
||||
Money feeDivider("1000000000000000000");
|
||||
fee /= feeDivider;
|
||||
tx.fee = fee;
|
||||
|
||||
if (type == TXLIST_NORMAL)
|
||||
{
|
||||
BlockchainTxOperationTypeETH op;
|
||||
op.addressFrom = jvalue["result"][i]["from"].as_string();
|
||||
op.addressTo = jvalue["result"][i]["to"].as_string();
|
||||
op.amountCoinId = CPFM_COIN_ID_ETH;
|
||||
|
||||
Money divider("1000000000000000000");
|
||||
Money m(jvalue["result"][i]["value"]);
|
||||
m /= divider;
|
||||
op.amount = m;
|
||||
|
||||
ltrace << "Operation. Amount: " << op.amount << ". Coin : " << getCoinName(op.amountCoinId);
|
||||
|
||||
tx.operations.push_back(op);
|
||||
}
|
||||
else if (type == TXLIST_TOKENS)
|
||||
{
|
||||
BlockchainTxOperationTypeETH op;
|
||||
op.addressFrom = jvalue["result"][i]["from"].as_string();
|
||||
op.addressTo = jvalue["result"][i]["to"].as_string();
|
||||
op.amountCoinId = GetCoinIdFromContractAddress(jvalue["result"][i]["contractAddress"].as_string());
|
||||
|
||||
if (!op.amountCoinId)
|
||||
{
|
||||
lerr << "Unknown token : " << jvalue["result"][i]["tokenSymbol"].as_string() << ". Tx will be ignored.";
|
||||
continue;
|
||||
}
|
||||
|
||||
Money divider = 1;
|
||||
int multiplier = strtol(jvalue["result"][i]["tokenDecimal"].as_string().c_str(),NULL,10);
|
||||
for (int i=0;i<multiplier;i++)
|
||||
{
|
||||
divider*=10;
|
||||
}
|
||||
|
||||
Money m(jvalue["result"][i]["value"]);
|
||||
m /= divider;
|
||||
op.amount = m;
|
||||
|
||||
ltrace << "Operation. Amount: " << op.amount << ". Coin : " << getCoinName(op.amountCoinId);
|
||||
|
||||
tx.operations.push_back(op);
|
||||
}
|
||||
else if (type == TXLIST_TOKENS)
|
||||
{
|
||||
lerr << "Internal tx are not supported.";
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
lerr << "Unknown tx type : " << type << ". Tx will be ignored.";
|
||||
continue;
|
||||
}
|
||||
|
||||
hashList.push_back(tx.hash);
|
||||
l.push_back(tx);
|
||||
}
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
bool BlockchainDataSourceETH_Etherscan::retrieveCacheFilesForAddress(string address)
|
||||
{
|
||||
if (!bfs::exists(getCacheFilenameForAddress(address,TXLIST_NORMAL)))
|
||||
{
|
||||
saveBlockchainAddressDataToCacheFile(address,TXLIST_NORMAL);
|
||||
}
|
||||
|
||||
if (!bfs::exists(getCacheFilenameForAddress(address,TXLIST_NORMAL)))
|
||||
{
|
||||
lerr << "Etherscan : cache file for " << TXLIST_NORMAL << " could not be found.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bfs::exists(getCacheFilenameForAddress(address,TXLIST_INTERNAL)))
|
||||
{
|
||||
saveBlockchainAddressDataToCacheFile(address,TXLIST_INTERNAL);
|
||||
}
|
||||
|
||||
if (!bfs::exists(getCacheFilenameForAddress(address,TXLIST_INTERNAL)))
|
||||
{
|
||||
lerr << "Etherscan : cache file for " << TXLIST_INTERNAL << " could not be found.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bfs::exists(getCacheFilenameForAddress(address,TXLIST_TOKENS)))
|
||||
{
|
||||
saveBlockchainAddressDataToCacheFile(address,TXLIST_TOKENS);
|
||||
}
|
||||
|
||||
if (!bfs::exists(getCacheFilenameForAddress(address,TXLIST_TOKENS)))
|
||||
{
|
||||
lerr << "Etherscan : cache file for " << TXLIST_TOKENS << " could not be found.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
string BlockchainDataSourceETH_Etherscan::getCacheFilenameForAddress(string address, string type)
|
||||
{
|
||||
string cacheFilename("data/cache/etherscan.io/" + type + "/" + address);
|
||||
return cacheFilename;
|
||||
}
|
||||
|
||||
void BlockchainDataSourceETH_Etherscan::saveBlockchainAddressDataToCacheFile(string address, string type)
|
||||
{
|
||||
try
|
||||
{
|
||||
sleep (5);
|
||||
|
||||
linfo << "Etherscan : querying API about " << type << " for " << address;
|
||||
|
||||
string sRequestURL = "/api?module=account&action="+type+"&address=";
|
||||
sRequestURL += address;
|
||||
http_client apiclient("https://api.etherscan.io");
|
||||
|
||||
m_currentRequestCacheFilename = getCacheFilenameForAddress(address,type);
|
||||
|
||||
apiclient.request(methods::GET,sRequestURL).then([](http_response response)
|
||||
{
|
||||
if (response.status_code() == status_codes::OK)
|
||||
{
|
||||
ldebug << "Etherscan : response OK.";
|
||||
return response.extract_json();
|
||||
}
|
||||
return pplx::task_from_result(json::value());
|
||||
})
|
||||
.then([this](pplx::task<json::value> previousTask)
|
||||
{
|
||||
if (previousTask.get() != json::value::null())
|
||||
{
|
||||
linfo << "Etherscan : saving query result to " << m_currentRequestCacheFilename;
|
||||
ofstream f;
|
||||
f.open(m_currentRequestCacheFilename);
|
||||
f << previousTask.get();
|
||||
f.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
lerr << "Etherscan : query result is empty. Nothing will be saved to the cache file.";
|
||||
}
|
||||
})
|
||||
.wait();
|
||||
}
|
||||
catch(const http::http_exception& e)
|
||||
{
|
||||
lerr << "Etherscan : failed to query API about " << address;
|
||||
}
|
||||
}
|
||||
|
||||
int BlockchainDataSourceETH_Etherscan::GetCoinIdFromContractAddress(string contractAddress)
|
||||
{
|
||||
// LPT - Livepeer Token
|
||||
if (contractAddress == "0x58b6a8a3302369daec383334672404ee733ab239")
|
||||
return CPFM_COIN_ID_LPT;
|
||||
|
||||
// LEND - EthLend - Migrated to AAVE.
|
||||
if (contractAddress == "0x80fb784b7ed66730e8b1dbd9820afd29931aab03")
|
||||
return CPFM_COIN_ID_LEND;
|
||||
|
||||
// AAVE
|
||||
if (contractAddress == "0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9")
|
||||
return CPFM_COIN_ID_AAVE;
|
||||
|
||||
// BCDT - Blockchain Certified Data Token.
|
||||
if (contractAddress == "0xacfa209fb73bf3dd5bbfb1101b9bc999c49062a5")
|
||||
return CPFM_COIN_ID_BCDT;
|
||||
|
||||
// KNC - Kyber Network
|
||||
if (contractAddress == "0xdd974d5c2e2928dea5f71b9825b8b646686bd200")
|
||||
return CPFM_COIN_ID_KNC;
|
||||
|
||||
// AIR - AirToken
|
||||
if (contractAddress == "0x27dce1ec4d3f72c3e457cc50354f1f975ddef488")
|
||||
return CPFM_COIN_ID_AIR;
|
||||
|
||||
lerr << "Unsupported contact address " << contractAddress;
|
||||
return 0;
|
||||
}
|
42
src/datasources/etherscan.h
Normal file
42
src/datasources/etherscan.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2021, evilny0
|
||||
*
|
||||
* This file is part of cpfm.
|
||||
*
|
||||
* cpfm is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* cpm is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with cpfm. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CPFM_DATASOURCE_ETHERSCAN_H_INCLUDED
|
||||
#define CPFM_DATASOURCE_ETHERSCAN_H_INCLUDED
|
||||
|
||||
#include "datasources/datasource.h"
|
||||
|
||||
|
||||
class BlockchainDataSourceETH_Etherscan
|
||||
{
|
||||
public:
|
||||
list<BlockchainTxDetailsTypeETH> getTxDetailsListForAddress(string address);
|
||||
|
||||
private:
|
||||
list<BlockchainTxDetailsTypeETH> getTxDetailsListForAddressTxType(string address, string type, list<string>& hashList);
|
||||
void saveBlockchainAddressDataToCacheFile(string address, string type);
|
||||
string getCacheFilenameForAddress(string address, string type);
|
||||
bool retrieveCacheFilesForAddress(string address);
|
||||
int GetCoinIdFromContractAddress(string contractAddress);
|
||||
|
||||
string m_currentRequestCacheFilename;
|
||||
};
|
||||
|
||||
#endif // CPFM_DATASOURCE_ETHERSCAN_H_INCLUDED
|
|
@ -47,10 +47,10 @@ void ExchangeHandlerKraken::analyzeUserData()
|
|||
getAccountBalance();
|
||||
analyzeAccountBalance();
|
||||
|
||||
//getAccountTrades();
|
||||
getAccountTrades();
|
||||
//analyzeAccountTrades();
|
||||
|
||||
//getAccountLedgers();
|
||||
getAccountLedgers();
|
||||
//analyzeAccountLedgers();
|
||||
}
|
||||
|
||||
|
@ -222,14 +222,13 @@ vector<unsigned char> ExchangeHandlerKraken::hmac_sha512(const vector<unsigned c
|
|||
unsigned int len = EVP_MAX_MD_SIZE;
|
||||
vector<unsigned char> digest(len);
|
||||
|
||||
HMAC_CTX ctx;
|
||||
HMAC_CTX_init(&ctx);
|
||||
HMAC_CTX* ctx = HMAC_CTX_new();
|
||||
|
||||
HMAC_Init_ex(&ctx, key.data(), key.size(), EVP_sha512(), NULL);
|
||||
HMAC_Update(&ctx, data.data(), data.size());
|
||||
HMAC_Final(&ctx, digest.data(), &len);
|
||||
HMAC_Init_ex(ctx, key.data(), key.size(), EVP_sha512(), NULL);
|
||||
HMAC_Update(ctx, data.data(), data.size());
|
||||
HMAC_Final(ctx, digest.data(), &len);
|
||||
|
||||
HMAC_CTX_cleanup(&ctx);
|
||||
HMAC_CTX_free(ctx);
|
||||
|
||||
return digest;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
#include <chrono>
|
||||
#include "log.h"
|
||||
|
||||
using namespace farm;
|
||||
using namespace cpfm;
|
||||
using namespace log;
|
||||
|
||||
const char* ErrorLogger::name() { return "ERROR"; }
|
||||
|
@ -34,6 +34,8 @@ const char* InfoLogger::name() { return "INFO "; }
|
|||
const char* InfoLogger::color() { return L_White; }
|
||||
const char* DebugLogger::name() { return "DEBUG"; }
|
||||
const char* DebugLogger::color() { return L_Teal; }
|
||||
const char* TraceLogger::name() { return "TRACE"; }
|
||||
const char* TraceLogger::color() { return L_Violet; }
|
||||
|
||||
LoggerBase::LoggerBase()
|
||||
{
|
||||
|
|
14
src/log.h
14
src/log.h
|
@ -28,13 +28,14 @@
|
|||
|
||||
using namespace std;
|
||||
|
||||
#define llog(X) farm::log::Logger<X>()
|
||||
#define lerr llog(farm::log::ErrorLogger)
|
||||
#define linfo llog(farm::log::InfoLogger)
|
||||
#define lwarn llog(farm::log::WarningLogger)
|
||||
#define ldebug llog(farm::log::DebugLogger)
|
||||
#define llog(X) cpfm::log::Logger<X>()
|
||||
#define lerr llog(cpfm::log::ErrorLogger)
|
||||
#define linfo llog(cpfm::log::InfoLogger)
|
||||
#define lwarn llog(cpfm::log::WarningLogger)
|
||||
#define ldebug llog(cpfm::log::DebugLogger)
|
||||
#define ltrace llog(cpfm::log::TraceLogger)
|
||||
|
||||
namespace farm { namespace log {
|
||||
namespace cpfm { namespace log {
|
||||
|
||||
#define L_Reset "\x1b[0m" // Text Reset
|
||||
|
||||
|
@ -107,6 +108,7 @@ struct ErrorLogger: public DefaultLogger { static const char* name(); static con
|
|||
struct WarningLogger: public DefaultLogger { static const char* name(); static const char* color(); };
|
||||
struct InfoLogger: public DefaultLogger { static const char* name(); static const char* color(); };
|
||||
struct DebugLogger: public DefaultLogger { static const char* name(); static const char* color(); };
|
||||
struct TraceLogger: public DefaultLogger { static const char* name(); static const char* color(); };
|
||||
|
||||
class LoggerBase
|
||||
{
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
Money::Money ()
|
||||
{
|
||||
|
||||
m_amount = 0;
|
||||
}
|
||||
|
||||
Money::Money (bmp::mpf_float_50 x)
|
||||
|
|
|
@ -54,7 +54,7 @@ public:
|
|||
Money& operator/=(const Money& x);
|
||||
friend Money operator/(Money a, const Money& b) { a /= b; return a; }
|
||||
|
||||
Money operator-() { return Money(-m_amount); }
|
||||
//Money operator-() { return Money(-m_amount); }
|
||||
|
||||
friend bool operator==(const Money& a, const int& b);
|
||||
friend bool operator!=(const Money& a, const int& b) { return !operator==(a,b); }
|
||||
|
|
24
src/pf.cpp
24
src/pf.cpp
|
@ -40,8 +40,8 @@ void PortfolioManager::doTestStuff()
|
|||
// Test user 1
|
||||
emptyUserBalances(1);
|
||||
|
||||
ExchangesManager exchangesManager(1);
|
||||
exchangesManager.analyzeUserAccounts();
|
||||
// ExchangesManager exchangesManager(1);
|
||||
// exchangesManager.analyzeUserAccounts();
|
||||
|
||||
WalletsManager walletsManager(1);
|
||||
walletsManager.analyzeUserWallets();
|
||||
|
@ -49,6 +49,7 @@ void PortfolioManager::doTestStuff()
|
|||
displayUserBalances(1);
|
||||
|
||||
// Test user 2
|
||||
/*
|
||||
emptyUserBalances(2);
|
||||
|
||||
ExchangesManager exchangesManagerTwo(2);
|
||||
|
@ -58,6 +59,7 @@ void PortfolioManager::doTestStuff()
|
|||
walletsManagerTwo.analyzeUserWallets();
|
||||
|
||||
displayUserBalances(2);
|
||||
*/
|
||||
}
|
||||
|
||||
void PortfolioManager::emptyUserBalances(int userId)
|
||||
|
@ -155,6 +157,24 @@ string getCoinName(int coinId)
|
|||
case CPFM_COIN_ID_BTG:
|
||||
s = "BTG";
|
||||
break;
|
||||
case CPFM_COIN_ID_LPT:
|
||||
s = "LPT";
|
||||
break;
|
||||
case CPFM_COIN_ID_BCDT:
|
||||
s = "BCDT";
|
||||
break;
|
||||
case CPFM_COIN_ID_AIR:
|
||||
s = "AIR";
|
||||
break;
|
||||
case CPFM_COIN_ID_AAVE:
|
||||
s = "AAVE";
|
||||
break;
|
||||
case CPFM_COIN_ID_LEND:
|
||||
s = "LEND";
|
||||
break;
|
||||
case CPFM_COIN_ID_KNC:
|
||||
s = "KNC";
|
||||
break;
|
||||
default:
|
||||
s = "Error!";
|
||||
break;
|
||||
|
|
33
src/pf.h
33
src/pf.h
|
@ -39,14 +39,31 @@
|
|||
#include "log.h"
|
||||
#include "money.h"
|
||||
|
||||
#define CPFM_COIN_ID_BTC 1
|
||||
#define CPFM_COIN_ID_LTC 2
|
||||
#define CPFM_COIN_ID_EUR 3
|
||||
#define CPFM_COIN_ID_ICN 4
|
||||
#define CPFM_COIN_ID_ETC 5
|
||||
#define CPFM_COIN_ID_ETH 6
|
||||
#define CPFM_COIN_ID_BCH 7
|
||||
#define CPFM_COIN_ID_BTG 8
|
||||
#define CPFM_COIN_ID_BTC 1
|
||||
#define CPFM_COIN_ID_LTC 2
|
||||
#define CPFM_COIN_ID_EUR 3
|
||||
#define CPFM_COIN_ID_ICN 4
|
||||
#define CPFM_COIN_ID_ETC 5
|
||||
#define CPFM_COIN_ID_ETH 6
|
||||
#define CPFM_COIN_ID_BCH 7
|
||||
#define CPFM_COIN_ID_BTG 8
|
||||
#define CPFM_COIN_ID_KNC 9
|
||||
#define CPFM_COIN_ID_AIR 10
|
||||
#define CPFM_COIN_ID_LEND 11
|
||||
#define CPFM_COIN_ID_AAVE 12
|
||||
#define CPFM_COIN_ID_LPT 13
|
||||
#define CPFM_COIN_ID_BCDT 14
|
||||
|
||||
class Time
|
||||
{
|
||||
public:
|
||||
void setFromUnixTime(time_t unixTime) { m_unixTime = unixTime; }
|
||||
const time_t toUnixTime() const { return m_unixTime; }
|
||||
|
||||
private:
|
||||
time_t m_unixTime;
|
||||
//date::sys_time<std::chrono::milliseconds>
|
||||
};
|
||||
|
||||
namespace bmp = boost::multiprecision;
|
||||
namespace bfs = boost::filesystem;
|
||||
|
|
|
@ -96,13 +96,13 @@ Money PriceSourceCryptoWatch::getCoinPriceFromData(int coinId)
|
|||
{
|
||||
case CPFM_COIN_ID_BTC:
|
||||
{
|
||||
Money x (jvalue["result"]["kraken:btceur"]);
|
||||
Money x (jvalue["result"]["market:kraken:btceur"]);
|
||||
m = x;
|
||||
break;
|
||||
}
|
||||
case CPFM_COIN_ID_LTC:
|
||||
{
|
||||
Money x (jvalue["result"]["kraken:ltceur"]);
|
||||
Money x (jvalue["result"]["market:kraken:ltceur"]);
|
||||
m = x;
|
||||
break;
|
||||
}
|
||||
|
@ -113,38 +113,79 @@ Money PriceSourceCryptoWatch::getCoinPriceFromData(int coinId)
|
|||
}
|
||||
case CPFM_COIN_ID_ICN:
|
||||
{
|
||||
Money x (jvalue["result"]["kraken:icnbtc"]);
|
||||
Money y (jvalue["result"]["kraken:btceur"]);
|
||||
ldebug << "ICN is not quoted in EUR. BTC price is : " << x;
|
||||
Money x (jvalue["result"]["market:kraken:icnbtc"]);
|
||||
Money y (jvalue["result"]["market:kraken:btceur"]);
|
||||
// ldebug << "ICN is not quoted in EUR. BTC price is : " << x;
|
||||
m = x*y;
|
||||
break;
|
||||
}
|
||||
case CPFM_COIN_ID_ETC:
|
||||
{
|
||||
Money x (jvalue["result"]["kraken:etceur"]);
|
||||
Money x (jvalue["result"]["market:kraken:etceur"]);
|
||||
m = x;
|
||||
break;
|
||||
}
|
||||
case CPFM_COIN_ID_ETH:
|
||||
{
|
||||
Money x (jvalue["result"]["kraken:etheur"]);
|
||||
Money x (jvalue["result"]["market:kraken:etheur"]);
|
||||
m = x;
|
||||
break;
|
||||
}
|
||||
case CPFM_COIN_ID_BCH:
|
||||
{
|
||||
Money x (jvalue["result"]["kraken:bcheur"]);
|
||||
Money x (jvalue["result"]["market:kraken:bcheur"]);
|
||||
m = x;
|
||||
break;
|
||||
}
|
||||
case CPFM_COIN_ID_BTG:
|
||||
{
|
||||
Money x (jvalue["result"]["bitfinex:btgbtc"]);
|
||||
Money y (jvalue["result"]["kraken:btceur"]);
|
||||
ldebug << "BTG is not quoted in EUR. BTC price is : " << x;
|
||||
Money x (jvalue["result"]["market:bitfinex:btgbtc"]);
|
||||
Money y (jvalue["result"]["market:kraken:btceur"]);
|
||||
// ldebug << "BTG is not quoted in EUR. BTC price is : " << x;
|
||||
m = x*y;
|
||||
break;
|
||||
}
|
||||
case CPFM_COIN_ID_KNC:
|
||||
{
|
||||
Money x (jvalue["result"]["market:binance:kncbtc"]);
|
||||
Money y (jvalue["result"]["market:kraken:btceur"]);
|
||||
// ldebug << "KNC is not quoted in EUR. BTC price is : " << x;
|
||||
m = x*y;
|
||||
break;
|
||||
}
|
||||
case CPFM_COIN_ID_AIR:
|
||||
{
|
||||
// ldebug << "AIR is not quoted. Returning 0.";
|
||||
m = 0;
|
||||
break;
|
||||
}
|
||||
case CPFM_COIN_ID_LEND:
|
||||
{
|
||||
Money x (jvalue["result"]["market:kraken:aaveeur"]);
|
||||
// ldebug << "LEND is not quoted. Using AAVE.";
|
||||
m = x/100;
|
||||
break;
|
||||
}
|
||||
case CPFM_COIN_ID_AAVE:
|
||||
{
|
||||
Money x (jvalue["result"]["market:kraken:aaveeur"]);
|
||||
m = x;
|
||||
break;
|
||||
}
|
||||
case CPFM_COIN_ID_LPT:
|
||||
{
|
||||
Money x (jvalue["result"]["market:poloniex:lptbtc"]);
|
||||
Money y (jvalue["result"]["market:kraken:btceur"]);
|
||||
// ldebug << "LPT is not quoted in EUR. BTC price is : " << x;
|
||||
m = x*y;
|
||||
break;
|
||||
}
|
||||
case CPFM_COIN_ID_BCDT:
|
||||
{
|
||||
// ldebug << "BCDT is not quoted. Returning 0.";
|
||||
m = 0;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
m = 0;
|
||||
|
|
838
src/sql.h
838
src/sql.h
|
@ -216,9 +216,32 @@ namespace TableWallets_
|
|||
};
|
||||
using _traits = sqlpp::make_traits<sqlpp::varchar>;
|
||||
};
|
||||
|
||||
struct Active
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "active";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T active;
|
||||
T& operator()()
|
||||
{
|
||||
return active;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return active;
|
||||
}
|
||||
};
|
||||
};
|
||||
using _traits = sqlpp::make_traits<sqlpp::boolean>;
|
||||
};
|
||||
}
|
||||
|
||||
struct TableWallets : sqlpp::table_t<TableWallets,TableWallets_::WalletId,TableWallets_::UserId,TableWallets_::TypeId,TableWallets_::WalletName>
|
||||
struct TableWallets : sqlpp::table_t<TableWallets,TableWallets_::WalletId,TableWallets_::UserId,TableWallets_::TypeId,TableWallets_::WalletName,TableWallets_::Active>
|
||||
{
|
||||
using _value_type = sqlpp::no_value_t;
|
||||
struct _alias_t
|
||||
|
@ -430,29 +453,6 @@ namespace TableWalletsTx_
|
|||
using _traits = sqlpp::make_traits<sqlpp::bigint>;
|
||||
};
|
||||
|
||||
struct BlockchainTxId
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "blockchain_tx_id";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T blockchain_tx_id;
|
||||
T& operator()()
|
||||
{
|
||||
return blockchain_tx_id;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return blockchain_tx_id;
|
||||
}
|
||||
};
|
||||
};
|
||||
using _traits = sqlpp::make_traits<sqlpp::varchar>;
|
||||
};
|
||||
|
||||
struct Amount
|
||||
{
|
||||
struct _alias_t
|
||||
|
@ -499,23 +499,92 @@ namespace TableWalletsTx_
|
|||
using _traits = sqlpp::make_traits<sqlpp::decimal>;
|
||||
};
|
||||
|
||||
struct Timestamp
|
||||
struct AmountCoinId
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "timestamp";
|
||||
static constexpr const char _literal[] = "amount_coin_id";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T timestamp;
|
||||
T amount_coin_id;
|
||||
T& operator()()
|
||||
{
|
||||
return timestamp;
|
||||
return amount_coin_id;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return timestamp;
|
||||
return amount_coin_id;
|
||||
}
|
||||
};
|
||||
};
|
||||
using _traits = sqlpp::make_traits<sqlpp::bigint>;
|
||||
};
|
||||
|
||||
struct FeeCoinId
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "fee_coin_id";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T fee_coin_id;
|
||||
T& operator()()
|
||||
{
|
||||
return fee_coin_id;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return fee_coin_id;
|
||||
}
|
||||
};
|
||||
};
|
||||
using _traits = sqlpp::make_traits<sqlpp::bigint>;
|
||||
};
|
||||
|
||||
struct UnixTime
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "unix_time";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T unix_time;
|
||||
T& operator()()
|
||||
{
|
||||
return unix_time;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return unix_time;
|
||||
}
|
||||
};
|
||||
};
|
||||
using _traits = sqlpp::make_traits<sqlpp::bigint>;
|
||||
};
|
||||
|
||||
struct OperationType
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "operation_type";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T operation_type;
|
||||
T& operator()()
|
||||
{
|
||||
return operation_type;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return operation_type;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -523,7 +592,7 @@ namespace TableWalletsTx_
|
|||
};
|
||||
}
|
||||
|
||||
struct TableWalletsTx : sqlpp::table_t<TableWalletsTx,TableWalletsTx_::TxId,TableWalletsTx_::WalletId,TableWalletsTx_::BlockchainTxId,TableWalletsTx_::Amount,TableWalletsTx_::Fee,TableWalletsTx_::Timestamp>
|
||||
struct TableWalletsTx : sqlpp::table_t<TableWalletsTx,TableWalletsTx_::TxId,TableWalletsTx_::WalletId,TableWalletsTx_::Amount,TableWalletsTx_::Fee,TableWalletsTx_::AmountCoinId,TableWalletsTx_::FeeCoinId,TableWalletsTx_::UnixTime,TableWalletsTx_::OperationType>
|
||||
{
|
||||
using _value_type = sqlpp::no_value_t;
|
||||
struct _alias_t
|
||||
|
@ -546,6 +615,691 @@ struct TableWalletsTx : sqlpp::table_t<TableWalletsTx,TableWalletsTx_::TxId,Tabl
|
|||
};
|
||||
};
|
||||
|
||||
namespace TableWalletsBtcRawTx_
|
||||
{
|
||||
struct RawTxId
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "raw_tx_id";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T raw_tx_id;
|
||||
T& operator()()
|
||||
{
|
||||
return raw_tx_id;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return raw_tx_id;
|
||||
}
|
||||
};
|
||||
};
|
||||
using _traits = sqlpp::make_traits<sqlpp::bigint>;
|
||||
};
|
||||
|
||||
struct WalletId
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "wallet_id";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T wallet_id;
|
||||
T& operator()()
|
||||
{
|
||||
return wallet_id;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return wallet_id;
|
||||
}
|
||||
};
|
||||
};
|
||||
using _traits = sqlpp::make_traits<sqlpp::bigint>;
|
||||
};
|
||||
}
|
||||
|
||||
struct TableWalletsBtcRawTx : sqlpp::table_t<TableWalletsBtcRawTx,TableWalletsBtcRawTx_::RawTxId,TableWalletsBtcRawTx_::WalletId>
|
||||
{
|
||||
using _value_type = sqlpp::no_value_t;
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "wallets_btc_raw_tx";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T wallets_btc_raw_tx;
|
||||
T& operator()()
|
||||
{
|
||||
return wallets_btc_raw_tx;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return wallets_btc_raw_tx;
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
namespace TableWalletsEthRawTx_
|
||||
{
|
||||
struct RawTxId
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "raw_tx_id";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T raw_tx_id;
|
||||
T& operator()()
|
||||
{
|
||||
return raw_tx_id;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return raw_tx_id;
|
||||
}
|
||||
};
|
||||
};
|
||||
using _traits = sqlpp::make_traits<sqlpp::bigint>;
|
||||
};
|
||||
|
||||
struct WalletId
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "wallet_id";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T wallet_id;
|
||||
T& operator()()
|
||||
{
|
||||
return wallet_id;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return wallet_id;
|
||||
}
|
||||
};
|
||||
};
|
||||
using _traits = sqlpp::make_traits<sqlpp::bigint>;
|
||||
};
|
||||
}
|
||||
|
||||
struct TableWalletsEthRawTx : sqlpp::table_t<TableWalletsEthRawTx,TableWalletsEthRawTx_::RawTxId,TableWalletsEthRawTx_::WalletId>
|
||||
{
|
||||
using _value_type = sqlpp::no_value_t;
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "wallets_eth_raw_tx";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T wallets_eth_raw_tx;
|
||||
T& operator()()
|
||||
{
|
||||
return wallets_eth_raw_tx;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return wallets_eth_raw_tx;
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
namespace TableBlockchainBtcRawTx_
|
||||
{
|
||||
struct RawTxId
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "raw_tx_id";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T raw_tx_id;
|
||||
T& operator()()
|
||||
{
|
||||
return raw_tx_id;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return raw_tx_id;
|
||||
}
|
||||
};
|
||||
};
|
||||
using _traits = sqlpp::make_traits<sqlpp::bigint>;
|
||||
};
|
||||
|
||||
struct BlockchainId
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "blockchain_id";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T blockchain_id;
|
||||
T& operator()()
|
||||
{
|
||||
return blockchain_id;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return blockchain_id;
|
||||
}
|
||||
};
|
||||
};
|
||||
using _traits = sqlpp::make_traits<sqlpp::bigint>;
|
||||
};
|
||||
|
||||
struct Hash
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "hash";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T hash;
|
||||
T& operator()()
|
||||
{
|
||||
return hash;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
};
|
||||
using _traits = sqlpp::make_traits<sqlpp::varchar>;
|
||||
};
|
||||
|
||||
struct UnixTime
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "unix_time";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T unix_time;
|
||||
T& operator()()
|
||||
{
|
||||
return unix_time;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return unix_time;
|
||||
}
|
||||
};
|
||||
};
|
||||
using _traits = sqlpp::make_traits<sqlpp::bigint>;
|
||||
};
|
||||
}
|
||||
|
||||
struct TableBlockchainBtcRawTx : sqlpp::table_t<TableBlockchainBtcRawTx,TableBlockchainBtcRawTx_::RawTxId,TableBlockchainBtcRawTx_::BlockchainId,TableBlockchainBtcRawTx_::Hash,TableBlockchainBtcRawTx_::UnixTime>
|
||||
{
|
||||
using _value_type = sqlpp::no_value_t;
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "blockchain_btc_raw_tx";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T blockchain_btc_raw_tx;
|
||||
T& operator()()
|
||||
{
|
||||
return blockchain_btc_raw_tx;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return blockchain_btc_raw_tx;
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
namespace TableBlockchainBtcRawTxDetails_
|
||||
{
|
||||
struct RawTxDetailId
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "raw_tx_detail_id";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T raw_tx_detail_id;
|
||||
T& operator()()
|
||||
{
|
||||
return raw_tx_detail_id;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return raw_tx_detail_id;
|
||||
}
|
||||
};
|
||||
};
|
||||
using _traits = sqlpp::make_traits<sqlpp::bigint>;
|
||||
};
|
||||
|
||||
struct RawTxId
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "raw_tx_id";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T raw_tx_id;
|
||||
T& operator()()
|
||||
{
|
||||
return raw_tx_id;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return raw_tx_id;
|
||||
}
|
||||
};
|
||||
};
|
||||
using _traits = sqlpp::make_traits<sqlpp::bigint>;
|
||||
};
|
||||
|
||||
struct Amount
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "amount";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T amount;
|
||||
T& operator()()
|
||||
{
|
||||
return amount;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return amount;
|
||||
}
|
||||
};
|
||||
};
|
||||
using _traits = sqlpp::make_traits<sqlpp::decimal>;
|
||||
};
|
||||
|
||||
struct Address
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "address";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T address;
|
||||
T& operator()()
|
||||
{
|
||||
return address;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return address;
|
||||
}
|
||||
};
|
||||
};
|
||||
using _traits = sqlpp::make_traits<sqlpp::varchar>;
|
||||
};
|
||||
}
|
||||
|
||||
struct TableBlockchainBtcRawTxDetails : sqlpp::table_t<TableBlockchainBtcRawTxDetails,TableBlockchainBtcRawTxDetails_::RawTxDetailId,TableBlockchainBtcRawTxDetails_::RawTxId,TableBlockchainBtcRawTxDetails_::Amount,TableBlockchainBtcRawTxDetails_::Address>
|
||||
{
|
||||
using _value_type = sqlpp::no_value_t;
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "blockchain_btc_raw_tx_details";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T blockchain_btc_raw_tx_details;
|
||||
T& operator()()
|
||||
{
|
||||
return blockchain_btc_raw_tx_details;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return blockchain_btc_raw_tx_details;
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
namespace TableBlockchainEthRawTx_
|
||||
{
|
||||
struct RawTxId
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "raw_tx_id";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T raw_tx_id;
|
||||
T& operator()()
|
||||
{
|
||||
return raw_tx_id;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return raw_tx_id;
|
||||
}
|
||||
};
|
||||
};
|
||||
using _traits = sqlpp::make_traits<sqlpp::bigint>;
|
||||
};
|
||||
|
||||
struct BlockchainId
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "blockchain_id";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T blockchain_id;
|
||||
T& operator()()
|
||||
{
|
||||
return blockchain_id;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return blockchain_id;
|
||||
}
|
||||
};
|
||||
};
|
||||
using _traits = sqlpp::make_traits<sqlpp::bigint>;
|
||||
};
|
||||
|
||||
struct Hash
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "hash";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T hash;
|
||||
T& operator()()
|
||||
{
|
||||
return hash;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
};
|
||||
using _traits = sqlpp::make_traits<sqlpp::varchar>;
|
||||
};
|
||||
|
||||
struct UnixTime
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "unix_time";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T unix_time;
|
||||
T& operator()()
|
||||
{
|
||||
return unix_time;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return unix_time;
|
||||
}
|
||||
};
|
||||
};
|
||||
using _traits = sqlpp::make_traits<sqlpp::bigint>;
|
||||
};
|
||||
|
||||
struct Fee
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "fee";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T fee;
|
||||
T& operator()()
|
||||
{
|
||||
return fee;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return fee;
|
||||
}
|
||||
};
|
||||
};
|
||||
using _traits = sqlpp::make_traits<sqlpp::decimal>;
|
||||
};
|
||||
}
|
||||
|
||||
struct TableBlockchainEthRawTx : sqlpp::table_t<TableBlockchainEthRawTx,TableBlockchainEthRawTx_::RawTxId,TableBlockchainEthRawTx_::BlockchainId,TableBlockchainEthRawTx_::Hash,TableBlockchainEthRawTx_::UnixTime,TableBlockchainEthRawTx_::Fee>
|
||||
{
|
||||
using _value_type = sqlpp::no_value_t;
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "blockchain_eth_raw_tx";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T blockchain_eth_raw_tx;
|
||||
T& operator()()
|
||||
{
|
||||
return blockchain_eth_raw_tx;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return blockchain_eth_raw_tx;
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
namespace TableBlockchainEthRawTxDetails_
|
||||
{
|
||||
struct RawTxDetailId
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "raw_tx_detail_id";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T raw_tx_detail_id;
|
||||
T& operator()()
|
||||
{
|
||||
return raw_tx_detail_id;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return raw_tx_detail_id;
|
||||
}
|
||||
};
|
||||
};
|
||||
using _traits = sqlpp::make_traits<sqlpp::bigint>;
|
||||
};
|
||||
|
||||
struct RawTxId
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "raw_tx_id";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T raw_tx_id;
|
||||
T& operator()()
|
||||
{
|
||||
return raw_tx_id;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return raw_tx_id;
|
||||
}
|
||||
};
|
||||
};
|
||||
using _traits = sqlpp::make_traits<sqlpp::bigint>;
|
||||
};
|
||||
|
||||
struct AddressFrom
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "address_from";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T address_from;
|
||||
T& operator()()
|
||||
{
|
||||
return address_from;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return address_from;
|
||||
}
|
||||
};
|
||||
};
|
||||
using _traits = sqlpp::make_traits<sqlpp::varchar>;
|
||||
};
|
||||
|
||||
struct AddressTo
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "address_to";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T address_to;
|
||||
T& operator()()
|
||||
{
|
||||
return address_to;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return address_to;
|
||||
}
|
||||
};
|
||||
};
|
||||
using _traits = sqlpp::make_traits<sqlpp::varchar>;
|
||||
};
|
||||
|
||||
struct AmountCoinId
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "amount_coin_id";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T amount_coin_id;
|
||||
T& operator()()
|
||||
{
|
||||
return amount_coin_id;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return amount_coin_id;
|
||||
}
|
||||
};
|
||||
};
|
||||
using _traits = sqlpp::make_traits<sqlpp::bigint>;
|
||||
};
|
||||
|
||||
struct Amount
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "amount";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T amount;
|
||||
T& operator()()
|
||||
{
|
||||
return amount;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return amount;
|
||||
}
|
||||
};
|
||||
};
|
||||
using _traits = sqlpp::make_traits<sqlpp::decimal>;
|
||||
};
|
||||
};
|
||||
|
||||
struct TableBlockchainEthRawTxDetails : sqlpp::table_t<TableBlockchainEthRawTxDetails,TableBlockchainEthRawTxDetails_::RawTxDetailId,TableBlockchainEthRawTxDetails_::RawTxId,TableBlockchainEthRawTxDetails_::AddressFrom,TableBlockchainEthRawTxDetails_::AddressTo,TableBlockchainEthRawTxDetails_::AmountCoinId,TableBlockchainEthRawTxDetails_::Amount>
|
||||
{
|
||||
using _value_type = sqlpp::no_value_t;
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "blockchain_eth_raw_tx_details";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T blockchain_eth_raw_tx_details;
|
||||
T& operator()()
|
||||
{
|
||||
return blockchain_eth_raw_tx_details;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return blockchain_eth_raw_tx_details;
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
namespace TableExchangesLedgers_
|
||||
{
|
||||
struct LedgerId
|
||||
|
@ -663,23 +1417,23 @@ namespace TableExchangesLedgers_
|
|||
using _traits = sqlpp::make_traits<sqlpp::varchar>;
|
||||
};
|
||||
|
||||
struct Timestamp
|
||||
struct UnixTime
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "timestamp";
|
||||
static constexpr const char _literal[] = "unix_time";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T timestamp;
|
||||
T unix_time;
|
||||
T& operator()()
|
||||
{
|
||||
return timestamp;
|
||||
return unix_time;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return timestamp;
|
||||
return unix_time;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -756,7 +1510,7 @@ namespace TableExchangesLedgers_
|
|||
};
|
||||
}
|
||||
|
||||
struct TableExchangesLedgers : sqlpp::table_t<TableExchangesLedgers,TableExchangesLedgers_::LedgerId,TableExchangesLedgers_::AccountId,TableExchangesLedgers_::ExchangeLedgerId,TableExchangesLedgers_::ExchangeReferenceId,TableExchangesLedgers_::Timestamp,TableExchangesLedgers_::OperationType,TableExchangesLedgers_::CoinId,TableExchangesLedgers_::Amount,TableExchangesLedgers_::Fee>
|
||||
struct TableExchangesLedgers : sqlpp::table_t<TableExchangesLedgers,TableExchangesLedgers_::LedgerId,TableExchangesLedgers_::AccountId,TableExchangesLedgers_::ExchangeLedgerId,TableExchangesLedgers_::ExchangeReferenceId,TableExchangesLedgers_::UnixTime,TableExchangesLedgers_::OperationType,TableExchangesLedgers_::CoinId,TableExchangesLedgers_::Amount,TableExchangesLedgers_::Fee>
|
||||
{
|
||||
using _value_type = sqlpp::no_value_t;
|
||||
struct _alias_t
|
||||
|
@ -919,23 +1673,23 @@ namespace TableExchangesTrades_
|
|||
using _traits = sqlpp::make_traits<sqlpp::bigint>;
|
||||
};
|
||||
|
||||
struct Timestamp
|
||||
struct UnixTime
|
||||
{
|
||||
struct _alias_t
|
||||
{
|
||||
static constexpr const char _literal[] = "timestamp";
|
||||
static constexpr const char _literal[] = "unix_time";
|
||||
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
|
||||
template <typename T>
|
||||
struct _member_t
|
||||
{
|
||||
T timestamp;
|
||||
T unix_time;
|
||||
T& operator()()
|
||||
{
|
||||
return timestamp;
|
||||
return unix_time;
|
||||
}
|
||||
const T& operator()() const
|
||||
{
|
||||
return timestamp;
|
||||
return unix_time;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -1104,7 +1858,7 @@ namespace TableExchangesTrades_
|
|||
};
|
||||
}
|
||||
|
||||
struct TableExchangesTrades : sqlpp::table_t<TableExchangesTrades,TableExchangesTrades_::TradeId,TableExchangesTrades_::AccountId,TableExchangesTrades_::ExchangeTradeId,TableExchangesTrades_::ExchangeOrderId,TableExchangesTrades_::BaseCoinId,TableExchangesTrades_::QuoteCoinId,TableExchangesTrades_::Timestamp,TableExchangesTrades_::Type,TableExchangesTrades_::OrderType,TableExchangesTrades_::Price,TableExchangesTrades_::Cost,TableExchangesTrades_::Fee,TableExchangesTrades_::Volume,TableExchangesTrades_::FeeCoinId>
|
||||
struct TableExchangesTrades : sqlpp::table_t<TableExchangesTrades,TableExchangesTrades_::TradeId,TableExchangesTrades_::AccountId,TableExchangesTrades_::ExchangeTradeId,TableExchangesTrades_::ExchangeOrderId,TableExchangesTrades_::BaseCoinId,TableExchangesTrades_::QuoteCoinId,TableExchangesTrades_::UnixTime,TableExchangesTrades_::Type,TableExchangesTrades_::OrderType,TableExchangesTrades_::Price,TableExchangesTrades_::Cost,TableExchangesTrades_::Fee,TableExchangesTrades_::Volume,TableExchangesTrades_::FeeCoinId>
|
||||
{
|
||||
using _value_type = sqlpp::no_value_t;
|
||||
struct _alias_t
|
||||
|
|
|
@ -19,9 +19,32 @@
|
|||
*/
|
||||
|
||||
#include "wallet.h"
|
||||
#include "wallets/btc.h"
|
||||
#include "wallets/bch.h"
|
||||
#include "wallets/eth.h"
|
||||
#include "wallets/wallet_btc.h"
|
||||
#include "wallets/wallet_bch.h"
|
||||
#include "wallets/wallet_eth.h"
|
||||
|
||||
string getBlockchainName(int blockchainId)
|
||||
{
|
||||
string s;
|
||||
|
||||
switch(blockchainId)
|
||||
{
|
||||
case CPFM_BLOCKCHAIN_ID_BTC:
|
||||
s = "BTC";
|
||||
break;
|
||||
case CPFM_BLOCKCHAIN_ID_BCH:
|
||||
s = "BCH";
|
||||
break;
|
||||
case CPFM_BLOCKCHAIN_ID_ETH:
|
||||
s = "ETH";
|
||||
break;
|
||||
default:
|
||||
s = "Error!";
|
||||
break;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
WalletsManager::WalletsManager(int userId)
|
||||
{
|
||||
|
@ -38,38 +61,34 @@ void WalletsManager::analyzeUserWallets()
|
|||
mysql::connection db(getMysqlConfig());
|
||||
const auto wallets = TableWallets{};
|
||||
|
||||
// Identify coins, we need one analyzer object per coin.
|
||||
list<WalletHandler*> handlers;
|
||||
for (const auto& row: db.run(select(wallets.type_id).from(wallets).where(wallets.user_id == m_userId).group_by(wallets.type_id)))
|
||||
list<Wallet*> userWallets;
|
||||
for (const auto& row: db.run(select(wallets.type_id,wallets.wallet_id).from(wallets).where(wallets.user_id == m_userId and wallets.active == true)))
|
||||
{
|
||||
int typeId = row.type_id;
|
||||
WalletHandler* walletHandler;
|
||||
switch (typeId)
|
||||
Wallet* wallet;
|
||||
switch (row.type_id)
|
||||
{
|
||||
case CPFM_WALLET_TYPE_ID_BTC:
|
||||
walletHandler = new WalletHandlerBTC(m_userId);
|
||||
handlers.push_back(walletHandler);
|
||||
wallet = new WalletBTC(row.wallet_id);
|
||||
userWallets.push_back(wallet);
|
||||
break;
|
||||
case CPFM_WALLET_TYPE_ID_BCH:
|
||||
walletHandler = new WalletHandlerBCH(m_userId);
|
||||
handlers.push_back(walletHandler);
|
||||
wallet = new WalletBCH(row.wallet_id);
|
||||
userWallets.push_back(wallet);
|
||||
break;
|
||||
case CPFM_WALLET_TYPE_ID_ETH:
|
||||
walletHandler = new WalletHandlerETH(m_userId);
|
||||
handlers.push_back(walletHandler);
|
||||
wallet = new WalletETH(row.wallet_id);
|
||||
userWallets.push_back(wallet);
|
||||
break;
|
||||
default:
|
||||
lerr << "Unsupported wallet type: " << typeId;
|
||||
lerr << "Unsupported wallet type: " << row.type_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
emptyWalletsTx();
|
||||
|
||||
for (auto const& handler: handlers)
|
||||
for (auto const& wallet: userWallets)
|
||||
{
|
||||
handler->analyzeUserWallets();
|
||||
delete handler;
|
||||
wallet->update();
|
||||
delete wallet;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,20 +104,16 @@ void WalletsManager::emptyWalletsTx()
|
|||
userWallets.push_back(row.wallet_id);
|
||||
}
|
||||
|
||||
for (const auto& id: userWallets)
|
||||
for (const auto& idWallet: userWallets)
|
||||
{
|
||||
db.run(remove_from(wallets_tx).where(wallets_tx.wallet_id == id));
|
||||
list<int> userWalletTx;
|
||||
for (const auto& row: db.run(select(wallets_tx.tx_id).from(wallets_tx).where(wallets_tx.wallet_id == idWallet)))
|
||||
{
|
||||
userWalletTx.push_back(row.tx_id);
|
||||
}
|
||||
|
||||
db.run(remove_from(wallets_tx).where(wallets_tx.wallet_id == idWallet));
|
||||
}
|
||||
|
||||
linfo << "Emptied Wallets Tx DB table for user " << m_userId;
|
||||
}
|
||||
|
||||
WalletHandler::WalletHandler(int userId)
|
||||
{
|
||||
m_userId = userId;
|
||||
}
|
||||
|
||||
WalletHandler::~WalletHandler()
|
||||
{
|
||||
|
||||
}
|
||||
|
|
20
src/wallet.h
20
src/wallet.h
|
@ -27,6 +27,17 @@
|
|||
#define CPFM_WALLET_TYPE_ID_ETH 2
|
||||
#define CPFM_WALLET_TYPE_ID_BCH 3
|
||||
|
||||
#define CPFM_WALLET_OPERATION_UNKNOWN 0
|
||||
#define CPFM_WALLET_OPERATION_MINING_MINTED 1
|
||||
#define CPFM_WALLET_OPERATION_MINING_POOL 2
|
||||
#define CPFM_WALLET_OPERATION_TRANSFERT 3
|
||||
|
||||
#define CPFM_BLOCKCHAIN_ID_BTC 1
|
||||
#define CPFM_BLOCKCHAIN_ID_BCH 2
|
||||
#define CPFM_BLOCKCHAIN_ID_ETH 3
|
||||
|
||||
string getBlockchainName(int blockchainId);
|
||||
|
||||
class WalletsManager
|
||||
{
|
||||
public:
|
||||
|
@ -41,16 +52,15 @@ private:
|
|||
int m_userId;
|
||||
};
|
||||
|
||||
class WalletHandler
|
||||
class Wallet
|
||||
{
|
||||
public:
|
||||
WalletHandler(int userId);
|
||||
virtual ~WalletHandler();
|
||||
Wallet (int walletId) { m_walletId = walletId; }
|
||||
|
||||
virtual void analyzeUserWallets() = 0;
|
||||
virtual void update() = 0;
|
||||
|
||||
protected:
|
||||
int m_userId;
|
||||
int m_walletId;
|
||||
};
|
||||
|
||||
#endif // CPFM_WALLET_H_INCLUDED
|
|
@ -1,316 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018, evilny0
|
||||
*
|
||||
* This file is part of cpfm.
|
||||
*
|
||||
* cpfm is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* cpm is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with cpfm. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "wallets/bch.h"
|
||||
|
||||
WalletHandlerBCH::WalletHandlerBCH(int userId) : WalletHandler(userId)
|
||||
{
|
||||
}
|
||||
|
||||
WalletHandlerBCH::~WalletHandlerBCH()
|
||||
{
|
||||
}
|
||||
|
||||
void WalletHandlerBCH::analyzeUserWallets()
|
||||
{
|
||||
linfo << "Analyzing user " << m_userId << " BCH wallets.";
|
||||
|
||||
mysql::connection db(getMysqlConfig());
|
||||
const auto wallets = TableWallets{};
|
||||
const auto wallets_addresses = TableWalletsAddresses{};
|
||||
|
||||
map<int, list<string>> userWallets;
|
||||
list<string> userAddresses;
|
||||
for (const auto &row : db.run(select(wallets.wallet_id, wallets.type_id, wallets_addresses.address).from(wallets.cross_join(wallets_addresses)).where(wallets.user_id == m_userId and wallets.wallet_id == wallets_addresses.wallet_id and wallets.type_id == CPFM_WALLET_TYPE_ID_BCH)))
|
||||
{
|
||||
userWallets[row.wallet_id].push_back(row.address);
|
||||
userAddresses.push_back(row.address);
|
||||
}
|
||||
|
||||
for (const auto &w : userWallets)
|
||||
{
|
||||
addUserWalletDataToDB(w.first, w.second, userAddresses);
|
||||
analyzeUserWalletData(w.first);
|
||||
}
|
||||
}
|
||||
|
||||
void WalletHandlerBCH::analyzeUserWalletData(int walletId)
|
||||
{
|
||||
mysql::connection db(getMysqlConfig());
|
||||
const auto wallets_tx = TableWalletsTx{};
|
||||
const auto wallets_balances = TableWalletsBalances{};
|
||||
|
||||
// Loop is on blockchain tx id, so first, get blockhain tx id list.
|
||||
list<string> blockchainTxs;
|
||||
for (const auto &row : db.run(select(wallets_tx.blockchain_tx_id).from(wallets_tx).where(wallets_tx.wallet_id == walletId).group_by(wallets_tx.blockchain_tx_id).order_by(wallets_tx.timestamp.asc())))
|
||||
{
|
||||
blockchainTxs.push_back(row.blockchain_tx_id);
|
||||
}
|
||||
|
||||
ldebug << "------------------------------------------------------------";
|
||||
|
||||
Money walletBalance = 0;
|
||||
Money walletInputs = 0;
|
||||
Money walletOutputs = 0;
|
||||
Money walletOutputFees = 0;
|
||||
for (const auto &txId: blockchainTxs)
|
||||
{
|
||||
Money txAmount = 0;
|
||||
Money txFee = 0;
|
||||
|
||||
for (const auto &row : db.run(select(wallets_tx.amount, wallets_tx.fee).from(wallets_tx).where(wallets_tx.blockchain_tx_id == txId and wallets_tx.wallet_id == walletId)))
|
||||
{
|
||||
Money amount(row.amount);
|
||||
Money fee(row.fee);
|
||||
|
||||
if (txFee == 0)
|
||||
txFee = fee;
|
||||
|
||||
txAmount += amount;
|
||||
}
|
||||
|
||||
txAmount += txFee;
|
||||
|
||||
if (txAmount < 0)
|
||||
walletOutputs += txAmount;
|
||||
else
|
||||
walletInputs += txAmount;
|
||||
|
||||
walletOutputFees += txFee;
|
||||
|
||||
walletBalance = walletInputs + walletOutputs - walletOutputFees;
|
||||
|
||||
string sReason = "?";
|
||||
|
||||
if (txAmount < 0)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
ldebug << "Tx: " << txId
|
||||
<< ". Amount: " << txAmount
|
||||
<< ". Outputs: " << walletOutputs
|
||||
<< ". Inputs: " << walletInputs
|
||||
<< ". Fees: " << txFee
|
||||
<< ". Balance: " << walletBalance
|
||||
<< ". Reason: " << sReason;
|
||||
*/
|
||||
}
|
||||
|
||||
linfo << "Wallet " << walletId << " balance is: " << walletBalance;
|
||||
|
||||
db(insert_into(wallets_balances).set(wallets_balances.wallet_id = walletId, wallets_balances.coin_id = CPFM_COIN_ID_BCH, wallets_balances.balance = walletBalance.toBoostMpf()));
|
||||
}
|
||||
|
||||
void WalletHandlerBCH::addUserWalletDataToDB(int walletId, list<string> walletAddresses, list<string> userAddresses)
|
||||
{
|
||||
linfo << "Analyzing wallet " << walletId << ".";
|
||||
|
||||
for (const auto &a : walletAddresses)
|
||||
{
|
||||
AddressHandlerBCH addr(a, walletId);
|
||||
addr.setWalletAddresses(walletAddresses);
|
||||
addr.setUserAddresses(userAddresses);
|
||||
addr.addAddressDataToDB();
|
||||
}
|
||||
}
|
||||
|
||||
AddressHandlerBCH::AddressHandlerBCH(string address, int walletId)
|
||||
{
|
||||
m_address = address;
|
||||
m_walletId = walletId;
|
||||
}
|
||||
|
||||
AddressHandlerBCH::~AddressHandlerBCH()
|
||||
{
|
||||
}
|
||||
|
||||
string AddressHandlerBCH::getCacheFilename()
|
||||
{
|
||||
string cacheFilename("data/cache/bch/" + m_address);
|
||||
return cacheFilename;
|
||||
}
|
||||
|
||||
void AddressHandlerBCH::getAddressData()
|
||||
{
|
||||
linfo << "Getting data for address: " << m_address << ".";
|
||||
|
||||
if (!bfs::exists(getCacheFilename()))
|
||||
{
|
||||
getBlockchainAddressData();
|
||||
}
|
||||
}
|
||||
|
||||
void AddressHandlerBCH::addAddressDataToDB()
|
||||
{
|
||||
linfo << "Analyzing data for address: " << m_address << ".";
|
||||
|
||||
getAddressData();
|
||||
addCachedAddressDataToDB();
|
||||
}
|
||||
|
||||
void AddressHandlerBCH::getBlockchainAddressData()
|
||||
{
|
||||
try
|
||||
{
|
||||
string sRequestURL = "/insight-api/txs/?address=";
|
||||
sRequestURL += m_address;
|
||||
http_client apiclient("https://cashexplorer.bitcoin.com");
|
||||
apiclient.request(methods::GET, sRequestURL).then([](http_response response)
|
||||
{
|
||||
if (response.status_code() == status_codes::OK)
|
||||
{
|
||||
return response.extract_json();
|
||||
}
|
||||
return pplx::task_from_result(json::value());
|
||||
})
|
||||
.then([this](pplx::task<json::value> previousTask) {
|
||||
ofstream f;
|
||||
f.open(getCacheFilename());
|
||||
f << previousTask.get();
|
||||
f.close();
|
||||
})
|
||||
.wait();
|
||||
}
|
||||
catch (const http::http_exception &e)
|
||||
{
|
||||
lerr << "Failed to query cashexplorer.bitcoin.com about " << m_address;
|
||||
}
|
||||
}
|
||||
|
||||
void AddressHandlerBCH::addCachedAddressDataToDB()
|
||||
{
|
||||
ifstream f;
|
||||
f.open(getCacheFilename());
|
||||
json::value jvalue = json::value::parse(f);
|
||||
f.close();
|
||||
|
||||
addAddressDataJSONToDB(jvalue);
|
||||
}
|
||||
|
||||
void AddressHandlerBCH::addAddressDataJSONToDB(json::value jvalue)
|
||||
{
|
||||
mysql::connection db(getMysqlConfig());
|
||||
const auto wallets_tx = TableWalletsTx{};
|
||||
|
||||
string myAddr = m_address;
|
||||
linfo << "Analyzing address: " << myAddr;
|
||||
|
||||
for (int i = 0; i < jvalue["txs"].size(); i++)
|
||||
{
|
||||
string txid = jvalue["txs"][i]["txid"].as_string();
|
||||
int timestamp = jvalue["txs"][i]["time"].as_integer();
|
||||
|
||||
Money txTotalOutputAmount;
|
||||
Money txTotalInputAmount;
|
||||
Money txMyOutputAmount;
|
||||
Money txMyInputAmount;
|
||||
bool bToMyAddr = false;
|
||||
bool bFromMyAddr = false;
|
||||
list<string> inputAddrList;
|
||||
list<string> outputAddrList;
|
||||
|
||||
for (int j = 0; j < jvalue["txs"][i]["vin"].size(); j++)
|
||||
{
|
||||
string inputAddr = jvalue["txs"][i]["vin"][j]["addr"].as_string();
|
||||
Money amount(jvalue["txs"][i]["vin"][j]["value"]);
|
||||
txTotalInputAmount += amount;
|
||||
|
||||
if (inputAddr == myAddr)
|
||||
{
|
||||
bFromMyAddr = true;
|
||||
txMyInputAmount += amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
inputAddrList.push_back(inputAddr);
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < jvalue["txs"][i]["vout"].size(); j++)
|
||||
{
|
||||
string outputAddr = jvalue["txs"][i]["vout"][j]["scriptPubKey"]["addresses"][0].as_string();
|
||||
Money amount(jvalue["txs"][i]["vout"][j]["value"]);
|
||||
txTotalOutputAmount += amount;
|
||||
|
||||
if (outputAddr == myAddr)
|
||||
{
|
||||
bToMyAddr = true;
|
||||
txMyOutputAmount += amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
outputAddrList.push_back(outputAddr);
|
||||
}
|
||||
}
|
||||
|
||||
Money amount;
|
||||
Money fee;
|
||||
if (bFromMyAddr)
|
||||
{
|
||||
amount = -txMyInputAmount;
|
||||
fee = txTotalInputAmount - txTotalOutputAmount;
|
||||
|
||||
for (auto const &addr : inputAddrList)
|
||||
{
|
||||
bool bFoundInUserAddresses = false;
|
||||
for (auto const &userAddr : m_userAddresses)
|
||||
{
|
||||
if (!userAddr.compare(addr))
|
||||
{
|
||||
bFoundInUserAddresses = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bFoundInUserAddresses)
|
||||
{
|
||||
lwarn << "User configuration is missing this address: " << addr;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const &addr : outputAddrList)
|
||||
{
|
||||
bool bFoundInUserAddresses = false;
|
||||
for (auto const &userAddr : m_userAddresses)
|
||||
{
|
||||
if (!userAddr.compare(addr))
|
||||
{
|
||||
bFoundInUserAddresses = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bFoundInUserAddresses)
|
||||
{
|
||||
linfo << "Potential other address to analyze: " << L_Cyan << addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bToMyAddr)
|
||||
{
|
||||
amount = txMyOutputAmount;
|
||||
fee = 0;
|
||||
}
|
||||
|
||||
db(insert_into(wallets_tx).set(wallets_tx.wallet_id = m_walletId, wallets_tx.blockchain_tx_id = txid, wallets_tx.amount = amount.toBoostMpf(), wallets_tx.fee = fee.toBoostMpf(), wallets_tx.timestamp = timestamp));
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018, evilny0
|
||||
*
|
||||
* This file is part of cpfm.
|
||||
*
|
||||
* cpfm is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* cpm is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with cpfm. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CPFM_WALLET_BCH_H_INCLUDED
|
||||
#define CPFM_WALLET_BCH_H_INCLUDED
|
||||
|
||||
#include "wallet.h"
|
||||
|
||||
|
||||
class WalletHandlerBCH : public WalletHandler
|
||||
{
|
||||
public:
|
||||
WalletHandlerBCH(int userId);
|
||||
virtual ~WalletHandlerBCH();
|
||||
|
||||
virtual void analyzeUserWallets();
|
||||
|
||||
private:
|
||||
void addUserWalletDataToDB(int walletId, list<string> walletAddresses, list<string> userAddresses);
|
||||
void analyzeUserWalletData(int walletId);
|
||||
};
|
||||
|
||||
class AddressHandlerBCH
|
||||
{
|
||||
public:
|
||||
AddressHandlerBCH(string address, int walletId);
|
||||
virtual ~AddressHandlerBCH();
|
||||
|
||||
void getAddressData();
|
||||
void addAddressDataToDB();
|
||||
void setWalletAddresses(list<string> walletAddresses) { m_walletAddresses = walletAddresses; }
|
||||
void setUserAddresses(list<string> userAddresses) { m_userAddresses = userAddresses; }
|
||||
|
||||
private:
|
||||
void getBlockchainAddressData();
|
||||
void addCachedAddressDataToDB();
|
||||
void addAddressDataJSONToDB(json::value jvalue);
|
||||
string getCacheFilename();
|
||||
|
||||
string m_address;
|
||||
int m_walletId;
|
||||
list<string> m_userAddresses;
|
||||
list<string> m_walletAddresses;
|
||||
};
|
||||
|
||||
#endif // CPFM_WALLET_BTC_H_INCLUDED
|
|
@ -1,332 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018, evilny0
|
||||
*
|
||||
* This file is part of cpfm.
|
||||
*
|
||||
* cpfm is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* cpm is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with cpfm. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "wallets/btc.h"
|
||||
|
||||
WalletHandlerBTC::WalletHandlerBTC(int userId) : WalletHandler(userId)
|
||||
{
|
||||
}
|
||||
|
||||
WalletHandlerBTC::~WalletHandlerBTC()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void WalletHandlerBTC::analyzeUserWallets()
|
||||
{
|
||||
linfo << "Analyzing user " << m_userId << " BTC wallets.";
|
||||
|
||||
mysql::connection db(getMysqlConfig());
|
||||
const auto wallets = TableWallets{};
|
||||
const auto wallets_addresses = TableWalletsAddresses{};
|
||||
|
||||
map<int,list<string>> userWallets;
|
||||
list<string> userAddresses;
|
||||
for (const auto& row: db.run(select(wallets.wallet_id,wallets.type_id,wallets_addresses.address).from(wallets.cross_join(wallets_addresses)).where(wallets.user_id == m_userId and wallets.wallet_id == wallets_addresses.wallet_id and wallets.type_id == CPFM_WALLET_TYPE_ID_BTC)))
|
||||
{
|
||||
userWallets[row.wallet_id].push_back(row.address);
|
||||
userAddresses.push_back(row.address);
|
||||
}
|
||||
|
||||
for (const auto& w: userWallets)
|
||||
{
|
||||
addUserWalletDataToDB(w.first,w.second,userAddresses);
|
||||
analyzeUserWalletData(w.first);
|
||||
}
|
||||
}
|
||||
|
||||
void WalletHandlerBTC::analyzeUserWalletData(int walletId)
|
||||
{
|
||||
mysql::connection db(getMysqlConfig());
|
||||
const auto wallets_tx = TableWalletsTx{};
|
||||
const auto wallets_balances = TableWalletsBalances{};
|
||||
|
||||
// Loop is on blockchain tx id, so first, get blockchain tx list.
|
||||
list<string> blockchainTxs;
|
||||
for (const auto& row: db.run(select(wallets_tx.blockchain_tx_id).from(wallets_tx).where(wallets_tx.wallet_id == walletId).group_by(wallets_tx.blockchain_tx_id).order_by(wallets_tx.timestamp.asc())))
|
||||
{
|
||||
blockchainTxs.push_back(row.blockchain_tx_id);
|
||||
}
|
||||
|
||||
ldebug << "------------------------------------------------------------";
|
||||
|
||||
Money walletBalance = 0;
|
||||
Money walletInputs = 0;
|
||||
Money walletOutputs = 0;
|
||||
Money walletOutputFees = 0;
|
||||
for (const auto& txId: blockchainTxs)
|
||||
{
|
||||
Money txAmount = 0;
|
||||
Money txFee = 0;
|
||||
|
||||
for (const auto& row: db.run(select(wallets_tx.amount, wallets_tx.fee).from(wallets_tx).where(wallets_tx.blockchain_tx_id == txId and wallets_tx.wallet_id == walletId)))
|
||||
{
|
||||
Money amount(row.amount);
|
||||
Money fee(row.fee);
|
||||
|
||||
if (txFee == 0)
|
||||
txFee = fee;
|
||||
|
||||
txAmount += amount;
|
||||
}
|
||||
|
||||
txAmount += txFee;
|
||||
|
||||
if (txAmount<0)
|
||||
walletOutputs += txAmount;
|
||||
else
|
||||
walletInputs += txAmount;
|
||||
|
||||
walletOutputFees += txFee;
|
||||
|
||||
walletBalance = walletInputs + walletOutputs - walletOutputFees;
|
||||
|
||||
string sReason = "?";
|
||||
|
||||
if (txAmount < 0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
ldebug << "Tx: " << txId
|
||||
<< ". Amount: " << txAmount
|
||||
<< ". Outputs: " << walletOutputs
|
||||
<< ". Inputs: " << walletInputs
|
||||
<< ". Fees: " << txFee
|
||||
<< ". Balance: " << walletBalance
|
||||
<< ". Reason: " << sReason;
|
||||
*/
|
||||
}
|
||||
|
||||
linfo << "Wallet " << walletId << " balance is: " << walletBalance;
|
||||
|
||||
db(insert_into(wallets_balances).set(
|
||||
wallets_balances.wallet_id = walletId,
|
||||
wallets_balances.coin_id = CPFM_COIN_ID_BTC,
|
||||
wallets_balances.balance = walletBalance.toBoostMpf()
|
||||
));
|
||||
}
|
||||
|
||||
void WalletHandlerBTC::addUserWalletDataToDB(int walletId, list<string> walletAddresses, list<string> userAddresses)
|
||||
{
|
||||
linfo << "Analyzing wallet " << walletId << ".";
|
||||
|
||||
for (const auto& a: walletAddresses)
|
||||
{
|
||||
AddressHandlerBTC addr(a,walletId);
|
||||
addr.setWalletAddresses(walletAddresses);
|
||||
addr.setUserAddresses(userAddresses);
|
||||
addr.addAddressDataToDB();
|
||||
}
|
||||
}
|
||||
|
||||
AddressHandlerBTC::AddressHandlerBTC(string address, int walletId)
|
||||
{
|
||||
m_address = address;
|
||||
m_walletId = walletId;
|
||||
}
|
||||
|
||||
AddressHandlerBTC::~AddressHandlerBTC()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
string AddressHandlerBTC::getCacheFilename()
|
||||
{
|
||||
string cacheFilename("data/cache/btc/" + m_address);
|
||||
return cacheFilename;
|
||||
}
|
||||
|
||||
void AddressHandlerBTC::getAddressData()
|
||||
{
|
||||
linfo << "Getting data for address: " << m_address << ".";
|
||||
|
||||
if (!bfs::exists(getCacheFilename()))
|
||||
{
|
||||
getBlockchainAddressData();
|
||||
}
|
||||
}
|
||||
|
||||
void AddressHandlerBTC::addAddressDataToDB()
|
||||
{
|
||||
linfo << "Analyzing data for address: " << m_address << ".";
|
||||
|
||||
getAddressData();
|
||||
addCachedAddressDataToDB();
|
||||
}
|
||||
|
||||
void AddressHandlerBTC::getBlockchainAddressData()
|
||||
{
|
||||
try
|
||||
{
|
||||
string sRequestURL = "/fr/rawaddr/";
|
||||
sRequestURL += m_address;
|
||||
http_client apiclient("https://blockchain.info/");
|
||||
apiclient.request(methods::GET,sRequestURL).then([](http_response response)
|
||||
{
|
||||
if (response.status_code() == status_codes::OK)
|
||||
{
|
||||
return response.extract_json();
|
||||
}
|
||||
return pplx::task_from_result(json::value());
|
||||
})
|
||||
.then([this](pplx::task<json::value> previousTask)
|
||||
{
|
||||
ofstream f;
|
||||
f.open(getCacheFilename());
|
||||
f << previousTask.get();
|
||||
f.close();
|
||||
})
|
||||
.wait();
|
||||
}
|
||||
catch(const http::http_exception& e)
|
||||
{
|
||||
lerr << "Failed to query blockchain.info about " << m_address;
|
||||
}
|
||||
}
|
||||
|
||||
void AddressHandlerBTC::addCachedAddressDataToDB()
|
||||
{
|
||||
ifstream f;
|
||||
f.open(getCacheFilename());
|
||||
json::value jvalue = json::value::parse(f);
|
||||
f.close();
|
||||
|
||||
addAddressDataJSONToDB(jvalue);
|
||||
}
|
||||
|
||||
void AddressHandlerBTC::addAddressDataJSONToDB(json::value jvalue)
|
||||
{
|
||||
mysql::connection db(getMysqlConfig());
|
||||
const auto wallets_tx = TableWalletsTx{};
|
||||
|
||||
string myAddr = jvalue["address"].as_string();
|
||||
linfo << "Analyzing address: " << myAddr;
|
||||
|
||||
Money balance = jvalue["final_balance"].as_integer();;
|
||||
|
||||
for (int i=0;i<jvalue["txs"].size();i++)
|
||||
{
|
||||
string hash = jvalue["txs"][i]["hash"].as_string();
|
||||
int timestamp = jvalue["txs"][i]["time"].as_integer();
|
||||
|
||||
Money txTotalOutputAmount;
|
||||
Money txTotalInputAmount;
|
||||
Money txMyOutputAmount;
|
||||
Money txMyInputAmount;
|
||||
bool bToMyAddr = false;
|
||||
bool bFromMyAddr = false;
|
||||
list<string> inputAddrList;
|
||||
list<string> outputAddrList;
|
||||
|
||||
for (int j=0;j<jvalue["txs"][i]["inputs"].size();j++)
|
||||
{
|
||||
string inputAddr = jvalue["txs"][i]["inputs"][j]["prev_out"]["addr"].as_string();
|
||||
__int64 amount = jvalue["txs"][i]["inputs"][j]["prev_out"]["value"].as_integer();
|
||||
txTotalInputAmount += amount;
|
||||
|
||||
if (inputAddr == myAddr)
|
||||
{
|
||||
bFromMyAddr = true;
|
||||
txMyInputAmount += amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
inputAddrList.push_back(inputAddr);
|
||||
}
|
||||
}
|
||||
|
||||
for (int j=0;j<jvalue["txs"][i]["out"].size();j++)
|
||||
{
|
||||
string outputAddr = jvalue["txs"][i]["out"][j]["addr"].as_string();
|
||||
__int64 amount = jvalue["txs"][i]["out"][j]["value"].as_integer();
|
||||
txTotalOutputAmount += amount;
|
||||
|
||||
if (outputAddr == myAddr)
|
||||
{
|
||||
bToMyAddr = true;
|
||||
txMyOutputAmount += amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
outputAddrList.push_back(outputAddr);
|
||||
}
|
||||
}
|
||||
|
||||
Money amount;
|
||||
Money fee;
|
||||
if (bFromMyAddr)
|
||||
{
|
||||
amount = -txMyInputAmount/100000000;
|
||||
fee = txTotalInputAmount/100000000 - txTotalOutputAmount/100000000;
|
||||
|
||||
for (auto const& addr: inputAddrList)
|
||||
{
|
||||
bool bFoundInUserAddresses = false;
|
||||
for (auto const& userAddr: m_userAddresses)
|
||||
{
|
||||
if (!userAddr.compare(addr))
|
||||
{
|
||||
bFoundInUserAddresses = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bFoundInUserAddresses)
|
||||
{
|
||||
lwarn << "User configuration is missing this address: " << addr;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const& addr: outputAddrList)
|
||||
{
|
||||
bool bFoundInUserAddresses = false;
|
||||
for (auto const& userAddr: m_userAddresses)
|
||||
{
|
||||
if (!userAddr.compare(addr))
|
||||
{
|
||||
bFoundInUserAddresses = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bFoundInUserAddresses)
|
||||
{
|
||||
linfo << "Potential other address to analyze: " << L_Cyan << addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bToMyAddr)
|
||||
{
|
||||
amount = txMyOutputAmount/100000000;
|
||||
fee = 0;
|
||||
}
|
||||
|
||||
db(insert_into(wallets_tx).set(
|
||||
wallets_tx.wallet_id = m_walletId,
|
||||
wallets_tx.blockchain_tx_id = hash,
|
||||
wallets_tx.amount = amount.toBoostMpf(),
|
||||
wallets_tx.fee = fee.toBoostMpf(),
|
||||
wallets_tx.timestamp = timestamp
|
||||
));
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018, evilny0
|
||||
*
|
||||
* This file is part of cpfm.
|
||||
*
|
||||
* cpfm is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* cpm is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with cpfm. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CPFM_WALLET_BTC_H_INCLUDED
|
||||
#define CPFM_WALLET_BTC_H_INCLUDED
|
||||
|
||||
#include "wallet.h"
|
||||
|
||||
|
||||
class WalletHandlerBTC : public WalletHandler
|
||||
{
|
||||
public:
|
||||
WalletHandlerBTC(int userId);
|
||||
virtual ~WalletHandlerBTC();
|
||||
|
||||
virtual void analyzeUserWallets();
|
||||
|
||||
private:
|
||||
void addUserWalletDataToDB(int walletId, list<string> walletAddresses, list<string> userAddresses);
|
||||
void analyzeUserWalletData(int walletId);
|
||||
};
|
||||
|
||||
class AddressHandlerBTC
|
||||
{
|
||||
public:
|
||||
AddressHandlerBTC(string address, int walletId);
|
||||
virtual ~AddressHandlerBTC();
|
||||
|
||||
void getAddressData();
|
||||
void addAddressDataToDB();
|
||||
void setWalletAddresses(list<string> walletAddresses) { m_walletAddresses = walletAddresses; }
|
||||
void setUserAddresses(list<string> userAddresses) { m_userAddresses = userAddresses; }
|
||||
|
||||
private:
|
||||
void getBlockchainAddressData();
|
||||
void addCachedAddressDataToDB();
|
||||
void addAddressDataJSONToDB(json::value jvalue);
|
||||
string getCacheFilename();
|
||||
|
||||
string m_address;
|
||||
int m_walletId;
|
||||
list<string> m_userAddresses;
|
||||
list<string> m_walletAddresses;
|
||||
};
|
||||
|
||||
#endif // CPFM_WALLET_BTC_H_INCLUDED
|
|
@ -1,353 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018, evilny0
|
||||
*
|
||||
* This file is part of cpfm.
|
||||
*
|
||||
* cpfm is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* cpm is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with cpfm. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "wallets/eth.h"
|
||||
|
||||
WalletHandlerETH::WalletHandlerETH(int userId) : WalletHandler(userId)
|
||||
{
|
||||
}
|
||||
|
||||
WalletHandlerETH::~WalletHandlerETH()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void WalletHandlerETH::analyzeUserWallets()
|
||||
{
|
||||
linfo << "Analyzing user " << m_userId << " ETH wallets.";
|
||||
|
||||
mysql::connection db(getMysqlConfig());
|
||||
const auto wallets = TableWallets{};
|
||||
const auto wallets_addresses = TableWalletsAddresses{};
|
||||
|
||||
map<int,list<string>> userWallets;
|
||||
list<string> userAddresses;
|
||||
for (const auto& row: db.run(select(wallets.wallet_id,wallets.type_id,wallets_addresses.address).from(wallets.cross_join(wallets_addresses)).where(wallets.user_id == m_userId and wallets.wallet_id == wallets_addresses.wallet_id and wallets.type_id == CPFM_WALLET_TYPE_ID_ETH)))
|
||||
{
|
||||
userWallets[row.wallet_id].push_back(row.address);
|
||||
userAddresses.push_back(row.address);
|
||||
}
|
||||
|
||||
for (const auto& w: userWallets)
|
||||
{
|
||||
addUserWalletDataToDB(w.first,w.second,userAddresses);
|
||||
//analyzeUserWalletData(w.first);
|
||||
}
|
||||
}
|
||||
|
||||
void WalletHandlerETH::analyzeUserWalletData(int walletId)
|
||||
{
|
||||
mysql::connection db(getMysqlConfig());
|
||||
const auto wallets_tx = TableWalletsTx{};
|
||||
const auto wallets_balances = TableWalletsBalances{};
|
||||
|
||||
// Loop is on blockchain tx id, so first, get blockchain tx list.
|
||||
list<string> blockchainTxs;
|
||||
for (const auto& row: db.run(select(wallets_tx.blockchain_tx_id).from(wallets_tx).where(wallets_tx.wallet_id == walletId).group_by(wallets_tx.blockchain_tx_id).order_by(wallets_tx.timestamp.asc())))
|
||||
{
|
||||
blockchainTxs.push_back(row.blockchain_tx_id);
|
||||
}
|
||||
|
||||
ldebug << "------------------------------------------------------------";
|
||||
|
||||
Money walletBalance = 0;
|
||||
Money walletInputs = 0;
|
||||
Money walletOutputs = 0;
|
||||
Money walletOutputFees = 0;
|
||||
for (const auto& txId: blockchainTxs)
|
||||
{
|
||||
Money txAmount = 0;
|
||||
Money txFee = 0;
|
||||
|
||||
for (const auto& row: db.run(select(wallets_tx.amount, wallets_tx.fee).from(wallets_tx).where(wallets_tx.blockchain_tx_id == txId and wallets_tx.wallet_id == walletId)))
|
||||
{
|
||||
Money amount(row.amount);
|
||||
Money fee(row.fee);
|
||||
|
||||
if (txFee == 0)
|
||||
txFee = fee;
|
||||
|
||||
txAmount += amount;
|
||||
}
|
||||
|
||||
txAmount += txFee;
|
||||
|
||||
if (txAmount<0)
|
||||
walletOutputs += txAmount;
|
||||
else
|
||||
walletInputs += txAmount;
|
||||
|
||||
walletOutputFees += txFee;
|
||||
|
||||
walletBalance = walletInputs + walletOutputs - walletOutputFees;
|
||||
|
||||
string sReason = "?";
|
||||
|
||||
if (txAmount < 0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
ldebug << "Tx: " << txId
|
||||
<< ". Amount: " << txAmount
|
||||
//<< ". Outputs: " << walletOutputs
|
||||
//<< ". Inputs: " << walletInputs
|
||||
<< ". Fees: " << txFee
|
||||
<< ". Balance: " << walletBalance
|
||||
<< ". Reason: " << sReason;
|
||||
*/
|
||||
}
|
||||
|
||||
linfo << "Wallet " << walletId << " balance is: " << walletBalance;
|
||||
|
||||
db(insert_into(wallets_balances).set(
|
||||
wallets_balances.wallet_id = walletId,
|
||||
wallets_balances.coin_id = CPFM_COIN_ID_ETH,
|
||||
wallets_balances.balance = walletBalance.toBoostMpf()
|
||||
));
|
||||
}
|
||||
|
||||
void WalletHandlerETH::addUserWalletDataToDB(int walletId, list<string> walletAddresses, list<string> userAddresses)
|
||||
{
|
||||
linfo << "Analyzing wallet " << walletId << ".";
|
||||
|
||||
for (const auto& a: walletAddresses)
|
||||
{
|
||||
AddressHandlerETH addr(a,walletId);
|
||||
addr.setWalletAddresses(walletAddresses);
|
||||
addr.setUserAddresses(userAddresses);
|
||||
addr.addAddressDataToDB();
|
||||
}
|
||||
}
|
||||
|
||||
AddressHandlerETH::AddressHandlerETH(string address, int walletId)
|
||||
{
|
||||
m_address = address;
|
||||
m_walletId = walletId;
|
||||
}
|
||||
|
||||
AddressHandlerETH::~AddressHandlerETH()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
string AddressHandlerETH::getCacheFilename()
|
||||
{
|
||||
string cacheFilename("data/cache/eth/" + m_address);
|
||||
return cacheFilename;
|
||||
}
|
||||
|
||||
void AddressHandlerETH::getAddressData()
|
||||
{
|
||||
linfo << "Getting data for address: " << m_address << ".";
|
||||
|
||||
if (!bfs::exists(getCacheFilename()))
|
||||
{
|
||||
getBlockchainAddressData();
|
||||
}
|
||||
}
|
||||
|
||||
void AddressHandlerETH::addAddressDataToDB()
|
||||
{
|
||||
linfo << "Analyzing data for address: " << m_address << ".";
|
||||
|
||||
getAddressData();
|
||||
addCachedAddressDataToDB();
|
||||
}
|
||||
|
||||
void AddressHandlerETH::getBlockchainAddressData()
|
||||
{
|
||||
try
|
||||
{
|
||||
string sRequestURL = "/api?module=account&action=balance&address=";
|
||||
sRequestURL += m_address;
|
||||
http_client apiclient("https://api.etherscan.io");
|
||||
apiclient.request(methods::GET,sRequestURL).then([](http_response response)
|
||||
{
|
||||
if (response.status_code() == status_codes::OK)
|
||||
{
|
||||
return response.extract_json();
|
||||
}
|
||||
return pplx::task_from_result(json::value());
|
||||
})
|
||||
.then([this](pplx::task<json::value> previousTask)
|
||||
{
|
||||
ofstream f;
|
||||
f.open(getCacheFilename());
|
||||
f << previousTask.get();
|
||||
f.close();
|
||||
})
|
||||
.wait();
|
||||
}
|
||||
catch(const http::http_exception& e)
|
||||
{
|
||||
lerr << "Failed to query cashexplorer.bitcoin.com about " << m_address;
|
||||
}
|
||||
}
|
||||
|
||||
void AddressHandlerETH::addCachedAddressDataToDB()
|
||||
{
|
||||
ifstream f;
|
||||
f.open(getCacheFilename());
|
||||
json::value jvalue = json::value::parse(f);
|
||||
f.close();
|
||||
|
||||
addAddressDataJSONToDB(jvalue);
|
||||
}
|
||||
|
||||
void AddressHandlerETH::addAddressDataJSONToDB(json::value jvalue)
|
||||
{
|
||||
mysql::connection db(getMysqlConfig());
|
||||
const auto wallets_tx = TableWalletsTx{};
|
||||
|
||||
string myAddr = m_address;
|
||||
linfo << "Analyzing address: " << myAddr;
|
||||
|
||||
if (!jvalue["message"].as_string().compare("OK") && !jvalue["status"].as_string().compare("1"))
|
||||
{
|
||||
Money walletBalance (jvalue["result"]);
|
||||
Money divider("1000000000000000000");
|
||||
walletBalance /= divider;
|
||||
|
||||
linfo << "Wallet " << m_walletId << " balance is: " << walletBalance;
|
||||
|
||||
mysql::connection db(getMysqlConfig());
|
||||
const auto wallets_tx = TableWalletsTx{};
|
||||
const auto wallets_balances = TableWalletsBalances{};
|
||||
|
||||
db(insert_into(wallets_balances).set(
|
||||
wallets_balances.wallet_id = m_walletId,
|
||||
wallets_balances.coin_id = CPFM_COIN_ID_ETH,
|
||||
wallets_balances.balance = walletBalance.toBoostMpf()
|
||||
));
|
||||
}
|
||||
|
||||
/*
|
||||
//Money balance = jvalue["final_balance"].as_integer();;
|
||||
|
||||
for (int i=0;i<jvalue["txs"].size();i++)
|
||||
{
|
||||
string txid = jvalue["txs"][i]["txid"].as_string();
|
||||
int timestamp = jvalue["txs"][i]["time"].as_integer();
|
||||
|
||||
Money txTotalOutputAmount;
|
||||
Money txTotalInputAmount;
|
||||
Money txMyOutputAmount;
|
||||
Money txMyInputAmount;
|
||||
bool bToMyAddr = false;
|
||||
bool bFromMyAddr = false;
|
||||
list<string> inputAddrList;
|
||||
list<string> outputAddrList;
|
||||
|
||||
for (int j=0;j<jvalue["txs"][i]["vin"].size();j++)
|
||||
{
|
||||
string inputAddr = jvalue["txs"][i]["vin"][j]["addr"].as_string();
|
||||
Money amount (jvalue["txs"][i]["vin"][j]["value"]);
|
||||
txTotalInputAmount += amount;
|
||||
|
||||
if (inputAddr == myAddr)
|
||||
{
|
||||
bFromMyAddr = true;
|
||||
txMyInputAmount += amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
inputAddrList.push_back(inputAddr);
|
||||
}
|
||||
}
|
||||
|
||||
for (int j=0;j<jvalue["txs"][i]["vout"].size();j++)
|
||||
{
|
||||
string outputAddr = jvalue["txs"][i]["vout"][j]["scriptPubKey"]["addresses"][0].as_string();
|
||||
Money amount(jvalue["txs"][i]["vout"][j]["value"]);
|
||||
txTotalOutputAmount += amount;
|
||||
|
||||
if (outputAddr == myAddr)
|
||||
{
|
||||
bToMyAddr = true;
|
||||
txMyOutputAmount += amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
outputAddrList.push_back(outputAddr);
|
||||
}
|
||||
}
|
||||
|
||||
Money amount;
|
||||
Money fee;
|
||||
if (bFromMyAddr)
|
||||
{
|
||||
amount = -txMyInputAmount;
|
||||
fee = txTotalInputAmount - txTotalOutputAmount;
|
||||
|
||||
for (auto const& addr: inputAddrList)
|
||||
{
|
||||
bool bFoundInUserAddresses = false;
|
||||
for (auto const& userAddr: m_userAddresses)
|
||||
{
|
||||
if (!userAddr.compare(addr))
|
||||
{
|
||||
bFoundInUserAddresses = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bFoundInUserAddresses)
|
||||
{
|
||||
lwarn << "User configuration is missing this address: " << addr;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const& addr: outputAddrList)
|
||||
{
|
||||
bool bFoundInUserAddresses = false;
|
||||
for (auto const& userAddr: m_userAddresses)
|
||||
{
|
||||
if (!userAddr.compare(addr))
|
||||
{
|
||||
bFoundInUserAddresses = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bFoundInUserAddresses)
|
||||
{
|
||||
linfo << "Potential other address to analyze: " << L_Cyan << addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bToMyAddr)
|
||||
{
|
||||
amount = txMyOutputAmount;
|
||||
fee = 0;
|
||||
}
|
||||
|
||||
db(insert_into(wallets_tx).set(
|
||||
wallets_tx.wallet_id = m_walletId,
|
||||
wallets_tx.tx_hash = txid,
|
||||
wallets_tx.amount = amount.toBoostMpf(),
|
||||
wallets_tx.fee = fee.toBoostMpf(),
|
||||
wallets_tx.timestamp = timestamp
|
||||
));
|
||||
}
|
||||
*/
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018, evilny0
|
||||
*
|
||||
* This file is part of cpfm.
|
||||
*
|
||||
* cpfm is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* cpm is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with cpfm. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CPFM_WALLET_ETH_H_INCLUDED
|
||||
#define CPFM_WALLET_ETH_H_INCLUDED
|
||||
|
||||
#include "wallet.h"
|
||||
|
||||
|
||||
class WalletHandlerETH : public WalletHandler
|
||||
{
|
||||
public:
|
||||
WalletHandlerETH(int userId);
|
||||
virtual ~WalletHandlerETH();
|
||||
|
||||
virtual void analyzeUserWallets();
|
||||
|
||||
private:
|
||||
void addUserWalletDataToDB(int walletId, list<string> walletAddresses, list<string> userAddresses);
|
||||
void analyzeUserWalletData(int walletId);
|
||||
};
|
||||
|
||||
class AddressHandlerETH
|
||||
{
|
||||
public:
|
||||
AddressHandlerETH(string address, int walletId);
|
||||
virtual ~AddressHandlerETH();
|
||||
|
||||
void getAddressData();
|
||||
void addAddressDataToDB();
|
||||
void setWalletAddresses(list<string> walletAddresses) { m_walletAddresses = walletAddresses; }
|
||||
void setUserAddresses(list<string> userAddresses) { m_userAddresses = userAddresses; }
|
||||
|
||||
private:
|
||||
void getBlockchainAddressData();
|
||||
void addCachedAddressDataToDB();
|
||||
void addAddressDataJSONToDB(json::value jvalue);
|
||||
string getCacheFilename();
|
||||
|
||||
string m_address;
|
||||
int m_walletId;
|
||||
list<string> m_userAddresses;
|
||||
list<string> m_walletAddresses;
|
||||
};
|
||||
|
||||
#endif // CPFM_WALLET_ETH_H_INCLUDED
|
27
src/wallets/wallet_bch.cpp
Normal file
27
src/wallets/wallet_bch.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2018, evilny0
|
||||
*
|
||||
* This file is part of cpfm.
|
||||
*
|
||||
* cpfm is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* cpm is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with cpfm. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "wallets/wallet_bch.h"
|
||||
|
||||
list<BlockchainTxDetailsTypeBTC> WalletBCH::getTxDetailsListForAddresses(list<string> addresses)
|
||||
{
|
||||
BlockchainDataSourceBCH src;
|
||||
return src.getTxDetailsListForAddresses(addresses);
|
||||
}
|
36
src/wallets/wallet_bch.h
Normal file
36
src/wallets/wallet_bch.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (c) 2018, evilny0
|
||||
*
|
||||
* This file is part of cpfm.
|
||||
*
|
||||
* cpfm is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* cpm is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with cpfm. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CPFM_WALLET_BCH_H_INCLUDED
|
||||
#define CPFM_WALLET_BCH_H_INCLUDED
|
||||
|
||||
#include "wallets/wallet_type_btc.h"
|
||||
#include "datasources/datasource.h"
|
||||
|
||||
class WalletBCH : public WalletTypeBTC
|
||||
{
|
||||
public:
|
||||
WalletBCH(int walletId):WalletTypeBTC(walletId) { m_coinId = CPFM_COIN_ID_BCH; m_blockchainId = CPFM_BLOCKCHAIN_ID_BCH; m_walletTypeId = CPFM_WALLET_TYPE_ID_BCH; }
|
||||
|
||||
protected:
|
||||
virtual list<BlockchainTxDetailsTypeBTC> getTxDetailsListForAddresses(list<string> addresses);
|
||||
};
|
||||
|
||||
#endif // CPFM_WALLET_BCH_H_INCLUDED
|
27
src/wallets/wallet_btc.cpp
Normal file
27
src/wallets/wallet_btc.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2018, evilny0
|
||||
*
|
||||
* This file is part of cpfm.
|
||||
*
|
||||
* cpfm is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* cpm is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with cpfm. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "wallets/wallet_btc.h"
|
||||
|
||||
list<BlockchainTxDetailsTypeBTC> WalletBTC::getTxDetailsListForAddresses(list<string> addresses)
|
||||
{
|
||||
BlockchainDataSourceBTC src;
|
||||
return src.getTxDetailsListForAddresses(addresses);
|
||||
}
|
36
src/wallets/wallet_btc.h
Normal file
36
src/wallets/wallet_btc.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (c) 2018, evilny0
|
||||
*
|
||||
* This file is part of cpfm.
|
||||
*
|
||||
* cpfm is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* cpm is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with cpfm. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CPFM_WALLET_BTC_H_INCLUDED
|
||||
#define CPFM_WALLET_BTC_H_INCLUDED
|
||||
|
||||
#include "wallets/wallet_type_btc.h"
|
||||
#include "datasources/datasource.h"
|
||||
|
||||
class WalletBTC : public WalletTypeBTC
|
||||
{
|
||||
public:
|
||||
WalletBTC(int walletId):WalletTypeBTC(walletId) { m_coinId = CPFM_COIN_ID_BTC; m_blockchainId = CPFM_BLOCKCHAIN_ID_BTC; m_walletTypeId = CPFM_WALLET_TYPE_ID_BTC; }
|
||||
|
||||
protected:
|
||||
virtual list<BlockchainTxDetailsTypeBTC> getTxDetailsListForAddresses(list<string> addresses);
|
||||
};
|
||||
|
||||
#endif // CPFM_WALLET_BTC_H_INCLUDED
|
27
src/wallets/wallet_eth.cpp
Normal file
27
src/wallets/wallet_eth.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2018, evilny0
|
||||
*
|
||||
* This file is part of cpfm.
|
||||
*
|
||||
* cpfm is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* cpm is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with cpfm. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "wallets/wallet_eth.h"
|
||||
|
||||
list<BlockchainTxDetailsTypeETH> WalletETH::getTxDetailsListForAddress(string address)
|
||||
{
|
||||
BlockchainDataSourceETH src;
|
||||
return src.getTxDetailsListForAddress(address);
|
||||
}
|
36
src/wallets/wallet_eth.h
Normal file
36
src/wallets/wallet_eth.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (c) 2018, evilny0
|
||||
*
|
||||
* This file is part of cpfm.
|
||||
*
|
||||
* cpfm is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* cpm is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with cpfm. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CPFM_WALLET_ETH_H_INCLUDED
|
||||
#define CPFM_WALLET_ETH_H_INCLUDED
|
||||
|
||||
#include "wallets/wallet_type_eth.h"
|
||||
#include "datasources/datasource.h"
|
||||
|
||||
class WalletETH : public WalletTypeETH
|
||||
{
|
||||
public:
|
||||
WalletETH(int walletId):WalletTypeETH(walletId) { m_coinId = CPFM_COIN_ID_ETH; m_blockchainId = CPFM_BLOCKCHAIN_ID_ETH; m_walletTypeId = CPFM_WALLET_TYPE_ID_ETH; }
|
||||
|
||||
protected:
|
||||
virtual list<BlockchainTxDetailsTypeETH> getTxDetailsListForAddress(string address);
|
||||
};
|
||||
|
||||
#endif // CPFM_WALLET_ETH_H_INCLUDED
|
324
src/wallets/wallet_type_btc.cpp
Normal file
324
src/wallets/wallet_type_btc.cpp
Normal file
|
@ -0,0 +1,324 @@
|
|||
/*
|
||||
* Copyright (c) 2021, evilny0
|
||||
*
|
||||
* This file is part of cpfm.
|
||||
*
|
||||
* cpfm is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* cpm is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with cpfm. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "wallets/wallet_type_btc.h"
|
||||
|
||||
SQLPP_ALIAS_PROVIDER(max_raw_tx_id);
|
||||
|
||||
void WalletTypeBTC::update()
|
||||
{
|
||||
if (!m_walletId)
|
||||
{
|
||||
lerr << "Wallet id not defined. Ignoring update.";
|
||||
}
|
||||
|
||||
linfo << "Analyzing " << getCoinName(m_coinId) << " wallet " << m_walletId;
|
||||
|
||||
mysql::connection db(getMysqlConfig());
|
||||
const auto wallets = TableWallets{};
|
||||
const auto wallets_addresses = TableWalletsAddresses{};
|
||||
const auto wallets_tx = TableWalletsTx{};
|
||||
|
||||
// Load wallet addresses
|
||||
list<string> addresses;
|
||||
for (const auto& row: db.run(select(wallets.wallet_id,wallets.type_id,wallets_addresses.address).from(wallets.cross_join(wallets_addresses)).where(wallets.wallet_id == wallets_addresses.wallet_id and wallets.wallet_id == m_walletId and wallets.type_id == m_walletTypeId)))
|
||||
{
|
||||
addresses.push_back(row.address);
|
||||
}
|
||||
|
||||
// Get wallet tx data from data source and insert it in the DB raw tables.
|
||||
list<BlockchainTxDetailsTypeBTC> l;
|
||||
l = getTxDetailsListForAddresses(addresses);
|
||||
addTxDetailsListToRawDB(m_walletId,l);
|
||||
|
||||
// Load tx data from DB raw tables, analyze data, and insert it in DB tables.
|
||||
l = getTxDetailsListFromRawDB(m_walletId);
|
||||
|
||||
db(remove_from(wallets_tx).where(wallets_tx.wallet_id == m_walletId));
|
||||
|
||||
addTxDetailsListToDB(m_walletId,addresses,l);
|
||||
updateBalanceFromTxDetailsInDB(m_walletId);
|
||||
}
|
||||
|
||||
void WalletTypeBTC::addTxDetailsListToRawDB(const int walletId, const list<BlockchainTxDetailsTypeBTC>& l)
|
||||
{
|
||||
mysql::connection db(getMysqlConfig());
|
||||
const auto wallets_btc_raw_tx = TableWalletsBtcRawTx{};
|
||||
const auto blockchain_btc_raw_tx = TableBlockchainBtcRawTx{};
|
||||
const auto blockchain_btc_raw_tx_details = TableBlockchainBtcRawTxDetails{};
|
||||
|
||||
linfo << "Clearing tx raw data links for " << getCoinName(m_coinId) << " wallet " << walletId << ".";
|
||||
|
||||
const auto result = db(remove_from(wallets_btc_raw_tx).where(wallets_btc_raw_tx.wallet_id == walletId));
|
||||
|
||||
linfo << "Now adding raw data to DB for " << getCoinName(m_coinId) << " wallet " << walletId << ".";
|
||||
|
||||
for (const auto& tx: l)
|
||||
{
|
||||
string hash = tx.hash;
|
||||
|
||||
ldebug << "Adding raw data to DB for tx " << hash << ".";
|
||||
|
||||
const auto result = db(select(blockchain_btc_raw_tx.raw_tx_id).from(blockchain_btc_raw_tx).where(blockchain_btc_raw_tx.hash == hash and blockchain_btc_raw_tx.blockchain_id == m_blockchainId));
|
||||
if (!result.empty())
|
||||
{
|
||||
ldebug << "Raw data for tx " << hash << " is already in DB. Ignoring.";
|
||||
|
||||
db(insert_into(wallets_btc_raw_tx).set(
|
||||
wallets_btc_raw_tx.raw_tx_id = result.front().raw_tx_id,
|
||||
wallets_btc_raw_tx.wallet_id = walletId
|
||||
));
|
||||
}
|
||||
else
|
||||
{
|
||||
db(insert_into(blockchain_btc_raw_tx).set(
|
||||
blockchain_btc_raw_tx.hash = hash,
|
||||
blockchain_btc_raw_tx.unix_time = tx.time.toUnixTime(),
|
||||
blockchain_btc_raw_tx.blockchain_id = m_blockchainId
|
||||
));
|
||||
|
||||
int rawTxId = db(select(max(blockchain_btc_raw_tx.raw_tx_id).as(max_raw_tx_id)).from(blockchain_btc_raw_tx).unconditionally()).front().max_raw_tx_id;
|
||||
|
||||
ldebug << "Raw tax id created : " << rawTxId << ".";
|
||||
|
||||
ldebug << "Adding inputs raw data to DB for tx " << hash << ".";
|
||||
|
||||
for (const auto& input : tx.inputs)
|
||||
{
|
||||
// Inputs are positive.
|
||||
Money amount = input.second;
|
||||
|
||||
db(insert_into(blockchain_btc_raw_tx_details).set(
|
||||
blockchain_btc_raw_tx_details.raw_tx_id = rawTxId,
|
||||
blockchain_btc_raw_tx_details.amount = amount.toBoostMpf(),
|
||||
blockchain_btc_raw_tx_details.address = input.first
|
||||
));
|
||||
}
|
||||
|
||||
ldebug << "Adding outputs raw data to DB for tx " << hash << ".";
|
||||
|
||||
for (const auto& output : tx.outputs)
|
||||
{
|
||||
// Outputs are negative.
|
||||
Money amount = 0-output.second;
|
||||
|
||||
db(insert_into(blockchain_btc_raw_tx_details).set(
|
||||
blockchain_btc_raw_tx_details.raw_tx_id = rawTxId,
|
||||
blockchain_btc_raw_tx_details.amount = amount.toBoostMpf(),
|
||||
blockchain_btc_raw_tx_details.address = output.first
|
||||
));
|
||||
}
|
||||
|
||||
db(insert_into(wallets_btc_raw_tx).set(
|
||||
wallets_btc_raw_tx.raw_tx_id = rawTxId,
|
||||
wallets_btc_raw_tx.wallet_id = walletId
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list<BlockchainTxDetailsTypeBTC> WalletTypeBTC::getTxDetailsListFromRawDB(const int walletId)
|
||||
{
|
||||
mysql::connection db(getMysqlConfig());
|
||||
const auto wallets_btc_raw_tx = TableWalletsBtcRawTx{};
|
||||
const auto blockchain_btc_raw_tx = TableBlockchainBtcRawTx{};
|
||||
const auto blockchain_btc_raw_tx_details = TableBlockchainBtcRawTxDetails{};
|
||||
|
||||
linfo << "Retrieving data from raw DB for " << getCoinName(m_coinId) << " wallet " << walletId << ".";
|
||||
|
||||
list<BlockchainTxDetailsTypeBTC> l;
|
||||
for (const auto& rowTx: db.run(select(blockchain_btc_raw_tx.raw_tx_id,blockchain_btc_raw_tx.hash,blockchain_btc_raw_tx.unix_time).from(blockchain_btc_raw_tx.cross_join(wallets_btc_raw_tx)).where(blockchain_btc_raw_tx.raw_tx_id == wallets_btc_raw_tx.raw_tx_id and wallets_btc_raw_tx.wallet_id == walletId)))
|
||||
{
|
||||
ldebug << "Retrieving data from raw DB for tx " << rowTx.hash << ".";
|
||||
|
||||
BlockchainTxDetailsTypeBTC tx;
|
||||
|
||||
tx.hash = rowTx.hash;
|
||||
tx.time.setFromUnixTime(rowTx.unix_time);
|
||||
|
||||
ldebug << "Retrieving inputs/outputs data from raw DB for tx " << rowTx.hash << ".";
|
||||
|
||||
for (const auto& row: db.run(select(blockchain_btc_raw_tx_details.amount,blockchain_btc_raw_tx_details.address).from(blockchain_btc_raw_tx_details).where(blockchain_btc_raw_tx_details.raw_tx_id == rowTx.raw_tx_id)))
|
||||
{
|
||||
Money m (row.amount);
|
||||
|
||||
if (m >= 0)
|
||||
tx.inputs[row.address] = m;
|
||||
else
|
||||
tx.outputs[row.address] = 0-m;
|
||||
}
|
||||
|
||||
l.push_back(tx);
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
void WalletTypeBTC::addTxDetailsListToDB(const int walletId, const list<string> walletAddresses, const list<BlockchainTxDetailsTypeBTC>& l)
|
||||
{
|
||||
mysql::connection db(getMysqlConfig());
|
||||
const auto wallets_tx = TableWalletsTx{};
|
||||
|
||||
linfo << "Now adding data to DB for " << getCoinName(m_coinId) << " wallet " << walletId << ".";
|
||||
|
||||
for (const auto& tx: l)
|
||||
{
|
||||
Money txTotalOutputAmount;
|
||||
Money txTotalInputAmount;
|
||||
Money txWalletOutputAmount;
|
||||
Money txWalletInputAmount;
|
||||
bool bToWallet = false;
|
||||
bool bFromWallet = false;
|
||||
list<string> inputAddresses;
|
||||
list<string> outputAddresses;
|
||||
|
||||
ltrace << "Now adding data to DB for tx " << tx.hash << ".";
|
||||
|
||||
for (const auto& input : tx.inputs)
|
||||
{
|
||||
//ltrace << "Input : " << input.first << ", amount : " << input.second;
|
||||
|
||||
txTotalInputAmount += input.second;
|
||||
|
||||
if (std::find(walletAddresses.begin(), walletAddresses.end(), input.first) != walletAddresses.end())
|
||||
{
|
||||
bFromWallet = true;
|
||||
txWalletInputAmount += input.second;
|
||||
}
|
||||
else
|
||||
{
|
||||
inputAddresses.push_back(input.first);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& output : tx.outputs)
|
||||
{
|
||||
//ltrace << "Output : " << output.first << ", amount : " << output.second;
|
||||
|
||||
txTotalOutputAmount += output.second;
|
||||
|
||||
if (std::find(walletAddresses.begin(), walletAddresses.end(), output.first) != walletAddresses.end())
|
||||
{
|
||||
bToWallet = true;
|
||||
txWalletOutputAmount += output.second;
|
||||
}
|
||||
else
|
||||
{
|
||||
outputAddresses.push_back(output.first);
|
||||
}
|
||||
}
|
||||
|
||||
if (bFromWallet)
|
||||
{
|
||||
// In case at least one address was in the inputs, then ALL other inputs should be.
|
||||
for (auto const& addr: inputAddresses)
|
||||
{
|
||||
if (std::find(walletAddresses.begin(), walletAddresses.end(), addr) == walletAddresses.end())
|
||||
{
|
||||
lwarn << "User configuration is missing this address: " << addr;
|
||||
}
|
||||
}
|
||||
|
||||
// In case at least one address was in the inputs, the outputs might be either another wallet for this user,
|
||||
// or one of the wallet addresses might be missing from the configuration.
|
||||
for (auto const& addr: outputAddresses)
|
||||
{
|
||||
if (std::find(walletAddresses.begin(), walletAddresses.end(), addr) == walletAddresses.end())
|
||||
{
|
||||
linfo << "Potential other address to analyze: " << L_Cyan << addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Money amount;
|
||||
Money fee;
|
||||
|
||||
if (bFromWallet)
|
||||
{
|
||||
// The fee only applies in case this tx was initiated by the wallet
|
||||
fee = txTotalInputAmount - txTotalOutputAmount;
|
||||
|
||||
amount = txWalletOutputAmount - txWalletInputAmount + fee;
|
||||
}
|
||||
else
|
||||
{
|
||||
amount = txWalletOutputAmount;
|
||||
fee = 0;
|
||||
}
|
||||
|
||||
db(insert_into(wallets_tx).set(
|
||||
wallets_tx.wallet_id = walletId,
|
||||
wallets_tx.amount = amount.toBoostMpf(),
|
||||
wallets_tx.fee = fee.toBoostMpf(),
|
||||
wallets_tx.unix_time = tx.time.toUnixTime(),
|
||||
wallets_tx.amount_coin_id = m_coinId,
|
||||
wallets_tx.fee_coin_id = m_coinId,
|
||||
wallets_tx.operation_type = CPFM_WALLET_OPERATION_UNKNOWN
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
void WalletTypeBTC::updateBalanceFromTxDetailsInDB(int walletId)
|
||||
{
|
||||
mysql::connection db(getMysqlConfig());
|
||||
const auto wallets_tx = TableWalletsTx{};
|
||||
const auto wallets_balances = TableWalletsBalances{};
|
||||
|
||||
ldebug << "------------------------------------------------------------";
|
||||
|
||||
Money walletBalance = 0;
|
||||
Money walletInputs = 0;
|
||||
Money walletOutputs = 0;
|
||||
Money walletFees = 0;
|
||||
|
||||
for (const auto& row: db.run(select(wallets_tx.amount, wallets_tx.fee).from(wallets_tx).where(wallets_tx.wallet_id == walletId).order_by(wallets_tx.unix_time.asc())))
|
||||
{
|
||||
Money txAmount(row.amount);
|
||||
Money txFee(row.fee);
|
||||
|
||||
if (txAmount<0)
|
||||
walletOutputs += txAmount;
|
||||
else
|
||||
walletInputs += txAmount;
|
||||
|
||||
walletFees += txFee;
|
||||
|
||||
walletBalance = walletInputs + walletOutputs - walletFees;
|
||||
|
||||
string sReason = "?";
|
||||
|
||||
ltrace << "Tx Amount: " << txAmount
|
||||
<< ". Reason: " << sReason
|
||||
<< ". Tx Fees: " << txFee
|
||||
<< ". Total outputs: " << walletOutputs
|
||||
<< ". Total inputs: " << walletInputs
|
||||
<< ". Total Balance: " << walletBalance;
|
||||
}
|
||||
|
||||
linfo << "Wallet " << walletId << " balance is: " << walletBalance;
|
||||
|
||||
db(remove_from(wallets_balances).where(wallets_balances.wallet_id == walletId));
|
||||
|
||||
db(insert_into(wallets_balances).set(
|
||||
wallets_balances.wallet_id = walletId,
|
||||
wallets_balances.coin_id = m_coinId,
|
||||
wallets_balances.balance = walletBalance.toBoostMpf()
|
||||
));
|
||||
}
|
48
src/wallets/wallet_type_btc.h
Normal file
48
src/wallets/wallet_type_btc.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2021, evilny0
|
||||
*
|
||||
* This file is part of cpfm.
|
||||
*
|
||||
* cpfm is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* cpm is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with cpfm. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CPFM_WALLET_TYPE_BTC_H_INCLUDED
|
||||
#define CPFM_WALLET_TYPE_BTC_H_INCLUDED
|
||||
|
||||
#include "wallet.h"
|
||||
#include "datasources/datasource.h"
|
||||
|
||||
class WalletTypeBTC : public Wallet
|
||||
{
|
||||
public:
|
||||
WalletTypeBTC(int walletId):Wallet(walletId) {}
|
||||
|
||||
virtual void update();
|
||||
|
||||
private:
|
||||
void addTxDetailsListToRawDB(const int walletId, const list<BlockchainTxDetailsTypeBTC>& l);
|
||||
void addTxDetailsListToDB(const int walletId, const list<string> walletAddresses, const list<BlockchainTxDetailsTypeBTC>& l);
|
||||
list<BlockchainTxDetailsTypeBTC> getTxDetailsListFromRawDB(const int walletId);
|
||||
void updateBalanceFromTxDetailsInDB(int walletId);
|
||||
|
||||
protected:
|
||||
virtual list<BlockchainTxDetailsTypeBTC> getTxDetailsListForAddresses(list<string> addresses) = 0;
|
||||
|
||||
int m_coinId;
|
||||
int m_walletTypeId;
|
||||
int m_blockchainId;
|
||||
};
|
||||
|
||||
#endif // CPFM_WALLET_TYPE_BTC_H_INCLUDED
|
248
src/wallets/wallet_type_eth.cpp
Normal file
248
src/wallets/wallet_type_eth.cpp
Normal file
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
* Copyright (c) 2021, evilny0
|
||||
*
|
||||
* This file is part of cpfm.
|
||||
*
|
||||
* cpfm is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* cpm is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with cpfm. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "wallets/wallet_type_eth.h"
|
||||
|
||||
SQLPP_ALIAS_PROVIDER(max_raw_tx_id);
|
||||
|
||||
void WalletTypeETH::update()
|
||||
{
|
||||
if (!m_walletId)
|
||||
{
|
||||
lerr << "Wallet id not defined. Ignoring update.";
|
||||
}
|
||||
|
||||
linfo << "Analyzing " << getBlockchainName(m_blockchainId) << " wallet " << m_walletId;
|
||||
|
||||
mysql::connection db(getMysqlConfig());
|
||||
const auto wallets = TableWallets{};
|
||||
const auto wallets_addresses = TableWalletsAddresses{};
|
||||
const auto wallets_tx = TableWalletsTx{};
|
||||
|
||||
// Load wallet address
|
||||
string address = db(select(wallets.wallet_id,wallets.type_id,wallets_addresses.address).from(wallets.cross_join(wallets_addresses)).where(wallets.wallet_id == wallets_addresses.wallet_id and wallets.wallet_id == m_walletId and wallets.type_id == m_walletTypeId)).front().address;
|
||||
|
||||
// Get wallet tx data from data source and insert it in the DB raw tables.
|
||||
list<BlockchainTxDetailsTypeETH> l;
|
||||
l = getTxDetailsListForAddress(address);
|
||||
addTxDetailsListToRawDB(m_walletId,l);
|
||||
|
||||
// Load tx data from DB raw tables, analyze data, and insert it in DB tables.
|
||||
l = getTxDetailsListFromRawDB(m_walletId);
|
||||
|
||||
db(remove_from(wallets_tx).where(wallets_tx.wallet_id == m_walletId));
|
||||
|
||||
addTxDetailsListToDB(m_walletId,address,l);
|
||||
updateBalanceFromTxDetailsInDB(m_walletId);
|
||||
}
|
||||
|
||||
void WalletTypeETH::addTxDetailsListToRawDB(const int walletId, const list<BlockchainTxDetailsTypeETH>& l)
|
||||
{
|
||||
mysql::connection db(getMysqlConfig());
|
||||
const auto wallets_eth_raw_tx = TableWalletsEthRawTx{};
|
||||
const auto blockchain_eth_raw_tx = TableBlockchainEthRawTx{};
|
||||
const auto blockchain_eth_raw_tx_details = TableBlockchainEthRawTxDetails{};
|
||||
|
||||
linfo << "Clearing tx raw data links for " << getBlockchainName(m_blockchainId) << " wallet " << walletId << ".";
|
||||
|
||||
const auto result = db(remove_from(wallets_eth_raw_tx).where(wallets_eth_raw_tx.wallet_id == walletId));
|
||||
|
||||
linfo << "Now adding raw data to DB for " << getBlockchainName(m_blockchainId) << " wallet " << walletId << ".";
|
||||
|
||||
for (const auto& tx: l)
|
||||
{
|
||||
string hash = tx.hash;
|
||||
|
||||
ldebug << "Adding raw data to DB for tx " << hash << ".";
|
||||
|
||||
const auto result = db(select(blockchain_eth_raw_tx.raw_tx_id).from(blockchain_eth_raw_tx).where(blockchain_eth_raw_tx.hash == hash and blockchain_eth_raw_tx.blockchain_id == m_blockchainId));
|
||||
if (!result.empty())
|
||||
{
|
||||
ldebug << "Raw data for tx " << hash << " is already in DB. Ignoring.";
|
||||
|
||||
db(insert_into(wallets_eth_raw_tx).set(
|
||||
wallets_eth_raw_tx.raw_tx_id = result.front().raw_tx_id,
|
||||
wallets_eth_raw_tx.wallet_id = walletId
|
||||
));
|
||||
}
|
||||
else
|
||||
{
|
||||
Money fee = tx.fee;
|
||||
|
||||
db(insert_into(blockchain_eth_raw_tx).set(
|
||||
blockchain_eth_raw_tx.hash = hash,
|
||||
blockchain_eth_raw_tx.unix_time = tx.time.toUnixTime(),
|
||||
blockchain_eth_raw_tx.blockchain_id = m_blockchainId,
|
||||
blockchain_eth_raw_tx.fee = fee.toBoostMpf()
|
||||
));
|
||||
|
||||
int rawTxId = db(select(max(blockchain_eth_raw_tx.raw_tx_id).as(max_raw_tx_id)).from(blockchain_eth_raw_tx).unconditionally()).front().max_raw_tx_id;
|
||||
|
||||
ldebug << "Raw tax id created : " << rawTxId << ".";
|
||||
|
||||
ldebug << "Adding raw data operations to DB for tx " << hash << ".";
|
||||
|
||||
for (const auto& op : tx.operations)
|
||||
{
|
||||
Money amount = op.amount;
|
||||
|
||||
db(insert_into(blockchain_eth_raw_tx_details).set(
|
||||
blockchain_eth_raw_tx_details.raw_tx_id = rawTxId,
|
||||
blockchain_eth_raw_tx_details.address_from = op.addressFrom,
|
||||
blockchain_eth_raw_tx_details.address_to = op.addressTo,
|
||||
blockchain_eth_raw_tx_details.amount = amount.toBoostMpf(),
|
||||
blockchain_eth_raw_tx_details.amount_coin_id = op.amountCoinId
|
||||
));
|
||||
}
|
||||
|
||||
db(insert_into(wallets_eth_raw_tx).set(
|
||||
wallets_eth_raw_tx.raw_tx_id = rawTxId,
|
||||
wallets_eth_raw_tx.wallet_id = walletId
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list<BlockchainTxDetailsTypeETH> WalletTypeETH::getTxDetailsListFromRawDB(const int walletId)
|
||||
{
|
||||
mysql::connection db(getMysqlConfig());
|
||||
const auto wallets_eth_raw_tx = TableWalletsEthRawTx{};
|
||||
const auto blockchain_eth_raw_tx = TableBlockchainEthRawTx{};
|
||||
const auto blockchain_eth_raw_tx_details = TableBlockchainEthRawTxDetails{};
|
||||
|
||||
linfo << "Retrieving data from raw DB for " << getBlockchainName(m_blockchainId) << " wallet " << walletId << ".";
|
||||
|
||||
list<BlockchainTxDetailsTypeETH> l;
|
||||
for (const auto& rowTx: db.run(select(blockchain_eth_raw_tx.raw_tx_id,blockchain_eth_raw_tx.hash,blockchain_eth_raw_tx.unix_time,blockchain_eth_raw_tx.fee).from(blockchain_eth_raw_tx.cross_join(wallets_eth_raw_tx)).where(blockchain_eth_raw_tx.raw_tx_id == wallets_eth_raw_tx.raw_tx_id and wallets_eth_raw_tx.wallet_id == walletId)))
|
||||
{
|
||||
ldebug << "Retrieving data from raw DB for tx " << rowTx.hash << ".";
|
||||
|
||||
BlockchainTxDetailsTypeETH tx;
|
||||
|
||||
tx.hash = rowTx.hash;
|
||||
tx.time.setFromUnixTime(rowTx.unix_time);
|
||||
tx.fee = rowTx.fee;
|
||||
|
||||
ldebug << "Retrieving operations data from raw DB for tx " << rowTx.hash << ".";
|
||||
|
||||
for (const auto& rowOp: db.run(select(blockchain_eth_raw_tx_details.amount,blockchain_eth_raw_tx_details.amount_coin_id,blockchain_eth_raw_tx_details.address_from,blockchain_eth_raw_tx_details.address_to).from(blockchain_eth_raw_tx_details).where(blockchain_eth_raw_tx_details.raw_tx_id == rowTx.raw_tx_id)))
|
||||
{
|
||||
BlockchainTxOperationTypeETH op;
|
||||
op.addressFrom = rowOp.address_from;
|
||||
op.addressTo = rowOp.address_to;
|
||||
op.amount = rowOp.amount;
|
||||
op.amountCoinId = rowOp.amount_coin_id;
|
||||
|
||||
tx.operations.push_back(op);
|
||||
}
|
||||
|
||||
l.push_back(tx);
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
void WalletTypeETH::addTxDetailsListToDB(const int walletId, string address, const list<BlockchainTxDetailsTypeETH>& l)
|
||||
{
|
||||
mysql::connection db(getMysqlConfig());
|
||||
const auto wallets_tx = TableWalletsTx{};
|
||||
|
||||
linfo << "Now adding data to DB for " << getBlockchainName(m_blockchainId) << " wallet " << walletId << ".";
|
||||
|
||||
for (const auto& tx: l)
|
||||
{
|
||||
ltrace << "Now adding data to DB for tx " << tx.hash << ".";
|
||||
|
||||
bool bFeeAdded = false;
|
||||
|
||||
for (const auto& op: tx.operations)
|
||||
{
|
||||
Money amount;
|
||||
Money fee;
|
||||
|
||||
if (address == op.addressFrom)
|
||||
{
|
||||
// Even if we have multiple operations for the same tx, the fee only applies once.
|
||||
if (!bFeeAdded)
|
||||
{
|
||||
// The fee only applies in case this tx was initiated by the wallet
|
||||
fee = tx.fee;
|
||||
}
|
||||
|
||||
amount = 0-op.amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
amount = op.amount;
|
||||
}
|
||||
|
||||
db(insert_into(wallets_tx).set(
|
||||
wallets_tx.wallet_id = walletId,
|
||||
wallets_tx.amount = amount.toBoostMpf(),
|
||||
wallets_tx.fee = fee.toBoostMpf(),
|
||||
wallets_tx.unix_time = tx.time.toUnixTime(),
|
||||
wallets_tx.amount_coin_id = op.amountCoinId,
|
||||
wallets_tx.fee_coin_id = m_coinId,
|
||||
wallets_tx.operation_type = CPFM_WALLET_OPERATION_UNKNOWN
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WalletTypeETH::updateBalanceFromTxDetailsInDB(int walletId)
|
||||
{
|
||||
mysql::connection db(getMysqlConfig());
|
||||
const auto wallets_tx = TableWalletsTx{};
|
||||
const auto wallets_balances = TableWalletsBalances{};
|
||||
|
||||
ldebug << "------------------------------------------------------------";
|
||||
|
||||
map<int,Money> walletBalances;
|
||||
|
||||
for (const auto& row: db.run(select(wallets_tx.amount, wallets_tx.amount_coin_id, wallets_tx.fee).from(wallets_tx).where(wallets_tx.wallet_id == walletId).order_by(wallets_tx.unix_time.asc())))
|
||||
{
|
||||
Money txAmount(row.amount);
|
||||
Money txFee(row.fee);
|
||||
|
||||
walletBalances[row.amount_coin_id] += txAmount;
|
||||
walletBalances[m_coinId] -= txFee;
|
||||
|
||||
string sReason = "?";
|
||||
|
||||
ltrace << "Tx Amount: " << txAmount
|
||||
<< ", Coin: " << getCoinName(row.amount_coin_id)
|
||||
<< ". Reason: " << sReason
|
||||
<< ". Tx Fees: " << txFee;
|
||||
}
|
||||
|
||||
db(remove_from(wallets_balances).where(wallets_balances.wallet_id == walletId));
|
||||
|
||||
for (const auto b: walletBalances)
|
||||
{
|
||||
linfo << "Wallet " << walletId << " " << getCoinName(b.first) << " balance is: " << b.second;
|
||||
|
||||
Money m = b.second;
|
||||
db(insert_into(wallets_balances).set(
|
||||
wallets_balances.wallet_id = walletId,
|
||||
wallets_balances.coin_id = b.first,
|
||||
wallets_balances.balance = m.toBoostMpf()
|
||||
));
|
||||
}
|
||||
}
|
48
src/wallets/wallet_type_eth.h
Normal file
48
src/wallets/wallet_type_eth.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2021, evilny0
|
||||
*
|
||||
* This file is part of cpfm.
|
||||
*
|
||||
* cpfm is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* cpm is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with cpfm. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CPFM_WALLET_TYPE_ETH_H_INCLUDED
|
||||
#define CPFM_WALLET_TYPE_ETH_H_INCLUDED
|
||||
|
||||
#include "wallet.h"
|
||||
#include "datasources/datasource.h"
|
||||
|
||||
class WalletTypeETH : public Wallet
|
||||
{
|
||||
public:
|
||||
WalletTypeETH(int walletId):Wallet(walletId) {}
|
||||
|
||||
virtual void update();
|
||||
|
||||
private:
|
||||
void addTxDetailsListToRawDB(const int walletId, const list<BlockchainTxDetailsTypeETH>& l);
|
||||
void addTxDetailsListToDB(const int walletId, string address, const list<BlockchainTxDetailsTypeETH>& l);
|
||||
list<BlockchainTxDetailsTypeETH> getTxDetailsListFromRawDB(const int walletId);
|
||||
void updateBalanceFromTxDetailsInDB(int walletId);
|
||||
|
||||
protected:
|
||||
virtual list<BlockchainTxDetailsTypeETH> getTxDetailsListForAddress(string address) = 0;
|
||||
|
||||
int m_walletTypeId;
|
||||
int m_blockchainId;
|
||||
int m_coinId;
|
||||
};
|
||||
|
||||
#endif // CPFM_WALLET_TYPE_ETH_H_INCLUDED
|
Loading…
Reference in New Issue
Block a user