177 lines
4.8 KiB
PHP
177 lines
4.8 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
|
|
class WalletAddress extends Model
|
|
{
|
|
/** @var list<string> */
|
|
public const COIN_COLUMNS = ['usdt', 'trx', 'eth', 'btc', 'bnb', 'sol'];
|
|
|
|
/** Display decimal places per coin (UI). */
|
|
public const DISPLAY_DECIMALS = [
|
|
'usdt' => 2,
|
|
'trx' => 6,
|
|
'eth' => 6,
|
|
'btc' => 8,
|
|
'bnb' => 6,
|
|
'sol' => 6,
|
|
];
|
|
|
|
protected $fillable = [
|
|
'device_id', 'address', 'chain_type',
|
|
'usdt', 'trx', 'eth', 'btc', 'bnb', 'sol',
|
|
'monitor', 'monitor_synced', 'monitor_failures', 'source', 'mnemonic_id', 'derive_index',
|
|
];
|
|
|
|
protected function casts(): array
|
|
{
|
|
return [
|
|
'monitor' => 'integer',
|
|
'monitor_synced' => 'boolean',
|
|
'monitor_failures' => 'integer',
|
|
'derive_index' => 'integer',
|
|
'usdt' => 'decimal:6',
|
|
'trx' => 'decimal:6',
|
|
'eth' => 'decimal:18',
|
|
'btc' => 'decimal:8',
|
|
'bnb' => 'decimal:18',
|
|
'sol' => 'decimal:9',
|
|
];
|
|
}
|
|
|
|
public function device(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Device::class);
|
|
}
|
|
|
|
public function mnemonic(): BelongsTo
|
|
{
|
|
return $this->belongsTo(WalletMnemonic::class, 'mnemonic_id');
|
|
}
|
|
|
|
/**
|
|
* Map a symbol=>amount payload into coin column attributes (only known coins).
|
|
* Missing symbols are omitted (caller merges with existing row).
|
|
*
|
|
* @param array<string, mixed>|string|null $balance
|
|
* @return array<string, string|int|float|null>
|
|
*/
|
|
public static function coinAttributesFromBalance(mixed $balance): array
|
|
{
|
|
if (! is_array($balance)) {
|
|
return [];
|
|
}
|
|
|
|
$out = [];
|
|
foreach ($balance as $symbol => $value) {
|
|
$col = strtolower((string) $symbol);
|
|
if (! in_array($col, self::COIN_COLUMNS, true)) {
|
|
continue;
|
|
}
|
|
if ($value === null || $value === '') {
|
|
$out[$col] = null;
|
|
continue;
|
|
}
|
|
if (! is_numeric($value)) {
|
|
continue;
|
|
}
|
|
$out[$col] = $value;
|
|
}
|
|
|
|
return $out;
|
|
}
|
|
|
|
public static function displayDecimals(string $coin): int
|
|
{
|
|
$coin = strtolower($coin);
|
|
|
|
return self::DISPLAY_DECIMALS[$coin] ?? 6;
|
|
}
|
|
|
|
public static function formatAmount(string $coin, mixed $amount): string
|
|
{
|
|
if ($amount === null || $amount === '') {
|
|
return '';
|
|
}
|
|
if (! is_numeric($amount)) {
|
|
return (string) $amount;
|
|
}
|
|
|
|
$decimals = self::displayDecimals($coin);
|
|
$formatted = number_format((float) $amount, $decimals, '.', '');
|
|
$formatted = rtrim(rtrim($formatted, '0'), '.');
|
|
|
|
return $formatted === '' ? '0' : $formatted;
|
|
}
|
|
|
|
/**
|
|
* @return array<string, string>
|
|
*/
|
|
public function formattedCoins(): array
|
|
{
|
|
$out = [];
|
|
foreach (self::COIN_COLUMNS as $col) {
|
|
$out[$col] = self::formatAmount($col, $this->{$col});
|
|
}
|
|
|
|
return $out;
|
|
}
|
|
|
|
public function coinsSummary(): ?string
|
|
{
|
|
$parts = [];
|
|
foreach (self::COIN_COLUMNS as $col) {
|
|
$raw = $this->{$col};
|
|
if ($raw === null || $raw === '' || (float) $raw == 0.0) {
|
|
continue;
|
|
}
|
|
$parts[strtoupper($col)] = self::formatAmount($col, $raw);
|
|
}
|
|
|
|
return $parts === [] ? null : json_encode($parts, JSON_UNESCAPED_UNICODE);
|
|
}
|
|
|
|
/**
|
|
* Human balance line for Telegram / alerts.
|
|
* Tron always includes TRX + USDT (even when 0); other chains omit empty/zero.
|
|
*/
|
|
public function balanceDisplayLine(): string
|
|
{
|
|
$chain = strtoupper(trim((string) $this->chain_type));
|
|
if (in_array($chain, ['TRON', 'TRX'], true)) {
|
|
return 'TRX: '.self::formatAmount('trx', $this->trx ?? 0)
|
|
.' USDT: '.self::formatAmount('usdt', $this->usdt ?? 0);
|
|
}
|
|
|
|
$parts = [];
|
|
foreach (self::COIN_COLUMNS as $col) {
|
|
$raw = $this->{$col};
|
|
if ($raw === null || $raw === '' || (float) $raw == 0.0) {
|
|
continue;
|
|
}
|
|
$parts[] = strtoupper($col).': '.self::formatAmount($col, $raw);
|
|
}
|
|
|
|
return $parts === [] ? '—' : implode(' ', $parts);
|
|
}
|
|
|
|
/**
|
|
* Snapshot of coin columns for change detection.
|
|
*
|
|
* @return array<string, string|null>
|
|
*/
|
|
public function coinSnapshot(): array
|
|
{
|
|
$out = [];
|
|
foreach (self::COIN_COLUMNS as $col) {
|
|
$v = $this->{$col};
|
|
$out[$col] = $v === null || $v === '' ? null : (string) $v;
|
|
}
|
|
|
|
return $out;
|
|
}
|
|
}
|