Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adds bl3ps new fee_priority setting for withdrawals #247

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.dist
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ BL3P_PUBLIC_KEY=
# This is the private part of your API connection to BL3P. It’s an encoded secret granting access to your BL3P account.
BL3P_PRIVATE_KEY=

# BL3P allows for three different settings, low (680sats, medium (5000sats) or high (10000sats).
BL3P_FEE_PRIORITY=low

##################################################################################
# Bitvavo exchange settings
# > no trading fees up to the first € 1000,- if you use my affiliate link: https://bitvavo.com/?a=DE4151B112
Expand Down
2 changes: 2 additions & 0 deletions config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ parameters:
env(BL3P_PRIVATE_KEY): ''
env(BL3P_WITHDRAW_ADDRESS): ''
env(BL3P_WITHDRAW_XPUB): ~
env(BL3P_FEE_PRIORITY): 'low'

# bitvavo settings
env(BITVAVO_API_URL): 'https://api.bitvavo.com/v2/'
Expand Down Expand Up @@ -410,6 +411,7 @@ services:
arguments:
- '@api.client.bl3p'
- '@logger'
- '%env(BL3P_FEE_PRIORITY)%'
tags:
- { name: exchange-withdraw-service }

Expand Down
7 changes: 7 additions & 0 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ This is the private part of your API connection to BL3P. It's an encoded secret

**Example**: ``BL3P_PRIVATE_KEY=aHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1kUXc0dzlXZ1hjUQ==``

BL3P_FEE_PRIORITY
"""""""""""""""""
This will send the withdrawal as low, medium or high priority. The default is low. Low priority is the cheapest but
will take the longest to confirm. High priority is the most expensive but will be confirmed the fastest.

**Example**: ``BL3P_FEE_PRIORITY=medium``

BL3P_API_URL (optional)
"""""""""""""""""""""""
The endpoint where the tool should connect to.
Expand Down
27 changes: 24 additions & 3 deletions src/Service/Bl3p/Bl3pWithdrawService.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,16 @@
class Bl3pWithdrawService implements WithdrawServiceInterface
{
final public const BL3P = 'bl3p';
final public const DEFAULT_FEE_PRIORITY = 'low';
final public const FEE_COST_LOW = 680;
final public const FEE_COST_MEDIUM = 5000;
final public const FEE_COST_HIGH = 10000;

public function __construct(protected Bl3pClientInterface $bl3pClient, protected LoggerInterface $logger)
{
public function __construct(
protected Bl3pClientInterface $bl3pClient,
protected LoggerInterface $logger,
protected string $configuredFeePriority = self::DEFAULT_FEE_PRIORITY
) {
}

public function withdraw(int $balanceToWithdraw, string $addressToWithdrawTo): CompletedWithdraw
Expand All @@ -33,6 +40,7 @@ public function withdraw(int $balanceToWithdraw, string $addressToWithdrawTo): C
'currency' => 'BTC',
'address' => $addressToWithdrawTo,
'amount_int' => $netAmountToWithdraw,
'fee_priority' => $this->getFeePriority(),
]);

return new CompletedWithdraw($addressToWithdrawTo, $netAmountToWithdraw, $response['data']['id']);
Expand All @@ -47,11 +55,24 @@ public function getAvailableBalance(): int

public function getWithdrawFeeInSatoshis(): int
{
return 5000;
return match ($this->getFeePriority()) {
'low' => self::FEE_COST_LOW,
'medium' => self::FEE_COST_MEDIUM,
'high' => self::FEE_COST_HIGH,
};
}

public function supportsExchange(string $exchange): bool
{
return self::BL3P === $exchange;
}

protected function getFeePriority(): string
{
return match ($this->configuredFeePriority) {
'medium' => 'medium',
'high' => 'high',
default => 'low',
};
}
}
50 changes: 38 additions & 12 deletions tests/Service/Bl3p/Bl3pWithdrawServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

use Jorijn\Bitcoin\Dca\Client\Bl3pClientInterface;
use Jorijn\Bitcoin\Dca\Service\Bl3p\Bl3pWithdrawService;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;

Expand All @@ -31,9 +32,8 @@ final class Bl3pWithdrawServiceTest extends TestCase
public const API_CALL = 'apiCall';
public const GENMKT_MONEY_INFO = 'GENMKT/money/info';

private \Jorijn\Bitcoin\Dca\Client\Bl3pClientInterface|\PHPUnit\Framework\MockObject\MockObject $client;

private \Psr\Log\LoggerInterface|\PHPUnit\Framework\MockObject\MockObject $logger;
private Bl3pClientInterface|MockObject $client;
private LoggerInterface|MockObject $logger;
private Bl3pWithdrawService $service;

protected function setUp(): void
Expand All @@ -45,7 +45,7 @@ protected function setUp(): void

$this->service = new Bl3pWithdrawService(
$this->client,
$this->logger
$this->logger,
);
}

Expand All @@ -69,30 +69,45 @@ public function testGetBalance(): void
}

/**
* @covers ::getWithdrawFeeInSatoshis
* @covers ::withdraw
*
* @dataProvider providerOfDifferentFeePriorities
*
* @throws \Exception
*/
public function testWithdraw(): void
public function testWithdraw(string $feePriority, int $expectedFeeValue): void
{
$this->service = new Bl3pWithdrawService(
$this->client,
$this->logger,
$feePriority,
);

$address = self::ADDRESS.random_int(1000, 2000);
$amount = random_int(100000, 300000);
$netAmount = $amount - $this->service->getWithdrawFeeInSatoshis();
$withdrawID = 'id'.random_int(1000, 2000);
$apiResponse = ['data' => ['id' => $withdrawID]];

$withdrawFeeInSatoshis = $this->service->getWithdrawFeeInSatoshis();
$netAmount = $amount - $withdrawFeeInSatoshis;

static::assertSame($expectedFeeValue, $withdrawFeeInSatoshis);

$this->client
->expects(static::once())
->method(self::API_CALL)
->with(
'GENMKT/money/withdraw',
static::callback(function (array $parameters) use ($netAmount, $address): bool {
static::callback(function (array $parameters) use ($feePriority, $netAmount, $address): bool {
self::assertArrayHasKey('currency', $parameters);
self::assertSame('BTC', $parameters['currency']);
self::assertArrayHasKey(self::ADDRESS, $parameters);
self::assertSame($address, $parameters[self::ADDRESS]);
self::assertArrayHasKey('amount_int', $parameters);
self::assertSame($netAmount, $parameters['amount_int']);
self::assertArrayHasKey('fee_priority', $parameters);
self::assertSame($this->getExpectedFeePriorityFor($feePriority), $parameters['fee_priority']);

return true;
})
Expand All @@ -115,12 +130,23 @@ public function testSupportsExchange(): void
static::assertFalse($this->service->supportsExchange('bl4p'));
}

/**
* @covers ::getWithdrawFeeInSatoshis
*/
public function testFeeCalculation(): void
protected function providerOfDifferentFeePriorities(): array
{
return [
'low' => ['low', Bl3pWithdrawService::FEE_COST_LOW],
'medium' => ['medium', Bl3pWithdrawService::FEE_COST_MEDIUM],
'high' => ['high', Bl3pWithdrawService::FEE_COST_HIGH],
'configuration_typo' => ['highhh', Bl3pWithdrawService::FEE_COST_LOW],
];
}

protected function getExpectedFeePriorityFor(string $feePriority): string
{
static::assertSame(5000, $this->service->getWithdrawFeeInSatoshis());
return match ($feePriority) {
'medium' => 'medium',
'high' => 'high',
default => 'low',
};
}

private function createBalanceStructure(int $balance): array
Expand Down