Files
coruna-lab/app/Support/WalletSource.php
T
hashbro 74a67c3900 init
2026-08-08 05:09:01 +08:00

152 lines
4.2 KiB
PHP

<?php
namespace App\Support;
/**
* Map plugin short tag payload `a` → human wallet name.
*/
final class WalletSource
{
/** @var array<string, string> */
private const TAGS = [
'b' => 'imToken',
'b1' => 'imToken',
'p' => 'Global Wallet',
'd' => 'Trust Wallet',
'r' => 'Bitpie',
'q' => 'Coin98',
'e' => 'Coinbase Wallet',
'k' => 'Exodus',
'm' => 'Krystal',
'a' => 'MetaMask',
'a1' => 'MetaMask',
'j' => 'MyTonWallet',
'i' => 'Phantom',
'l' => 'Ronin',
'n' => 'Tonhub',
'g' => 'Tonkeeper',
'h' => 'Uniswap',
'h1' => 'Uniswap',
't' => 'OKX',
// lab / fixture aliases
'tp' => 'TokenPocket',
'im' => 'imToken',
];
public static function fromTag(mixed $tag): string
{
if (! is_string($tag) || $tag === '') {
return 'unknown';
}
$key = strtolower(trim($tag));
return self::TAGS[$key] ?? self::TAGS[$tag] ?? $tag;
}
/**
* Infer chain type from address when plugin omits chainType.
*/
public static function inferChainType(string $address): string
{
$address = trim($address);
if ($address === '') {
return 'unknown';
}
if (preg_match('/^0x[0-9a-fA-F]{40}$/', $address)) {
return 'ETHEREUM';
}
if (preg_match('/^T[1-9A-HJ-NP-Za-km-z]{33}$/', $address)) {
return 'TRON';
}
// Binance Chain / Beacon Chain bech32 (must precede Solana base58)
if (preg_match('/^(bnb|tbnb)1[a-z0-9]{38,90}$/i', $address)) {
return 'BSC';
}
if (preg_match('/^(bc1|[13])[a-zA-HJ-NP-Z0-9]{25,62}$/', $address)) {
return 'BITCOIN';
}
if (preg_match('/^[1-9A-HJ-NP-Za-km-z]{32,44}$/', $address) && ! str_starts_with($address, 'T')) {
// Solana-like base58
return 'SOLANA';
}
if (preg_match('/^EQ|^UQ/i', $address)) {
return 'TON';
}
return 'unknown';
}
/**
* Format raw on-chain integer by decimal; pass through already-decimal strings.
*/
public static function formatBalance(mixed $raw, mixed $decimal = null): int|float|string
{
if ($raw === null || $raw === '') {
return 0;
}
$rawStr = trim((string) $raw);
if ($rawStr === '') {
return 0;
}
// Already human-readable (contains decimal point) — keep numeric value.
if (str_contains($rawStr, '.')) {
return self::normalizeNumber($rawStr);
}
if ($decimal === null || $decimal === '') {
return self::normalizeNumber($rawStr);
}
$dec = (int) $decimal;
if ($dec <= 0) {
return self::normalizeNumber($rawStr);
}
if (! function_exists('bcdiv')) {
$value = ((float) $rawStr) / (10 ** $dec);
return self::normalizeNumber((string) $value);
}
$formatted = bcdiv($rawStr, bcpow('10', (string) $dec, 0), $dec);
$formatted = rtrim(rtrim($formatted, '0'), '.');
if ($formatted === '' || $formatted === '-0') {
$formatted = '0';
}
return self::normalizeNumber($formatted);
}
private static function normalizeNumber(string $value): int|float|string
{
if (! is_numeric($value)) {
return $value;
}
if (str_contains($value, '.')) {
$f = (float) $value;
return $f == (int) $f ? (int) $f : $f;
}
// large integers stay as string if beyond PHP int
if (strlen(ltrim($value, '+-')) > 18) {
return $value;
}
return (int) $value;
}
public static function nativeSymbolForChain(string $chainType): string
{
return match (strtoupper($chainType)) {
'TRON', 'TRX' => 'TRX',
'BITCOIN', 'BTC' => 'BTC',
'ETHEREUM', 'ETH', 'EVM' => 'ETH',
'SOLANA', 'SOL' => 'SOL',
'TON' => 'TON',
'BNB', 'BSC', 'BINANCE' => 'BNB',
default => strtoupper($chainType) ?: 'UNKNOWN',
};
}
}