Skip to content

Commit

Permalink
Add functionality to consolidate old node payments where you have mul…
Browse files Browse the repository at this point in the history
…tiple addresses that receive node payments with the same TXID into one transaction using the walletsource as identifier. \n\nFixed code to properly display wallet source
  • Loading branch information
psyraxaus committed Jul 26, 2022
1 parent e273256 commit c756211
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 11 deletions.
176 changes: 169 additions & 7 deletions app/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,32 +121,170 @@ const _saveExportedFile = async (targetWindow, file, csvFormat) => {
dbErrorBox(err, 'connecting to');
}
});

const nodePayFromAddrs = [
"zsf45QuD75XJdm3uLftiW6pucvbhvrbhAhZ",
"zsoVG9Evw68te8hRAP3xPXSbx9HoH26LUYN",
"zsi4CcCUYtR1iNjEyjkLPjSVPzSPa4atxt9"
]

let tobexported;
let nonConsolSqlQuery;
let consoldateSqlQuery

switch(csvFormat) {
case "CrytotaxCalc":
csvFields = ['Timestamp (UTC)', 'Type', 'Base Currency', 'Base Amount', 'Quote Currency', 'Quote Amount', 'Fee Currency', 'Fee Amount', 'From (Optional)', 'To (Optional)', 'ID (Optional)', 'Description (Optional)' ];
tobexported = await _db_all(db, `SELECT datetime(time, 'unixepoch') as 'Timestamp (UTC)', IIF(amount < 0, 'transfer-out', 'transfer-in') as Type, currency, ABS(amount) as 'Base Amount', "" AS quote_curr, "" AS quote_amount, IIF(amount < 0, currency, '') AS fee_currency, IIF(amount < 0, fees, '') AS fee_amount, vins AS "From (Optional)", vouts AS "To (Optional)", txid AS "ID (Optional)", "" AS description FROM transactions`);
nonConsolSqlQuery = `SELECT
datetime(time, 'unixepoch') as 'Timestamp (UTC)',
IIF(amount < 0, 'transfer-out', 'transfer-in') as Type,
currency AS 'Base Currency',
ABS(amount) as 'Base Amount',
"" AS 'Quote Currency',
"" AS 'Quote Amount',
IIF(amount < 0, currency, '') AS 'Fee Currency',
IIF(amount < 0, fees, '') AS 'Fee Amount',
vins AS "From (Optional)",
vouts AS "To (Optional)",
txid AS "ID (Optional)", ""
AS 'Description (Optional)'
FROM transactions
WHERE
vins <> "zsf45QuD75XJdm3uLftiW6pucvbhvrbhAhZ"
AND vins <> "zsoVG9Evw68te8hRAP3xPXSbx9HoH26LUYN"
AND vins <> "zsi4CcCUYtR1iNjEyjkLPjSVPzSPa4atxt9"`;

consoldateSqlQuery = `SELECT datetime(time, 'unixepoch') as 'Timestamp (UTC)', 'transfer-in' AS Type, currency AS 'Base Currency', SUM(amount) AS 'Base Amount', "" AS 'Quote Currency', "" AS 'Quote Amount', "" AS 'Fee Currency', "" AS 'Fee Amount', vins AS 'From (Optional)', GROUP_CONCAT(address) AS 'To (Optional)', txid AS 'ID (Optional)', "" AS 'Description (Optional)' FROM transactions`
allTransactionsStr = await _formatTransactionsForExport(nodePayFromAddrs, db, nonConsolSqlQuery, consoldateSqlQuery, settings.consolidateNodeTxns)
break;
case "Cointracking":
csvFields = ['Type', 'Buy Amount', 'Buy Currency', 'Sell Amount', 'Sell Currency', 'Fee', 'Fee Currency', 'Exchange', 'Trade-Group', 'Comment', 'Date', "Tx-ID", "Buy Value in Account Currrency", "Sell Value in Account Currency" ];
tobexported = await _db_all(db, `SELECT IIF(amount < 0, 'Withdrawal', 'Deposit') as Type, IIF(amount > 0, ABS(amount), "") as 'Buy Amount', IIF(amount > 0, currency, '') as 'Buy Currency', IIF(amount < 0, ABS(amount), "") as 'Sell Amount', IIF(amount < 0, currency, '') as 'Sell Currency', IIF(amount < 0, fees, '') AS 'Fee', IIF(amount < 0, currency, '') AS 'Fee Currency', walletsource AS "Exchange", "" AS "Trade-Group", description AS Comment, strftime('%Y-%m-%d %H:%M:%S', datetime(time, 'unixepoch')) as 'Date', txid AS "Tx-ID", "" AS "Buy Value in Account Currrency", "" AS "Sell Value in Account Currency" FROM transactions INNER JOIN wallet ON wallet.address = transactions.address`);

nonConsolSqlQuery = `SELECT
IIF(amount < 0, 'Withdrawal', 'Deposit') as Type,
IIF(amount > 0, ABS(amount), "") as 'Buy Amount',
IIF(amount > 0, currency, '') as 'Buy Currency',
IIF(amount < 0, ABS(amount), "") as 'Sell Amount',
IIF(amount < 0, currency, '') as 'Sell Currency',
IIF(amount < 0, fees, '') AS 'Fee',
IIF(amount < 0, currency, '') AS 'Fee Currency',
walletsource AS 'Exchange',
"" AS 'Trade-Group',
description AS Comment,
strftime('%Y-%m-%d %H:%M:%S', datetime(time, 'unixepoch')) as 'Date',
txid AS "Tx-ID",
"" AS "Buy Value in Account Currrency",
"" AS "Sell Value in Account Currency"
FROM transactions
INNER JOIN wallet ON wallet.address = transactions.address
WHERE
vins <> "zsf45QuD75XJdm3uLftiW6pucvbhvrbhAhZ"
AND vins <> "zsoVG9Evw68te8hRAP3xPXSbx9HoH26LUYN"
AND vins <> "zsi4CcCUYtR1iNjEyjkLPjSVPzSPa4atxt9"`;

consoldateSqlQuery = `SELECT
'Deposit' AS Type,
SUM(amount) AS 'Buy Amount',
currency AS 'Buy Currency',
"" AS 'Sell Amount',
"" AS 'Sell Currency',
"" AS 'Fee',
"" AS 'Fee Currency',
walletsource AS 'Exchange',
"" AS 'Trade-Group',
description AS Comment,
strftime('%Y-%m-%d %H:%M:%S', datetime(time, 'unixepoch')) as 'Date',
txid AS "Tx-ID",
"" AS "Buy Value in Account Currrency",
"" AS "Sell Value in Account Currency"
FROM transactions
INNER JOIN wallet ON wallet.address = transactions.address`;
allTransactionsStr = await _formatTransactionsForExport(nodePayFromAddrs, db, nonConsolSqlQuery, consoldateSqlQuery, settings.consolidateNodeTxns);
break;
case "Koinly":
csvFields = ['Date', 'Sent Amount', 'Sent Currency', 'Received Amount', 'Received Currency', 'Fee Amount', 'Fee Currency', 'Net Worth Amount', 'Net Worth Currency', 'Label', 'Description', 'TxHash' ];
tobexported = await _db_all(db,`SELECT strftime('%Y-%m-%d %H:%M UTC', datetime(time, 'unixepoch')) as 'Date', IIF(amount < 0, ABS(amount), "") as 'Sent Amount', IIF(amount < 0, currency, '') AS 'Sent Currency', IIF(amount > 0, ABS(amount), "") as 'Received Amount', IIF(amount > 0, currency, '') AS 'Received Currency', IIF(amount < 0, fees, '') AS 'Fee Amount', IIF(amount < 0, currency, '') AS 'Fee Currency', "" AS 'Net Worth Amount', "" AS 'Net Worth Currency', walletsource AS 'Label', description AS 'Description', txid AS 'TxHash' FROM transactions INNER JOIN wallet ON wallet.address = transactions.address`);
nonConsolSqlQuery = `SELECT
strftime('%Y-%m-%d %H:%M UTC', datetime(time, 'unixepoch')) as 'Date',
IIF(amount < 0, ABS(amount), "") as 'Sent Amount',
IIF(amount < 0, currency, '') AS 'Sent Currency',
IIF(amount > 0, ABS(amount), "") as 'Received Amount',
IIF(amount > 0, currency, '') AS 'Received Currency',
IIF(amount < 0, fees, '') AS 'Fee Amount',
IIF(amount < 0, currency, '') AS 'Fee Currency',
'' AS 'Net Worth Amount',
'' AS 'Net Worth Currency',
walletsource AS 'Label',
description AS 'Description',
txid AS 'TxHash'
FROM transactions
INNER JOIN wallet ON wallet.address = transactions.address
WHERE
vins <> "zsf45QuD75XJdm3uLftiW6pucvbhvrbhAhZ"
AND vins <> "zsoVG9Evw68te8hRAP3xPXSbx9HoH26LUYN"
AND vins <> "zsi4CcCUYtR1iNjEyjkLPjSVPzSPa4atxt9"`;

consoldateSqlQuery = `SELECT
strftime('%Y-%m-%d %H:%M UTC', datetime(time, 'unixepoch')) as 'Date',
'' as 'Sent Amount',
'' AS 'Sent Currency',
SUM(amount) as 'Received Amount',
currency AS 'Received Currency',
'' AS 'Fee Amount',
'' AS 'Fee Currency',
'' AS 'Net Worth Amount',
'' AS 'Net Worth Currency',
walletsource AS 'Label',
description AS 'Description',
txid AS "TxHash"
FROM transactions
INNER JOIN wallet ON wallet.address = transactions.address`;
allTransactionsStr = await _formatTransactionsForExport(nodePayFromAddrs, db, nonConsolSqlQuery, consoldateSqlQuery, settings.consolidateNodeTxns);
break;
case "Accointing":
csvFields = ['transactionType', 'date', 'inBuyAmount', 'inBuyAsset', 'outSellAmount', 'outSellAsset', 'feeAmount (optional)', 'feeAsset (optional)', 'classification (optional)', 'operationId (optional)', 'comments (optional)' ];
tobexported = await _db_all(db, `SELECT IIF(amount > 0, 'deposit', 'withdraw') as transactionType, strftime('%Y/%m/%d %H:%M:%S', datetime(time, 'unixepoch')) as 'date', IIF(amount > 0, ABS(amount), "") as 'inBuyAmount', IIF(amount > 0, currency, '') AS 'inBuyAsset', IIF(amount < 0, ABS(amount), "") as 'outSellAmount', IIF(amount < 0, currency, '') AS 'outSellAsset', IIF(amount < 0, fees, '') AS 'feeAmount (optional)', IIF(amount < 0, currency, '') AS 'feeAsset (optional)', "" AS 'classification (optional)', txid AS 'operationId (optional)', walletsource AS 'comments (optional)' FROM transactions INNER JOIN wallet ON wallet.address = transactions.address`);
nonConsolSqlQuery = `SELECT
IIF(amount > 0, 'deposit', 'withdraw') as transactionType,
strftime('%Y/%m/%d %H:%M:%S', datetime(time, 'unixepoch')) as 'date',
IIF(amount > 0, ABS(amount), "") as 'inBuyAmount',
IIF(amount > 0, currency, '') AS 'inBuyAsset',
IIF(amount < 0, ABS(amount), "") as 'outSellAmount',
IIF(amount < 0, currency, '') AS 'outSellAsset',
IIF(amount < 0, fees, '') AS 'feeAmount (optional)',
IIF(amount < 0, currency, '') AS 'feeAsset (optional)',
"" AS 'classification (optional)',
txid AS 'operationId (optional)',
walletsource AS 'comments (optional)'
FROM transactions
INNER JOIN wallet ON wallet.address = transactions.address
WHERE
vins <> "zsf45QuD75XJdm3uLftiW6pucvbhvrbhAhZ"
AND vins <> "zsoVG9Evw68te8hRAP3xPXSbx9HoH26LUYN"
AND vins <> "zsi4CcCUYtR1iNjEyjkLPjSVPzSPa4atxt9"`;

consoldateSqlQuery = `SELECT
'deposit' as transactionType,
strftime('%Y/%m/%d %H:%M:%S', datetime(time, 'unixepoch')) as 'date',
SUM(amount) as 'inBuyAmount',
currency AS 'inBuyAsset',
'' as 'outSellAmount',
'' AS 'outSellAsset',
'' AS 'feeAmount (optional)',
'' AS 'feeAsset (optional)',
"" AS 'classification (optional)',
txid AS 'operationId (optional)',
walletsource AS 'comments (optional)'
FROM transactions
INNER JOIN wallet ON wallet.address = transactions.address`;
allTransactionsStr = await _formatTransactionsForExport(nodePayFromAddrs, db, nonConsolSqlQuery, consoldateSqlQuery, settings.consolidateNodeTxns);
break;
case "Unformatted":
csvFields = ['Timestamp (UTC)', 'Type','Amount Transacted', 'Currency', 'Fee Amount', 'Fee Currency', 'From Address', 'To', 'Transaction ID', 'Wallet Source', 'Description' ];
tobexported = await _db_all(db, `SELECT datetime(time, 'unixepoch') as 'Time (UTC)', IIF(amount < 0, 'out', 'in') as Type, amount as 'Amount Transacted', currency, IIF(amount < 0, fees, '') AS 'Fee Amount', IIF(amount < 0, currency, '') AS 'Fee Currency', vins AS 'From', vouts AS 'To', txid AS 'ID', walletsource AS 'Wallet Source', description AS Description FROM transactions INNER JOIN wallet ON wallet.address = transactions.address`);
nonConsolSqlQuery = `SELECT datetime(time, 'unixepoch') as 'Time (UTC)', IIF(amount < 0, 'out', 'in') as Type, amount as 'Amount Transacted', currency, IIF(amount < 0, fees, '') AS 'Fee Amount', IIF(amount < 0, currency, '') AS 'Fee Currency', vins AS 'From', vouts AS 'To', txid AS 'ID', walletsource AS 'Wallet Source', description AS Description FROM transactions INNER JOIN wallet ON wallet.address = transactions.address`;
allTransactionsStr = await _formatTransactionsForExport(nodePayFromAddrs, db, nonConsolSqlQuery, consoldateSqlQuery, settings.consolidateNodeTxns);
break;
}
const jsonTransactions = JSON.parse(JSON.stringify(tobexported));

const jsonTransactions = JSON.parse(allTransactionsStr.replace(/\]\,\[/gm, ","));
const json2csvParser = new Json2csvParser({ csvFields });
const csv = json2csvParser.parse(jsonTransactions);

Expand Down Expand Up @@ -489,7 +627,7 @@ async function _db_all(db, query){
return new Promise(function(resolve,reject){
db.all(query, function(err,rows){
if (err) {
dbErrorBox(err, 'reading existing addresses from')
dbErrorBox(err)
}
resolve(rows);
});
Expand Down Expand Up @@ -757,3 +895,27 @@ function getFilteredVin(address, originalVin) {
}));
});
}

const _formatTransactionsForExport = async (nodePayFromAddrs, db, sqlNonConsolQuery, sqlConsolQuery, consolidateFlag) => {
let allTransactionsStr;
if (consolidateFlag == false || !sqlConsolQuery) {
sqlNonConsolQuery = sqlNonConsolQuery.split('WHERE')[0];
nonConsolidatedObj = await _db_all(db, sqlNonConsolQuery);
allTransactionsStr = JSON.stringify(nonConsolidatedObj);

return allTransactionsStr;
} else if (consolidateFlag == true){
nonConsolidatedObj = await _db_all(db, sqlNonConsolQuery);
allTransactionsStr = JSON.stringify(nonConsolidatedObj);

for (const nodeAddr of Object.keys(nodePayFromAddrs)) {
nodeTxns = await _db_all(db, `SELECT txid, COUNT(*) FROM transactions WHERE vins = '${nodePayFromAddrs[nodeAddr]}' GROUP BY txid HAVING COUNT(*) > 0`)
for (var nodeTxid of Object.keys(nodeTxns)) {
let consolidatedTxnsObj = await _db_all(db, sqlConsolQuery + ` WHERE txid = "${nodeTxns[nodeTxid].txid}";`);
allTransactionsStr = allTransactionsStr + "," + JSON.stringify(consolidatedTxnsObj)
}
}

return allTransactionsStr;
}
}
2 changes: 1 addition & 1 deletion app/render/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ footer {
padding: 10px;
}

.urlBoxes, .explorerBox, .apiBox, .bottomSection {
.urlBoxes, .explorerBox, .apiBox, .bottomSection, .consolidate {
width: 100%
}

Expand Down
3 changes: 3 additions & 0 deletions app/render/html/settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@

</div>
</div>
<div class="consolidate">
<input type="checkbox" id="consolidate"> Consolidate early node transaction sent to multiple addresses you own that have the same TXID. Needed for some online tax calculation sites.
</div>
<div class="bottomSection">
<div class="settingsStatus" id="settingsStatus"></div>
<div class="submitSettings" id="submitSettings">
Expand Down
3 changes: 2 additions & 1 deletion app/render/js/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ addAddressesButton.addEventListener('click', () => {
settingsButton.addEventListener('click', () => {
let win = new BrowserWindow( {
width: 645,
height: 225,
height: 255,
show: true,
parent: currentWindow,
frame: false,
Expand Down Expand Up @@ -164,6 +164,7 @@ ipcRenderer.on('task-running', (event, message) => {

ipcRenderer.on('settings', (event, message) => {
settings = message;
console.log(message)
mainProcess.saveSettings(currentWindow);
currentTaskwindowText.innerHTML = `Current API URL: ${settings.apiUrl}`;
});
Expand Down
6 changes: 5 additions & 1 deletion app/render/js/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ const koinlyRad = document.getElementById('Koinly');
const unformattedRad = document.getElementById('Unformatted');
const closeButton = document.getElementById('closebutton');
const saveSettingsButton = document.getElementById('savesettingsbutton');
const consolidateNodeTxnsBox = document.getElementById('consolidate');

ipc.on('settingsRunning', (event, message) => {
settings = message;
explorerUrlTextBox.value = settings.explorerUrl;
apiUrlTextBox.value = settings.apiUrl;
refreshTextBox.value = settings.refreshIntervalAPI;
consolidateNodeTxnsBox.checked = settings.consolidateNodeTxns;
switch (settings.csvFormat) {
case 'Unformatted':
unformattedRad.checked = true;
Expand All @@ -49,11 +51,13 @@ closeButton.addEventListener('click', (event) => {
saveSettingsButton.addEventListener('click', (event) => {
event.preventDefault();
let csvType = document.querySelector('input[name="csvType"]:checked').value;
let consolidate = document.querySelector('#consolidate');
let updatedSettings = {
explorerUrl: explorerUrlTextBox.value,
apiUrl: apiUrlTextBox.value,
csvFormat: csvType,
refreshIntervalAPI: parseInt(refreshTextBox.value)
refreshIntervalAPI: parseInt(refreshTextBox.value),
consolidateNodeTxns: consolidate.checked
};
mainProcess.setSettings(currentWindow, updatedSettings);
window.close();
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "txnsapp",
"version": "1.0.5",
"version": "1.0.6",
"description": "Application to fetch the transaction history for Horizen addresses and export to various online cointracking services",
"main": "app/main.js",
"dependencies": {
Expand Down

0 comments on commit c756211

Please sign in to comment.