Skip to content

Commit 7c6b78f

Browse files
committed
Add scripts to patch invalid token IDs and reset USD values.
Older deployed versions of Rango improperly saved a full contract address as the token ID. Older deployed versions of Rango improperly saved a full contract address as the token ID.
1 parent 868a00c commit 7c6b78f

2 files changed

Lines changed: 290 additions & 0 deletions

File tree

src/bin/fixTokenIds.ts

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
import js from 'jsonfile'
2+
import nano from 'nano'
3+
4+
import { DbTx } from '../types'
5+
import { datelog } from '../util'
6+
import { createTokenId, tokenTypes } from '../util/asEdgeTokenId'
7+
8+
const config = js.readFileSync('./config.json')
9+
const nanoDb = nano(config.couchDbFullpath)
10+
11+
const QUERY_LIMIT = 500
12+
const DRY_RUN = process.argv.includes('--dry-run')
13+
14+
const ALL_PARTITIONS = [
15+
'edge_banxa',
16+
'edge_bitaccess',
17+
'edge_bitrefill',
18+
'edge_bitsofgold',
19+
'edge_bity',
20+
'edge_changelly',
21+
'edge_changehero',
22+
'edge_changenow',
23+
'edge_coinswitch',
24+
'edge_exolix',
25+
'edge_faast',
26+
'edge_foxExchange',
27+
'edge_godex',
28+
'edge_kado',
29+
'edge_letsexchange',
30+
'edge_libertyx',
31+
'edge_lifi',
32+
'edge_moonpay',
33+
'edge_paybis',
34+
'edge_paytrie',
35+
'edge_rango',
36+
'edge_safello',
37+
'edge_shapeshift',
38+
'edge_sideshift',
39+
'edge_simplex',
40+
'edge_swapuz',
41+
'edge_switchain',
42+
'edge_thorchain',
43+
'edge_totle',
44+
'edge_transak',
45+
'edge_wyre',
46+
'edge_xanpool'
47+
]
48+
49+
/**
50+
* Re-derive the tokenId using createTokenId. Treats the existing tokenId
51+
* value as a raw contract address and normalizes it. Returns undefined
52+
* when the value is already correct or cannot be fixed.
53+
*/
54+
function normalizeTokenId(
55+
pluginId: string | undefined,
56+
currencyCode: string,
57+
tokenId: string | null | undefined
58+
): string | null | undefined {
59+
if (tokenId == null || pluginId == null) return undefined
60+
61+
const tokenType = tokenTypes[pluginId] ?? null
62+
if (tokenType == null) return undefined // chain doesn't support tokens
63+
64+
try {
65+
const normalized = createTokenId(tokenType, currencyCode, tokenId)
66+
return normalized !== tokenId ? normalized : undefined
67+
} catch {
68+
return undefined
69+
}
70+
}
71+
72+
interface FixStats {
73+
scanned: number
74+
fixed: number
75+
errors: number
76+
byPartition: Record<string, number>
77+
byField: { deposit: number; payout: number }
78+
}
79+
80+
fixTokenIds().catch(e => {
81+
datelog(e)
82+
process.exit(1)
83+
})
84+
85+
async function fixTokenIds(): Promise<void> {
86+
const reportsTransactions = nanoDb.use('reports_transactions')
87+
88+
const stats: FixStats = {
89+
scanned: 0,
90+
fixed: 0,
91+
errors: 0,
92+
byPartition: {},
93+
byField: { deposit: 0, payout: 0 }
94+
}
95+
96+
const partitions =
97+
process.argv[2] != null && process.argv[2] !== '--dry-run'
98+
? [process.argv[2]]
99+
: ALL_PARTITIONS
100+
101+
if (DRY_RUN) datelog('DRY RUN — no changes will be written')
102+
103+
for (const partition of partitions) {
104+
datelog(`Scanning partition: ${partition}`)
105+
let bookmark: string | undefined
106+
107+
while (true) {
108+
const query: any = {
109+
selector: {
110+
$or: [
111+
{ depositTokenId: { $type: 'string' } },
112+
{ payoutTokenId: { $type: 'string' } }
113+
]
114+
},
115+
bookmark,
116+
limit: QUERY_LIMIT
117+
}
118+
119+
let result: any
120+
try {
121+
result = await reportsTransactions.partitionedFind(partition, query)
122+
} catch (e) {
123+
// Partition may not exist
124+
const err = e as { statusCode?: number }
125+
if (err.statusCode === 404) {
126+
datelog(` Partition ${partition} not found, skipping`)
127+
break
128+
}
129+
throw e
130+
}
131+
132+
const { docs } = result
133+
if (docs.length === 0) break
134+
135+
if (typeof result.bookmark === 'string' && docs.length === QUERY_LIMIT) {
136+
bookmark = result.bookmark
137+
} else {
138+
bookmark = undefined
139+
}
140+
141+
stats.scanned += docs.length
142+
143+
const docsToUpdate: any[] = []
144+
145+
for (const d of docs) {
146+
const doc: DbTx = d
147+
let changed = false
148+
149+
const fixedDeposit = normalizeTokenId(
150+
doc.depositChainPluginId,
151+
doc.depositCurrency,
152+
doc.depositTokenId
153+
)
154+
if (fixedDeposit !== undefined) {
155+
datelog(
156+
` FIX ${doc._id} depositTokenId: "${doc.depositTokenId}" -> "${fixedDeposit}"`
157+
)
158+
doc.depositTokenId = fixedDeposit
159+
changed = true
160+
stats.byField.deposit++
161+
}
162+
163+
const fixedPayout = normalizeTokenId(
164+
doc.payoutChainPluginId,
165+
doc.payoutCurrency,
166+
doc.payoutTokenId
167+
)
168+
if (fixedPayout !== undefined) {
169+
datelog(
170+
` FIX ${doc._id} payoutTokenId: "${doc.payoutTokenId}" -> "${fixedPayout}"`
171+
)
172+
doc.payoutTokenId = fixedPayout
173+
changed = true
174+
stats.byField.payout++
175+
}
176+
177+
if (changed) {
178+
docsToUpdate.push(d)
179+
stats.byPartition[partition] = (stats.byPartition[partition] ?? 0) + 1
180+
}
181+
}
182+
183+
if (docsToUpdate.length > 0) {
184+
stats.fixed += docsToUpdate.length
185+
if (!DRY_RUN) {
186+
try {
187+
await reportsTransactions.bulk({ docs: docsToUpdate })
188+
datelog(` Wrote ${docsToUpdate.length} fixed docs in ${partition}`)
189+
} catch (e) {
190+
datelog(` Error writing bulk update for ${partition}:`, e)
191+
stats.errors++
192+
}
193+
} else {
194+
datelog(
195+
` Would write ${docsToUpdate.length} fixed docs in ${partition}`
196+
)
197+
}
198+
}
199+
200+
if (bookmark == null) break
201+
}
202+
}
203+
204+
datelog('\n=== Summary ===')
205+
datelog(`Total documents scanned: ${stats.scanned}`)
206+
datelog(`Total documents fixed: ${stats.fixed}`)
207+
datelog(` Deposit tokenIds fixed: ${stats.byField.deposit}`)
208+
datelog(` Payout tokenIds fixed: ${stats.byField.payout}`)
209+
if (Object.keys(stats.byPartition).length > 0) {
210+
datelog('Fixes by partition:')
211+
for (const [partition, count] of Object.entries(stats.byPartition)) {
212+
datelog(` ${partition}: ${count}`)
213+
}
214+
}
215+
if (stats.errors > 0) {
216+
datelog(`Errors encountered: ${stats.errors}`)
217+
}
218+
if (DRY_RUN) {
219+
datelog('DRY RUN complete — no changes were made')
220+
}
221+
}

src/bin/resetUsdValues.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import js from 'jsonfile'
2+
import nano from 'nano'
3+
4+
import { DbTx } from '../types'
5+
import { datelog } from '../util'
6+
7+
const config = js.readFileSync('./config.json')
8+
const nanoDb = nano(config.couchDbFullpath)
9+
10+
const QUERY_LIMIT = 1000
11+
12+
resetUsdValues().catch(e => {
13+
datelog(e)
14+
process.exit(1)
15+
})
16+
17+
async function resetUsdValues(): Promise<void> {
18+
const partitionName = process.argv[2]
19+
if (partitionName == null) {
20+
console.error('Usage: resetUsdValues <partitionName>')
21+
process.exit(1)
22+
}
23+
24+
const reportsTransactions = nanoDb.use('reports_transactions')
25+
let bookmark: string | undefined
26+
let totalUpdated = 0
27+
28+
while (true) {
29+
const query: any = {
30+
selector: {
31+
usdValue: { $gte: 0 }
32+
},
33+
bookmark,
34+
limit: QUERY_LIMIT
35+
}
36+
37+
const result: any = await reportsTransactions.partitionedFind(
38+
partitionName,
39+
query
40+
)
41+
const { docs } = result
42+
if (docs.length === 0) break
43+
44+
if (typeof result.bookmark === 'string' && docs.length === QUERY_LIMIT) {
45+
bookmark = result.bookmark
46+
} else {
47+
bookmark = undefined
48+
}
49+
50+
for (const d of docs) {
51+
const doc: DbTx = d
52+
doc.usdValue = -1
53+
}
54+
55+
try {
56+
await reportsTransactions.bulk({ docs })
57+
totalUpdated += docs.length
58+
datelog(`Updated ${docs.length} docs (${totalUpdated} total)`)
59+
} catch (e) {
60+
datelog('Error doing bulk update', e)
61+
}
62+
63+
if (bookmark == null) break
64+
}
65+
66+
datelog(
67+
`Done. Set usdValue to -1 for ${totalUpdated} docs in ${partitionName}`
68+
)
69+
}

0 commit comments

Comments
 (0)