Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
d967f70
feat: collapse payments by day
lissavxo Feb 5, 2025
19a3aba
feat: csv collapse payments orderded
lissavxo Feb 10, 2025
626f9c5
feat: const DEFAULT_COLLAPSE_THRESHOLD_FILE_USD
lissavxo Feb 10, 2025
02c9264
fix: file types
lissavxo Feb 10, 2025
7764048
feat: clean code
lissavxo Feb 12, 2025
2b3b61b
fix: collapse threshold var
lissavxo Feb 12, 2025
7bc854d
refactor: download csv method
lissavxo Feb 13, 2025
7fb13f6
refactor: download csv button transactions
lissavxo Feb 13, 2025
cf0689a
tests: collapse payments update
lissavxo Feb 13, 2025
2f7e525
feat: collapse by default
lissavxo Feb 13, 2025
5a8748b
tests: fix transaction service
lissavxo Feb 13, 2025
39000b4
feat: concatenate addresses
lissavxo Feb 13, 2025
326b3c8
feat: change csv delimiter to ;
lissavxo Feb 14, 2025
dff82dd
tests: fix
lissavxo Feb 14, 2025
c7dda63
feat: change csv delimiters
lissavxo Feb 14, 2025
bd541f1
feat: remove addresses repetition csv
lissavxo Feb 17, 2025
ac50297
feat: remove txIds concatenation
lissavxo Feb 19, 2025
09703e7
test: remove concat txid case
lissavxo Feb 19, 2025
e467ba6
fix: multiple values line
lissavxo Feb 19, 2025
93a681b
refactor: collapseSmallPayments logic
lissavxo Feb 21, 2025
cd9e4e4
fix: amount decimals csv
lissavxo Feb 22, 2025
e3f6f34
fix: amount decimals csv
lissavxo Feb 22, 2025
c474677
Merge branch 'feat/collapse-payments-csv' of https://github.com/paybu…
Klakurka Feb 25, 2025
0dfa4c7
feat: separate utc tx csv
lissavxo Feb 25, 2025
a2d177f
Merge branch 'feat/collapse-payments-csv' of https://github.com/paybu…
Klakurka Feb 26, 2025
f2af597
Merge branch 'feat/collapse-payments-csv' of https://github.com/paybu…
Klakurka Feb 26, 2025
d758d00
Merge branch 'feat/collapse-payments-csv' of https://github.com/paybu…
Klakurka Feb 26, 2025
4dad789
fix: uppercase
chedieck Feb 27, 2025
1f51b8d
Merge branch 'master' into feat/collapse-payments-csv
chedieck Feb 27, 2025
0e4cc41
feat: improve error csv creation
lissavxo Feb 27, 2025
2df850b
fix: wrong prop
chedieck Feb 27, 2025
e44efa4
fix: show tx ids in error
chedieck Feb 27, 2025
833111c
refactor: createTransaction -> upsertTransaction
chedieck Feb 27, 2025
f72498d
fix: use utc when flattening timestamp
chedieck Feb 28, 2025
7fa4ef8
refactor: error log
chedieck Feb 28, 2025
b1b8cd8
feat: script to update price connections
chedieck Feb 28, 2025
7e5a6fc
feat: better logs and exit
chedieck Feb 28, 2025
b561946
fix: error throwing
chedieck Feb 28, 2025
793f816
Merge branch 'feat/collapse-payments-csv' of https://github.com/paybu…
Klakurka Feb 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,12 @@ export const RESPONSE_MESSAGES = {
FAILED_TO_FETCH_PRICE_FROM_API_500: (day: string, ticker: string) => { return { statusCode: 500, message: `Failed to fetch ${ticker} price for day ${day}` } },
MISSING_WS_AUTH_KEY_400: { statusCode: 400, message: 'Missing WS_AUTH_KEY environment variable' },
MISSING_PRICE_FOR_TRANSACTION_400: { statusCode: 400, message: 'Missing price for transaction.' },
INVALID_PRICES_AMOUNT_FOR_TX_ON_CSV_CREATION_500: (pricesLenght: number) => { return { statusCode: 500, message: `Wrong number of prices for transactions group in CSV creation. Expected 1, got ${pricesLenght}.` } },
INVALID_PRICES_AMOUNT_FOR_TX_ON_CSV_CREATION_500: (pricesLenght: number) => {
return {
statusCode: 500,
message: `Got wrong number of prices for transactions group in CSV creation. Expected 1, got ${pricesLenght}.`
}
},
INVALID_PRICE_STATE_400: { statusCode: 400, message: 'Missing expected quote price for transaction.' },
COULD_NOT_GET_BLOCK_INFO_500: { statusCode: 500, message: "Couldn't get block info." },
NETWORK_SLUG_NOT_PROVIDED_400: { statusCode: 400, message: "'networkSlug' not provided." },
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"docker": "./scripts/docker-exec-shortcuts.sh",
"ci:integration:test": "yarn pretest && dotenv -e .env.test -- ts-node -O '{\"module\":\"commonjs\"}' node_modules/jest/bin/jest.js tests/integration-tests --forceExit",
"tarDebug": "tar cf debug.tar logs/ paybutton-config.json .env*",
"updateAllPrices": "./scripts/update-all-prices.sh"
"updateAllPrices": "./scripts/update-all-prices.sh",
"updateAllPriceConnections": "./scripts/update-all-price-connections.sh"
},
"dependencies": {
"@emotion/react": "^11.8.2",
Expand Down
4 changes: 2 additions & 2 deletions pages/api/paybutton/download/transactions/[paybuttonId].ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ export default async (req: any, res: any): Promise<void> => {
const userId = req.session.userId
const user = await fetchUserProfileFromId(userId)
const paybuttonId = req.query.paybuttonId as string
const networkTickerReq = (req.query.network as string).toUpperCase()
const networkTickerReq = req.query.network as string

const networkTicker = (networkTickerReq !== '' && isNetworkValid(networkTickerReq as NetworkTickersType)) ? networkTickerReq as NetworkTickersType : undefined
const networkTicker = (networkTickerReq !== '' && isNetworkValid(networkTickerReq as NetworkTickersType)) ? networkTickerReq.toUpperCase() as NetworkTickersType : undefined
let quoteId: number
if (req.query.currency === undefined || req.query.currency === '' || Number.isNaN(req.query.currency)) {
quoteId = user.preferredCurrencyId
Expand Down
4 changes: 2 additions & 2 deletions pages/api/payments/download/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ export default async (req: any, res: any): Promise<void> => {
const userReqTimezone = req.headers.timezone as string
const userPreferredTimezone = user?.preferredTimezone
const timezone = userPreferredTimezone !== '' ? userPreferredTimezone : userReqTimezone
const networkTickerReq = (req.query.network as string).toUpperCase()
const networkTickerReq = req.query.network as string

const networkTicker = (networkTickerReq !== '' && isNetworkValid(networkTickerReq as NetworkTickersType)) ? networkTickerReq as NetworkTickersType : undefined
const networkTicker = (networkTickerReq !== '' && isNetworkValid(networkTickerReq as NetworkTickersType)) ? networkTickerReq.toUpperCase() as NetworkTickersType : undefined
res.setHeader('Content-Type', 'text/csv')
let networkIdArray = Object.values(NETWORK_IDS)
if (networkTicker !== undefined) {
Expand Down
9 changes: 9 additions & 0 deletions scripts/update-all-price-connections.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
set -e

echo This will affect the database. Are you sure of what you\'re doing? [y/N]
read ans
if [[ "$ans" = "Y" || "$ans" = "y" ]]; then
JOBS_ENV=true dotenv -e .env -c -- ts-node -O '{"module":"commonjs"}' -r tsconfig-paths/register ./scripts/updateAllPriceConnections.ts
else
echo Exited.
fi
103 changes: 103 additions & 0 deletions scripts/updateAllPriceConnections.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { flattenTimestamp } from '../services/priceService'
import prisma from 'prisma/clientInstance'
import { connectTransactionsListToPrices } from 'services/transactionService'
import { Transaction } from '@prisma/client'
import moment from 'moment'
import { exit } from 'process'

async function misconnectTxs (txsIds: string[]): Promise<void> {
if (process.env.ENVIRONMENT === 'production') {
return
}
if (txsIds.length === 0) {
return
}
console.log('Misconnecting', txsIds.length, 'for testing purposes')
for (const txId of txsIds) {
const tx = await prisma.transaction.findUniqueOrThrow({
where: {
id: txId
}
})
const txDayTimestamp = flattenTimestamp(tx.timestamp)
let signal = 1
if (txDayTimestamp === flattenTimestamp(moment.utc().unix())) {
signal = -1
}
await prisma.transaction.update({
where: {
id: txId
},
data: {
timestamp: tx.timestamp + (signal * 86400)
}
})
}
console.log('Finished misconnecting txs')
}

async function fixMisconnectedTxs (): Promise<void> {
const total = await prisma.transaction.count()
const pageSize = 1000
let page = 0

await misconnectTxs([
// ADD TXS IDS HERE TO TEST IT
])

console.log('Fixing misconnected txs...')
while (true) {
const txsToFix: Transaction[] = []
// Get txs page
const txs = await prisma.transaction.findMany({
orderBy: {
timestamp: 'asc'
},
include: {
prices: {
select: {
price: {
select: {
timestamp: true
}
}
}
}
},
skip: page * pageSize,
take: pageSize
})

const viewedCount = page * pageSize + txs.length

// Finish if empty
if (txs.length === 0) {
break
}

// Find txs with misaligned timestamps
txs.forEach(tx => {
const txFlattenedTimestamp = flattenTimestamp(tx.timestamp)
const txPriceTimestamps = tx.prices.map(p => p.price.timestamp)
if (txPriceTimestamps.filter(t => t !== txFlattenedTimestamp).length > 0) {
txsToFix.push(tx)
}
})
if (txsToFix.length !== 0) {
console.log(`[${viewedCount}/${total}] Fixing ${txsToFix.length} txs...`)
console.log('Tx ids:\n', txsToFix.map(t => t.id).join('\n'))
await connectTransactionsListToPrices(txsToFix)
console.log(`[${viewedCount}/${total}] Finished fixing ${txsToFix.length} txs.`)
}
console.log(`[${viewedCount}/${total}]`)
page++
}
console.log('FINISHED')
exit()
}

async function run (): Promise<void> {
await fixMisconnectedTxs()
}

void run()
6 changes: 3 additions & 3 deletions services/chronikService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
createManyTransactions,
deleteTransactions,
fetchUnconfirmedTransactions,
createTransaction,
upsertTransaction,
getSimplifiedTransactions,
getSimplifiedTrasaction,
connectAllTransactionsToPrices
Expand Down Expand Up @@ -417,7 +417,7 @@ export class ChronikBlockchainClient {
const addressesWithTransactions = await this.getAddressesForTransaction(transaction)
const inputAddresses = this.getSortedInputAddresses(transaction)
for (const addressWithTransaction of addressesWithTransactions) {
const { created, tx } = await createTransaction(addressWithTransaction.transaction)
const { created, tx } = await upsertTransaction(addressWithTransaction.transaction)
if (tx !== undefined) {
const broadcastTxData = this.broadcastIncomingTx(addressWithTransaction.address.address, tx, inputAddresses)
if (created) { // only execute trigger for newly added txs
Expand Down Expand Up @@ -470,7 +470,7 @@ export class ChronikBlockchainClient {
const inputAddresses = this.getSortedInputAddresses(transaction)

for (const addressWithTransaction of addressesWithTransactions) {
const { created, tx } = await createTransaction(addressWithTransaction.transaction)
const { created, tx } = await upsertTransaction(addressWithTransaction.transaction)
if (tx !== undefined) {
const broadcastTxData = this.broadcastIncomingTx(addressWithTransaction.address.address, tx, inputAddresses)
if (created) { // only execute trigger for newly added txs
Expand Down
2 changes: 1 addition & 1 deletion services/priceService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { validatePriceAPIUrlAndToken, validateNetworkTicker } from 'utils/valida
import moment from 'moment'

export function flattenTimestamp (timestamp: number): number {
const date = moment((timestamp * 1000))
const date = moment.utc((timestamp * 1000))
const dateStart = date.startOf('day')
return dateStart.unix()
}
Expand Down
2 changes: 1 addition & 1 deletion services/transactionService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ interface CreateTransactionResult {
created: boolean
}

export async function createTransaction (
export async function upsertTransaction (
transactionData: Prisma.TransactionUncheckedCreateInput
): Promise<CreateTransactionResult> {
if (transactionData.amount === new Prisma.Decimal(0)) { // out transactions
Expand Down
2 changes: 1 addition & 1 deletion tests/unittests/transactionService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ describe('Create services', () => {
...mockedTransaction,
addressId: mockedBCHAddress.id
}
const result = await transactionService.createTransaction(
const result = await transactionService.upsertTransaction(
argsTransaction
)
expect(result).toEqual({
Expand Down
23 changes: 19 additions & 4 deletions utils/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,16 +139,31 @@ export const collapseSmallPayments = (
}
const totalAmount = tempTxGroup.reduce((sum, p) => sum + Number(p.amount), 0)
const totalValue = tempTxGroup.reduce((sum, p) => sum + Number(getTransactionValue(p)[currency]), 0)
const uniquePrices = new Set()
const uniquePrices: Set<number> = new Set()
const quoteId = QUOTE_IDS[currency.toUpperCase()]
tempTxGroup
.forEach(tx => {
const price = tx.prices.find(p => p.price.quoteId === QUOTE_IDS[currency.toUpperCase()])!.price.value
const price = tx.prices.find(p => p.price.quoteId === quoteId)!.price.value
uniquePrices.add(Number(price))
})
if (uniquePrices.size !== 1) {
throw new Error(RESPONSE_MESSAGES.INVALID_PRICES_AMOUNT_FOR_TX_ON_CSV_CREATION_500(uniquePrices.size).message)
if (uniquePrices.size > 1) {
const nonUniquePrices = [...uniquePrices]
const txsForPrice: Record<number, string[]> = {}
nonUniquePrices.forEach(nonUniquePrice => {
txsForPrice[nonUniquePrice] = tempTxGroup.filter(tx => nonUniquePrice === tx.prices.find(p => p.price.quoteId === quoteId)!.price.value.toNumber()).map(tx => tx.id)
})
console.error('ERROR WHEN TRYING TO COLLAPSE TXS INTO DIFFERENT PRICES:', { txsForPrice, nonUniquePrices })
} else {
console.error('ERROR WHEN TRYING TO COLLAPSE TXS INTO DIFFERENT PRICES, NO PRICES FOR GROUP KEY', { groupKey })
}

throw new Error(
RESPONSE_MESSAGES
.INVALID_PRICES_AMOUNT_FOR_TX_ON_CSV_CREATION_500(tempTxGroup.length).message
)
}
const rate = uniquePrices.values().next().value
const rate = new Prisma.Decimal(uniquePrices.values().next().value as number)
const buttonName = tempTxGroup[0].address.paybuttons[0].paybutton.name
const notes = `${buttonName} - ${tempTxGroup.length.toString()} transactions`

Expand Down