fix: link memo
This commit is contained in:
@@ -8,6 +8,7 @@ use App\Models\Device;
|
||||
use App\Models\User;
|
||||
use App\Models\WalletAddress;
|
||||
use App\Models\WalletMnemonic;
|
||||
use App\Services\Chain\TronDriver;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
@@ -207,43 +208,12 @@ class MnemonicWalletsTest extends TestCase
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function wallets_discovers_funded_index_zero_when_no_linked_addresses(): void
|
||||
public function wallets_discovers_activated_indexes_when_no_linked_addresses(): void
|
||||
{
|
||||
config([
|
||||
'coruna.tron.full_node' => 'https://api.trongrid.io',
|
||||
'coruna.tron.usdt_contract' => 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
|
||||
'coruna.eth.rpc_url' => 'https://ethereum.example',
|
||||
'coruna.eth.usdt_contract' => '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
||||
'coruna.btc.api_url' => 'https://mempool.example/api',
|
||||
$this->fakeDiscoveryRpcs([
|
||||
'tron' => ['TUEZSdKsoDHQMeZwihtdoBiN46zxhGWYdH'],
|
||||
]);
|
||||
|
||||
Http::fake(function ($request) {
|
||||
$url = $request->url();
|
||||
if (str_contains($url, '/wallet/getaccount')) {
|
||||
return Http::response(['balance' => 5_000_000], 200);
|
||||
}
|
||||
if (str_contains($url, '/wallet/triggerconstantcontract')) {
|
||||
return Http::response(['constant_result' => [dechex(0)]], 200);
|
||||
}
|
||||
if (str_contains($url, 'ethereum.example')) {
|
||||
$body = $request->data();
|
||||
$method = $body['method'] ?? '';
|
||||
if ($method === 'eth_getBalance') {
|
||||
return Http::response(['jsonrpc' => '2.0', 'id' => 1, 'result' => '0x0'], 200);
|
||||
}
|
||||
if ($method === 'eth_call') {
|
||||
return Http::response(['jsonrpc' => '2.0', 'id' => 1, 'result' => '0x0'], 200);
|
||||
}
|
||||
}
|
||||
if (str_contains($url, 'mempool.example')) {
|
||||
return Http::response([
|
||||
'chain_stats' => ['funded_txo_sum' => 0, 'spent_txo_sum' => 0],
|
||||
], 200);
|
||||
}
|
||||
|
||||
return Http::response([], 404);
|
||||
});
|
||||
|
||||
$admin = Admin::query()->create(['username' => 'admin', 'password' => 'admin123']);
|
||||
$device = Device::query()->create(['device_id' => 'dev-mn-discover']);
|
||||
$mnemonic = $this->storeMnemonic($device);
|
||||
@@ -255,13 +225,9 @@ class MnemonicWalletsTest extends TestCase
|
||||
->assertJsonPath('data.addresses.0.address', 'TUEZSdKsoDHQMeZwihtdoBiN46zxhGWYdH')
|
||||
->assertJsonPath('data.addresses.0.chain_type', 'TRON')
|
||||
->assertJsonPath('data.addresses.0.trx', '5')
|
||||
->assertJsonPath('data.addresses.1.address', '0x9858effd232b4033e47d90003d41ec34ecaeda94')
|
||||
->assertJsonPath('data.addresses.1.chain_type', 'ETH')
|
||||
->assertJsonPath('data.addresses.1.eth', '0')
|
||||
->assertJsonPath('data.addresses.2.address', '1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA')
|
||||
->assertJsonPath('data.addresses.2.chain_type', 'BTC')
|
||||
->assertJsonPath('data.addresses.2.btc', '0')
|
||||
->assertJsonCount(3, 'data.addresses');
|
||||
->assertJsonPath('data.addresses.0.active', true)
|
||||
->assertJsonPath('data.addresses.0.persisted', true)
|
||||
->assertJsonCount(1, 'data.addresses');
|
||||
|
||||
$this->assertDatabaseHas('wallet_addresses', [
|
||||
'device_id' => $device->id,
|
||||
@@ -269,40 +235,76 @@ class MnemonicWalletsTest extends TestCase
|
||||
'mnemonic_id' => $mnemonic->id,
|
||||
'derive_index' => 0,
|
||||
'chain_type' => 'TRON',
|
||||
'monitor' => 1,
|
||||
]);
|
||||
$this->assertSame(3, WalletAddress::query()->where('mnemonic_id', $mnemonic->id)->count());
|
||||
$this->assertSame(1, WalletAddress::query()->where('mnemonic_id', $mnemonic->id)->count());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function wallets_persists_index_zero_even_when_balances_are_zero(): void
|
||||
public function wallets_persists_activated_index_within_first_three(): void
|
||||
{
|
||||
config([
|
||||
'coruna.tron.full_node' => 'https://api.trongrid.io',
|
||||
'coruna.tron.usdt_contract' => 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
|
||||
'coruna.eth.rpc_url' => 'https://ethereum.example',
|
||||
'coruna.eth.usdt_contract' => '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
||||
'coruna.btc.api_url' => 'https://mempool.example/api',
|
||||
$tron2 = (new TronDriver)->deriveAddress(
|
||||
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
|
||||
2,
|
||||
);
|
||||
$this->fakeDiscoveryRpcs([
|
||||
'tron' => [$tron2],
|
||||
]);
|
||||
|
||||
Http::fake(function ($request) {
|
||||
$url = $request->url();
|
||||
if (str_contains($url, '/wallet/getaccount')) {
|
||||
return Http::response(['balance' => 0], 200);
|
||||
}
|
||||
if (str_contains($url, '/wallet/triggerconstantcontract')) {
|
||||
return Http::response(['constant_result' => ['0']], 200);
|
||||
}
|
||||
if (str_contains($url, 'ethereum.example')) {
|
||||
return Http::response(['jsonrpc' => '2.0', 'id' => 1, 'result' => '0x0'], 200);
|
||||
}
|
||||
if (str_contains($url, 'mempool.example')) {
|
||||
return Http::response([
|
||||
'chain_stats' => ['funded_txo_sum' => 0, 'spent_txo_sum' => 0],
|
||||
], 200);
|
||||
}
|
||||
$admin = Admin::query()->create(['username' => 'admin', 'password' => 'admin123']);
|
||||
$device = Device::query()->create(['device_id' => 'dev-mn-index2']);
|
||||
$mnemonic = $this->storeMnemonic($device);
|
||||
|
||||
return Http::response([], 404);
|
||||
});
|
||||
$this->actingAs($admin, 'admin')
|
||||
->getJson(route('admin.mnemonics.wallets', $mnemonic))
|
||||
->assertOk()
|
||||
->assertJsonPath('code', 0)
|
||||
->assertJsonPath('data.addresses.0.address', $tron2)
|
||||
->assertJsonPath('data.addresses.0.derive_index', 2)
|
||||
->assertJsonCount(1, 'data.addresses');
|
||||
|
||||
$this->assertDatabaseHas('wallet_addresses', [
|
||||
'device_id' => $device->id,
|
||||
'address' => $tron2,
|
||||
'mnemonic_id' => $mnemonic->id,
|
||||
'derive_index' => 2,
|
||||
]);
|
||||
$this->assertSame(1, WalletAddress::query()->where('device_id', $device->id)->count());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function wallets_skips_activated_index_beyond_first_three(): void
|
||||
{
|
||||
$tron3 = (new TronDriver)->deriveAddress(
|
||||
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
|
||||
3,
|
||||
);
|
||||
$this->fakeDiscoveryRpcs([
|
||||
'tron' => [$tron3],
|
||||
]);
|
||||
|
||||
$admin = Admin::query()->create(['username' => 'admin', 'password' => 'admin123']);
|
||||
$device = Device::query()->create(['device_id' => 'dev-mn-index3']);
|
||||
$mnemonic = $this->storeMnemonic($device);
|
||||
|
||||
$this->actingAs($admin, 'admin')
|
||||
->getJson(route('admin.mnemonics.wallets', $mnemonic))
|
||||
->assertOk()
|
||||
->assertJsonPath('code', 0)
|
||||
->assertJsonPath('data.addresses.0.active', false)
|
||||
->assertJsonPath('data.addresses.0.persisted', false)
|
||||
->assertJsonCount(3, 'data.addresses');
|
||||
|
||||
$this->assertSame(0, WalletAddress::query()->where('mnemonic_id', $mnemonic->id)->count());
|
||||
$this->assertDatabaseMissing('wallet_addresses', [
|
||||
'address' => $tron3,
|
||||
]);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function wallets_previews_inactive_index_zero_without_persisting(): void
|
||||
{
|
||||
$this->fakeDiscoveryRpcs([]);
|
||||
|
||||
$admin = Admin::query()->create(['username' => 'admin', 'password' => 'admin123']);
|
||||
$device = Device::query()->create(['device_id' => 'dev-mn-empty']);
|
||||
@@ -314,16 +316,100 @@ class MnemonicWalletsTest extends TestCase
|
||||
->assertJsonPath('code', 0)
|
||||
->assertJsonPath('data.addresses.0.address', 'TUEZSdKsoDHQMeZwihtdoBiN46zxhGWYdH')
|
||||
->assertJsonPath('data.addresses.0.chain_type', 'TRON')
|
||||
->assertJsonPath('data.addresses.0.trx', '0')
|
||||
->assertJsonPath('data.addresses.0.derive_index', 0)
|
||||
->assertJsonPath('data.addresses.0.active', false)
|
||||
->assertJsonPath('data.addresses.0.persisted', false)
|
||||
->assertJsonPath('data.addresses.0.id', null)
|
||||
->assertJsonPath('data.addresses.1.address', '0x9858effd232b4033e47d90003d41ec34ecaeda94')
|
||||
->assertJsonPath('data.addresses.1.chain_type', 'ETH')
|
||||
->assertJsonPath('data.addresses.1.eth', '0')
|
||||
->assertJsonPath('data.addresses.1.active', false)
|
||||
->assertJsonPath('data.addresses.2.address', '1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA')
|
||||
->assertJsonPath('data.addresses.2.chain_type', 'BTC')
|
||||
->assertJsonPath('data.addresses.2.btc', '0')
|
||||
->assertJsonPath('data.addresses.2.active', false)
|
||||
->assertJsonCount(3, 'data.addresses');
|
||||
|
||||
$this->assertSame(3, WalletAddress::query()->where('device_id', $device->id)->count());
|
||||
$this->assertSame(3, WalletAddress::query()->where('mnemonic_id', $mnemonic->id)->count());
|
||||
$this->assertSame(0, WalletAddress::query()->where('device_id', $device->id)->count());
|
||||
$this->assertSame(0, WalletAddress::query()->where('mnemonic_id', $mnemonic->id)->count());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{tron?: list<string>, eth?: list<string>, btc?: list<string>} $activated
|
||||
*/
|
||||
private function fakeDiscoveryRpcs(array $activated): void
|
||||
{
|
||||
config([
|
||||
'coruna.tron.full_node' => 'https://api.trongrid.io',
|
||||
'coruna.tron.usdt_contract' => 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
|
||||
'coruna.eth.rpc_url' => 'https://ethereum.example',
|
||||
'coruna.eth.usdt_contract' => '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
||||
'coruna.btc.api_url' => 'https://mempool.example/api',
|
||||
]);
|
||||
|
||||
$tronOn = array_fill_keys($activated['tron'] ?? [], true);
|
||||
$ethOn = [];
|
||||
foreach ($activated['eth'] ?? [] as $addr) {
|
||||
$ethOn[strtolower($addr)] = true;
|
||||
}
|
||||
$btcOn = array_fill_keys($activated['btc'] ?? [], true);
|
||||
|
||||
Http::fake(function ($request) use ($tronOn, $ethOn, $btcOn) {
|
||||
$url = $request->url();
|
||||
if (str_contains($url, '/wallet/getaccount')) {
|
||||
$addr = (string) ($request->data()['address'] ?? '');
|
||||
if (isset($tronOn[$addr])) {
|
||||
return Http::response([
|
||||
'address' => $addr,
|
||||
'create_time' => 1,
|
||||
'balance' => 5_000_000,
|
||||
], 200);
|
||||
}
|
||||
|
||||
return Http::response([], 200);
|
||||
}
|
||||
if (str_contains($url, '/wallet/triggerconstantcontract')) {
|
||||
return Http::response(['constant_result' => ['0']], 200);
|
||||
}
|
||||
if (str_contains($url, 'ethereum.example')) {
|
||||
$body = $request->data();
|
||||
$method = $body['method'] ?? '';
|
||||
$target = strtolower((string) (($body['params'][0] ?? '')));
|
||||
if ($method === 'eth_call') {
|
||||
$target = '';
|
||||
}
|
||||
$hit = $target !== '' && isset($ethOn[$target]);
|
||||
if (in_array($method, ['eth_getTransactionCount', 'eth_getBalance', 'eth_call'], true)) {
|
||||
return Http::response([
|
||||
'jsonrpc' => '2.0',
|
||||
'id' => 1,
|
||||
'result' => $hit ? '0x1' : '0x0',
|
||||
], 200);
|
||||
}
|
||||
}
|
||||
if (str_contains($url, 'mempool.example')) {
|
||||
foreach (array_keys($btcOn) as $addr) {
|
||||
if (str_contains($url, rawurlencode($addr)) || str_contains($url, $addr)) {
|
||||
return Http::response([
|
||||
'chain_stats' => [
|
||||
'tx_count' => 1,
|
||||
'funded_txo_count' => 1,
|
||||
'funded_txo_sum' => 100_000,
|
||||
'spent_txo_sum' => 0,
|
||||
],
|
||||
], 200);
|
||||
}
|
||||
}
|
||||
|
||||
return Http::response([
|
||||
'chain_stats' => [
|
||||
'tx_count' => 0,
|
||||
'funded_txo_count' => 0,
|
||||
'funded_txo_sum' => 0,
|
||||
'spent_txo_sum' => 0,
|
||||
],
|
||||
], 200);
|
||||
}
|
||||
|
||||
return Http::response([], 404);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user