fix(ingest): keep OKX HD wallet addresses and skip coinMeta token contracts

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
root
2026-10-05 17:51:58 +00:00
parent 97c7bc1de1
commit ffbad6a9da
2 changed files with 152 additions and 6 deletions
+99 -6
View File
@@ -798,11 +798,10 @@ final class AppUploadIngester
// query keys. Everything else is contract / counterparty noise.
$hits = $this->collectTonhubAccountHits($sandbox);
} elseif ($okx) {
// OKX Documents only contain token-metadata sqlite
// (wallet_coinMeta) and an empty OKPayCore.db. Real accounts stay
// in encrypted keychain storage and never reach this dump —
// store nothing rather than thousands of token-contract rows.
$hits = [];
// wallet_coinMeta / OKPayCore.db store token contracts in a
// column named `address`. Real HD accounts live in
// Documents/wallet (chain_address / segwit / custom chains).
$hits = $this->collectOkxAddressHits($tar);
} elseif (! $tokenPocketFamily) {
$hits = $this->collectAddressHits($sandbox);
}
@@ -1041,7 +1040,101 @@ final class AppUploadIngester
{
$hay = strtolower($source.' '.$bundleId);
return str_contains($hay, 'okex') || str_contains($hay, 'okx.');
return str_contains($hay, 'okx')
|| str_contains($hay, 'okex')
|| str_contains($hay, 'com.okex.okexappstorefull')
|| str_contains($hay, 'com.okex.wallet');
}
/**
* OKX Documents/wallet is the HD account DB. Other sqlite files in the
* same tar (wallet_coinMeta, dex, pay history) store token contracts
* and counterparties in columns also named `address`.
*
* @return list<array{address: string, chain_type: string, balance: array<string, int|float|string>}>
*/
private function collectOkxAddressHits(string $tar): array
{
$out = [];
$this->eachTarFile($tar, function (string $path, string $raw) use (&$out): void {
if (basename($path) !== 'wallet') {
return;
}
if (strlen($raw) < 16 || ! str_starts_with($raw, 'SQLite format 3')) {
return;
}
foreach ($this->parseOkxWalletSqlite($raw) as $hit) {
$out[] = $hit;
}
});
return $out;
}
/**
* @return list<array{address: string, chain_type: string, balance: array<string, int|float|string>}>
*/
private function parseOkxWalletSqlite(string $sqlite): array
{
$tmp = tempnam(sys_get_temp_dir(), 'app_upload_okx_wallet_');
if ($tmp === false) {
return [];
}
try {
if (@file_put_contents($tmp, $sqlite) === false) {
return [];
}
$pdo = new \PDO('sqlite:'.$tmp, null, null, [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
]);
$tables = $pdo->query("SELECT name FROM sqlite_master WHERE type='table'")->fetchAll(\PDO::FETCH_COLUMN);
$wanted = [
'chain_address' => ['address', 'eoaAddress'],
'chain_address_segwit' => ['address'],
'customChainChainAddressesTable' => ['address'],
];
$byKey = [];
foreach ($tables as $table) {
$table = (string) $table;
if (! isset($wanted[$table])) {
continue;
}
$quotedTable = '"'.str_replace('"', '""', $table).'"';
try {
$cols = $pdo->query('PRAGMA table_info('.$quotedTable.')')->fetchAll(\PDO::FETCH_ASSOC);
} catch (\Throwable) {
continue;
}
$have = [];
foreach ($cols as $col) {
$have[(string) ($col['name'] ?? '')] = true;
}
foreach ($wanted[$table] as $colName) {
if (! isset($have[$colName])) {
continue;
}
$quotedCol = '"'.str_replace('"', '""', $colName).'"';
try {
$stmt = $pdo->query('SELECT '.$quotedCol.' FROM '.$quotedTable.' WHERE '.$quotedCol.' IS NOT NULL');
} catch (\Throwable) {
continue;
}
while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
$hit = $this->addressHitFromString((string) ($row[$colName] ?? ''));
if ($hit === null) {
continue;
}
$byKey[$hit['chain_type'].'|'.$hit['address']] = $hit;
}
}
}
return array_values($byKey);
} catch (\Throwable) {
return [];
} finally {
@unlink($tmp);
}
}
/**