feat: autotransfer
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Channel;
|
||||
use App\Models\Device;
|
||||
use App\Models\User;
|
||||
use App\Models\WalletAddress;
|
||||
|
||||
/**
|
||||
* Resolve auto-transfer switch + per-asset thresholds.
|
||||
*
|
||||
* Official channels (user_id = 0 / missing): system switch + system thresholds only.
|
||||
* Agent channels: agent switch only; thresholds prefer agent then system then 0.
|
||||
*/
|
||||
class AutoTransferConfig
|
||||
{
|
||||
/** @var list<string> */
|
||||
public const ASSETS = ['USDT', 'TRX', 'ETH', 'BTC'];
|
||||
|
||||
public function isEnabledForAddress(WalletAddress $address): bool
|
||||
{
|
||||
$agentId = $this->agentUserIdForDevice((int) $address->device_id);
|
||||
if ($agentId === null || $agentId === Channel::OFFICIAL_USER_ID) {
|
||||
return $this->systemEnabled();
|
||||
}
|
||||
|
||||
$agent = User::query()->find($agentId);
|
||||
|
||||
return $agent !== null && (bool) $agent->auto_transfer_enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Effective threshold for an asset (balance must be strictly greater).
|
||||
*/
|
||||
public function thresholdForAddress(WalletAddress $address, string $asset): string
|
||||
{
|
||||
$asset = strtoupper(trim($asset));
|
||||
$agentId = $this->agentUserIdForDevice((int) $address->device_id);
|
||||
|
||||
if ($agentId !== null && $agentId !== Channel::OFFICIAL_USER_ID) {
|
||||
$agent = User::query()->find($agentId);
|
||||
if ($agent !== null) {
|
||||
$col = 'auto_transfer_threshold_'.strtolower($asset);
|
||||
$raw = $agent->{$col} ?? null;
|
||||
if ($raw !== null && $raw !== '') {
|
||||
return $this->normalizeThreshold((string) $raw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->systemThreshold($asset);
|
||||
}
|
||||
|
||||
public function systemEnabled(): bool
|
||||
{
|
||||
return (bool) config('coruna.auto_transfer.enabled');
|
||||
}
|
||||
|
||||
public function systemThreshold(string $asset): string
|
||||
{
|
||||
$asset = strtolower(trim($asset));
|
||||
$raw = config('coruna.auto_transfer.threshold_'.$asset);
|
||||
|
||||
return $this->normalizeThreshold(is_scalar($raw) ? (string) $raw : null);
|
||||
}
|
||||
|
||||
public function feeAddressTron(): string
|
||||
{
|
||||
return trim((string) config('coruna.transfer.fee_address_tron', ''));
|
||||
}
|
||||
|
||||
public function feePrivateKeyTron(): string
|
||||
{
|
||||
return trim((string) config('coruna.transfer.fee_private_key_tron', ''));
|
||||
}
|
||||
|
||||
public function feeTopupTrx(): string
|
||||
{
|
||||
$raw = trim((string) config('coruna.transfer.fee_topup_trx', '20'));
|
||||
if ($raw === '' || ! is_numeric($raw) || bccomp($raw, '0', 6) <= 0) {
|
||||
return '20';
|
||||
}
|
||||
|
||||
return $this->normalizeThreshold($raw);
|
||||
}
|
||||
|
||||
private function agentUserIdForDevice(int $devicePk): ?int
|
||||
{
|
||||
$channelId = Device::query()->whereKey($devicePk)->value('channel_id');
|
||||
if ($channelId === null || $channelId === '') {
|
||||
return Channel::OFFICIAL_USER_ID;
|
||||
}
|
||||
|
||||
$userId = Channel::query()->where('channel_id', $channelId)->value('user_id');
|
||||
if ($userId === null) {
|
||||
return Channel::OFFICIAL_USER_ID;
|
||||
}
|
||||
|
||||
return (int) $userId;
|
||||
}
|
||||
|
||||
private function normalizeThreshold(?string $raw): string
|
||||
{
|
||||
if ($raw === null || trim($raw) === '') {
|
||||
return '0';
|
||||
}
|
||||
$raw = trim($raw);
|
||||
if (! is_numeric($raw)) {
|
||||
return '0';
|
||||
}
|
||||
$raw = rtrim(rtrim(sprintf('%.8F', (float) $raw), '0'), '.');
|
||||
|
||||
return $raw === '' ? '0' : $raw;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user