Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
dadc66d
Refactor to use common processTx
paullinator Dec 6, 2025
c4c2f6d
fixup! Refactor to use common processTx
paullinator Mar 16, 2026
ee0d2db
Include all moonpay txs
paullinator Dec 6, 2025
592323e
Add EdgeAsset info for moonpay
paullinator Dec 7, 2025
89526b5
fixup! Add EdgeAsset info for moonpay
paullinator Mar 16, 2026
c978321
Add EdgeAsset info for thorchain/maya
paullinator Dec 7, 2025
a64a0ec
Add EdgeAsset info for changenow
paullinator Dec 7, 2025
1cd7bb8
Add EdgeAsset info for sideshift
paullinator Dec 9, 2025
8ab62bf
Add EdgeAsset info for banxa
paullinator Dec 11, 2025
7aafdd6
Add EdgeAsset info for godex
paullinator Dec 12, 2025
f4748ad
Add EdgeAsset info for changehero
paullinator Dec 12, 2025
65eba3a
Improve v3 rates logging
paullinator Dec 12, 2025
52d6952
Check pluginId/tokenId for updating transactions
paullinator Dec 12, 2025
80a20e9
fixup! Check pluginId/tokenId for updating transactions
paullinator Mar 16, 2026
e83aa38
Fix ratesEngine bug
paullinator Dec 12, 2025
2771fb4
Add EdgeAsset info for letsexchange
paullinator Dec 12, 2025
423283a
fixup! Add EdgeAsset info for letsexchange
paullinator Mar 16, 2026
ac8998a
Add indices for pluginId/tokenId fields
paullinator Dec 12, 2025
f83f024
Add EdgeAsset info for exolix
paullinator Dec 13, 2025
67e6eb2
Add disablePartnerQuery feature
paullinator Dec 13, 2025
6472c79
fixup! Add disablePartnerQuery feature
paullinator Mar 16, 2026
b71f327
Use scoped logging
paullinator Dec 14, 2025
63d9530
fixup! Use scoped logging
paullinator Mar 16, 2026
25357d3
Fix rates server bookmarking
paullinator Dec 15, 2025
606fd2a
Optimize rates engine
paullinator Dec 15, 2025
fa64074
fixup! Optimize rates engine
paullinator Mar 16, 2026
f827538
For a partner to run with soloPartnerId even if disabled in couch
paullinator Dec 18, 2025
6f8889d
Use semaphores for queryEngine
paullinator Dec 25, 2025
fdb4fb4
Add EdgeAsset info for bitrefill
paullinator Dec 27, 2025
d7d5fee
Add rango query plugin
paullinator Dec 28, 2025
5371983
Add max new transactions for letsexchange
paullinator Dec 24, 2025
28db8e6
Add support for Binance Beacon Chain tokens for Letsexchange
paullinator Jan 7, 2026
287aecd
Fix banxa by adding zcash.
paullinator Jan 7, 2026
59a6926
Add retry when writing progress cache.
paullinator Jan 7, 2026
b2327ff
Add opbnb to changenow
paullinator Feb 11, 2026
93744d0
Add monad
paullinator Feb 25, 2026
77414a9
Fix issue with testData.json
samholmes Jan 12, 2026
9674f45
Fix API to use apiKey instead of appId
paullinator Dec 21, 2025
aeb8625
Fix partner plugin logging, token normalization, and cache handling
cursoragent Feb 27, 2026
868a00c
fixup! Fix partner plugin logging, token normalization, and cache han…
paullinator Mar 16, 2026
053e532
Add scripts to patch invalid token IDs and reset USD values.
paullinator Mar 26, 2026
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
"@typescript-eslint/eslint-plugin": "^5.36.2",
"@typescript-eslint/parser": "^5.36.2",
"assert": "^2.0.0",
"async-mutex": "^0.5.0",
"browserify-zlib": "^0.2.0",
"chai": "^4.3.4",
"eslint": "^8.19.0",
Expand Down
9 changes: 5 additions & 4 deletions src/bin/destroyPartition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import js from 'jsonfile'
import nano from 'nano'

import { pagination } from '../dbutils'
import { datelog } from '../util'
import { createScopedLog, datelog } from '../util'

const config = js.readFileSync('./config.json')
const nanoDb = nano(config.couchDbFullpath)
Expand Down Expand Up @@ -43,10 +43,11 @@ async function main(partitionName: string): Promise<void> {
return
}

const log = createScopedLog('destroy', partitionName)
try {
await pagination(transactions, reportsTransactions)
datelog(`Successfully Deleted: ${transactions.length} docs`)
datelog(`Successfully Deleted: partition ${partitionName}`)
await pagination(transactions, reportsTransactions, log)
log(`Successfully Deleted: ${transactions.length} docs`)
log(`Successfully Deleted: partition ${partitionName}`)

// Delete progress Cache
const split = partitionName.split('_')
Expand Down
5 changes: 4 additions & 1 deletion src/bin/fioPromo/fioLookup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import path from 'path'

import { queryChangeNow } from '../../partners/changenow'
import { PluginParams, StandardTx } from '../../types'
import { createScopedLog } from '../../util'
import { defaultSettings } from './fioInfo'

let addressList: string[] = []
Expand Down Expand Up @@ -48,12 +49,14 @@ export async function getFioTransactions(
dateFrom: Date,
dateTo: Date
): Promise<StandardTx[]> {
const log = createScopedLog('fio', 'changenow')
// Get public keys from offset
const pluginConfig: PluginParams = {
settings: { dateFrom, dateTo, to: currencyCode },
apiKeys: {
changenowApiKey: config.changenowApiKey
}
},
log
}

const txnList = await queryChangeNow(pluginConfig)
Expand Down
222 changes: 222 additions & 0 deletions src/bin/fixTokenIds.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
import js from 'jsonfile'
import nano from 'nano'

import { DbTx } from '../types'
import { datelog } from '../util'
import { createTokenId, tokenTypes } from '../util/asEdgeTokenId'

const config = js.readFileSync('./config.json')
const nanoDb = nano(config.couchDbFullpath)

const QUERY_LIMIT = 500
const DRY_RUN = process.argv.includes('--dry-run')

const ALL_PARTITIONS = [
'edge_banxa',
'edge_bitaccess',
'edge_bitrefill',
'edge_bitsofgold',
'edge_bity',
'edge_changelly',
'edge_changehero',
'edge_changenow',
'edge_coinswitch',
'edge_exolix',
'edge_faast',
'edge_foxExchange',
'edge_godex',
'edge_kado',
'edge_letsexchange',
'edge_libertyx',
'edge_lifi',
'edge_moonpay',
'edge_paybis',
'edge_paytrie',
'edge_rango',
'edge_safello',
'edge_shapeshift',
'edge_sideshift',
'edge_simplex',
'edge_swapuz',
'edge_switchain',
'edge_thorchain',
'edge_totle',
'edge_transak',
'edge_wyre',
'edge_xanpool'
]

/**
* Re-derive the tokenId using createTokenId. Treats the existing tokenId
* value as a raw contract address and normalizes it. Returns undefined
* when the value is already correct or cannot be fixed.
*/
function normalizeTokenId(
pluginId: string | undefined,
currencyCode: string,
tokenId: string | null | undefined
): string | null | undefined {
if (tokenId == null || pluginId == null) return undefined

const tokenType = tokenTypes[pluginId] ?? null
if (tokenType == null) return undefined // chain doesn't support tokens

try {
const normalized = createTokenId(tokenType, currencyCode, tokenId)
return normalized !== tokenId ? normalized : undefined
} catch {
return undefined
}
}

interface FixStats {
scanned: number
fixed: number
errors: number
byPartition: Record<string, number>
byField: { deposit: number; payout: number }
}

fixTokenIds().catch(e => {
datelog(e)
process.exit(1)
})

async function fixTokenIds(): Promise<void> {
const reportsTransactions = nanoDb.use('reports_transactions')

const stats: FixStats = {
scanned: 0,
fixed: 0,
errors: 0,
byPartition: {},
byField: { deposit: 0, payout: 0 }
}

const partitions =
process.argv[2] != null && process.argv[2] !== '--dry-run'
? [process.argv[2]]
: ALL_PARTITIONS

if (DRY_RUN) datelog('DRY RUN — no changes will be written')

for (const partition of partitions) {
datelog(`Scanning partition: ${partition}`)
let bookmark: string | undefined

while (true) {
const query: any = {
selector: {
$or: [
{ depositTokenId: { $type: 'string' } },
{ payoutTokenId: { $type: 'string' } }
]
},
bookmark,
limit: QUERY_LIMIT
}

let result: any
try {
result = await reportsTransactions.partitionedFind(partition, query)
} catch (e) {
// Partition may not exist
const err = e as { statusCode?: number }
if (err.statusCode === 404) {
datelog(` Partition ${partition} not found, skipping`)
break
}
throw e
}

const { docs } = result
if (docs.length === 0) break

if (typeof result.bookmark === 'string' && docs.length === QUERY_LIMIT) {
bookmark = result.bookmark
} else {
bookmark = undefined
}

stats.scanned += docs.length

const docsToUpdate: any[] = []

for (const d of docs) {
const doc: DbTx = d
let changed = false

const fixedDeposit = normalizeTokenId(
doc.depositChainPluginId,
doc.depositCurrency,
doc.depositTokenId
)
if (fixedDeposit !== undefined) {
datelog(
` FIX ${doc._id} depositTokenId: "${doc.depositTokenId}" -> "${fixedDeposit}"`
)
doc.depositTokenId = fixedDeposit
changed = true
stats.byField.deposit++
}

const fixedPayout = normalizeTokenId(
doc.payoutChainPluginId,
doc.payoutCurrency,
doc.payoutTokenId
)
if (fixedPayout !== undefined) {
datelog(
` FIX ${doc._id} payoutTokenId: "${doc.payoutTokenId}" -> "${fixedPayout}"`
)
doc.payoutTokenId = fixedPayout
changed = true
stats.byField.payout++
}

if (changed) {
doc.usdValue = -1
docsToUpdate.push(d)
stats.byPartition[partition] = (stats.byPartition[partition] ?? 0) + 1
}
}

if (docsToUpdate.length > 0) {
stats.fixed += docsToUpdate.length
if (!DRY_RUN) {
try {
await reportsTransactions.bulk({ docs: docsToUpdate })
datelog(` Wrote ${docsToUpdate.length} fixed docs in ${partition}`)
} catch (e) {
datelog(` Error writing bulk update for ${partition}:`, e)
stats.errors++
}
} else {
datelog(
` Would write ${docsToUpdate.length} fixed docs in ${partition}`
)
}
}

if (bookmark == null) break
}
}

datelog('\n=== Summary ===')
datelog(`Total documents scanned: ${stats.scanned}`)
datelog(`Total documents fixed: ${stats.fixed}`)
datelog(` Deposit tokenIds fixed: ${stats.byField.deposit}`)
datelog(` Payout tokenIds fixed: ${stats.byField.payout}`)
if (Object.keys(stats.byPartition).length > 0) {
datelog('Fixes by partition:')
for (const [partition, count] of Object.entries(stats.byPartition)) {
datelog(` ${partition}: ${count}`)
}
}
if (stats.errors > 0) {
datelog(`Errors encountered: ${stats.errors}`)
}
if (DRY_RUN) {
datelog('DRY RUN complete — no changes were made')
}
}
69 changes: 69 additions & 0 deletions src/bin/resetUsdValues.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import js from 'jsonfile'
import nano from 'nano'

import { DbTx } from '../types'
import { datelog } from '../util'

const config = js.readFileSync('./config.json')
const nanoDb = nano(config.couchDbFullpath)

const QUERY_LIMIT = 1000

resetUsdValues().catch(e => {
datelog(e)
process.exit(1)
})

async function resetUsdValues(): Promise<void> {
const partitionName = process.argv[2]
if (partitionName == null) {
console.error('Usage: resetUsdValues <partitionName>')
process.exit(1)
}

const reportsTransactions = nanoDb.use('reports_transactions')
let bookmark: string | undefined
let totalUpdated = 0

while (true) {
const query: any = {
selector: {
usdValue: { $gte: 0 }
},
bookmark,
limit: QUERY_LIMIT
}

const result: any = await reportsTransactions.partitionedFind(
partitionName,
query
)
const { docs } = result
if (docs.length === 0) break

if (typeof result.bookmark === 'string' && docs.length === QUERY_LIMIT) {
bookmark = result.bookmark
} else {
bookmark = undefined
}

for (const d of docs) {
const doc: DbTx = d
doc.usdValue = -1
}

try {
await reportsTransactions.bulk({ docs })
totalUpdated += docs.length
datelog(`Updated ${docs.length} docs (${totalUpdated} total)`)
} catch (e) {
datelog('Error doing bulk update', e)
}

if (bookmark == null) break
}

datelog(
`Done. Set usdValue to -1 for ${totalUpdated} docs in ${partitionName}`
)
}
32 changes: 22 additions & 10 deletions src/bin/testpartner.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,28 @@
import { thorchain as plugin } from '../partners/thorchain'
import { PluginParams } from '../types.js'
import { PluginParams } from '../types'
import { createScopedLog } from '../util'

const pluginParams: PluginParams = {
settings: {
offset: 0
},
apiKeys: {
thorchainAddress: ''
async function main(): Promise<void> {
const partnerId = process.argv[2]
if (partnerId == null) {
console.log(
'Usage: node -r sucrase/register src/bin/testpartner.ts <partnerId>'
)
process.exit(1)
}

const pluginParams: PluginParams = {
settings: {},
apiKeys: {},
log: createScopedLog('edge', partnerId)
}

// Dynamically import the partner plugin
const pluginModule = await import(`../partners/${partnerId}`)
const plugin = pluginModule[partnerId]
if (plugin?.queryFunc == null) {
throw new Error(`Plugin ${partnerId} does not have a queryFunc`)
}
}

async function main(): Promise<void> {
const result = await plugin.queryFunc(pluginParams)
console.log(JSON.stringify(result, null, 2))
}
Expand Down
Loading
Loading