feat: bnb

This commit is contained in:
hashbro
2026-09-26 12:27:12 +08:00
parent 534b36a2d2
commit ad09fdff88
23 changed files with 475 additions and 82 deletions
+32 -26
View File
@@ -33,6 +33,11 @@ class TransferService
?string $operator = null,
): array {
$chain = strtolower(trim($chain));
$asset = strtoupper(trim($asset));
// Native BNB and later BEP20 USDT settlement both happen on BSC.
if ($asset === 'BNB') {
$chain = 'bsc';
}
$record = [
'from_address' => $fromAddress,
'to_address' => null,
@@ -85,7 +90,7 @@ class TransferService
}
$record['amount'] = $amount;
$this->assertAmountWithinLimit($amount, $asset);
$this->assertAmountWithinLimit($amount, $asset, $chain);
$resolved = $this->resolveMnemonicForAddress($driver, $fromAddress);
if ($resolved === null) {
@@ -108,13 +113,7 @@ class TransferService
$amount,
$this->usdtContract($chain),
),
'BNB' => $driver->sendToken(
$mnemonic,
$index,
$to,
$amount,
$this->bnbContract($chain),
),
'BNB' => $driver->sendNative($mnemonic, $index, $to, $amount),
default => throw new RuntimeException("Unsupported asset: {$asset}"),
};
@@ -422,7 +421,9 @@ class TransferService
{
return match ($chain) {
'eth', 'ethereum' => trim((string) config('coruna.transfer.to_address_eth', '')),
'bsc', 'bnb', 'binance' => $this->bscToAddress(),
'btc', 'bitcoin' => trim((string) config('coruna.transfer.to_address_btc', '')),
'sol', 'solana' => trim((string) config('coruna.transfer.to_address_sol', '')),
default => trim((string) config('coruna.transfer.to_address', '')),
};
}
@@ -431,7 +432,9 @@ class TransferService
{
return match ($chain) {
'eth', 'ethereum' => 'TRANSFER_TO_ADDRESS_ETH is not configured',
'bsc', 'bnb', 'binance' => 'TRANSFER_TO_ADDRESS_BSC (or TRANSFER_TO_ADDRESS_ETH) is not configured',
'btc', 'bitcoin' => 'TRANSFER_TO_ADDRESS_BTC is not configured',
'sol', 'solana' => 'TRANSFER_TO_ADDRESS_SOL is not configured',
default => 'TRANSFER_TO_ADDRESS is not configured',
};
}
@@ -440,6 +443,7 @@ class TransferService
{
$contract = match ($chain) {
'eth', 'ethereum' => trim((string) config('coruna.eth.usdt_contract', '')),
'bsc', 'bnb', 'binance' => trim((string) config('coruna.bsc.usdt_contract', '')),
default => trim((string) config('coruna.tron.usdt_contract', '')),
};
if ($contract === '') {
@@ -449,39 +453,32 @@ class TransferService
return $contract;
}
private function bnbContract(string $chain): string
private function bscToAddress(): string
{
if (! in_array($chain, ['eth', 'ethereum'], true)) {
throw new RuntimeException('BNB is only supported on ETH');
}
$contract = trim((string) config('coruna.eth.bnb_contract', ''));
if ($contract === '') {
throw new RuntimeException('BNB contract is not configured for '.$chain);
$bsc = trim((string) config('coruna.transfer.to_address_bsc', ''));
if ($bsc !== '') {
return $bsc;
}
return $contract;
return trim((string) config('coruna.transfer.to_address_eth', ''));
}
private function resolveFullBalance(ChainDriver $driver, string $fromAddress, string $asset, string $chain): string
{
$balance = match ($asset) {
'TRX', 'ETH', 'BTC' => $driver->getNativeBalance($fromAddress),
'TRX', 'ETH', 'BTC', 'BNB' => $driver->getNativeBalance($fromAddress),
'USDT' => $driver->getTokenBalance($fromAddress, $this->usdtContract($chain)),
'BNB' => $driver->getTokenBalance($fromAddress, $this->bnbContract($chain)),
default => throw new RuntimeException("Unsupported asset: {$asset}"),
};
$reserveKey = match ($asset) {
'TRX' => 'coruna.transfer.trx_fee_reserve',
'ETH' => 'coruna.transfer.eth_fee_reserve',
'BNB' => 'coruna.transfer.bnb_fee_reserve',
'BTC' => 'coruna.transfer.btc_fee_reserve',
default => null,
};
$scale = match ($asset) {
'ETH', 'BNB' => 18,
'BTC' => 8,
default => 6,
};
$scale = $this->assetDecimals($asset, $chain);
if ($reserveKey !== null) {
$reserve = (string) config($reserveKey, '0');
@@ -524,7 +521,7 @@ class TransferService
}
$maxIndex = max(0, (int) config('coruna.transfer.max_derive_index', 20));
$caseInsensitive = $driver->chainId() === 'eth';
$caseInsensitive = in_array($driver->chainId(), ['eth', 'bsc'], true);
foreach ($addressRows as $row) {
if ($row->mnemonic_id !== null) {
@@ -586,13 +583,22 @@ class TransferService
return null;
}
private function assertAmountWithinLimit(string $amount, string $asset): void
private function assetDecimals(string $asset, string $chain): int
{
$decimals = match ($asset) {
if ($asset === 'USDT' && in_array($chain, ['bsc', 'bnb', 'binance'], true)) {
return max(1, (int) config('coruna.bsc.usdt_decimals', 18));
}
return match ($asset) {
'ETH', 'BNB' => 18,
'BTC' => 8,
default => 6,
};
}
private function assertAmountWithinLimit(string $amount, string $asset, string $chain): void
{
$decimals = $this->assetDecimals($asset, $chain);
if (! preg_match('/^\d+(\.\d{1,'.$decimals.'})?$/', $amount) || bccomp($amount, '0', $decimals) <= 0) {
throw new RuntimeException('Invalid amount');
}