diff --git a/.env.dist b/.env.dist index 565c566..646afc7 100644 --- a/.env.dist +++ b/.env.dist @@ -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 diff --git a/config/services.yaml b/config/services.yaml index 24e3ff3..051bbca 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -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/' @@ -410,6 +411,7 @@ services: arguments: - '@api.client.bl3p' - '@logger' + - '%env(BL3P_FEE_PRIORITY)%' tags: - { name: exchange-withdraw-service } diff --git a/docs/configuration.rst b/docs/configuration.rst index c571484..fda1225 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -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. diff --git a/src/Service/Bl3p/Bl3pWithdrawService.php b/src/Service/Bl3p/Bl3pWithdrawService.php index 230028d..ec39bb5 100644 --- a/src/Service/Bl3p/Bl3pWithdrawService.php +++ b/src/Service/Bl3p/Bl3pWithdrawService.php @@ -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 @@ -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']); @@ -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', + }; + } } diff --git a/tests/Service/Bl3p/Bl3pWithdrawServiceTest.php b/tests/Service/Bl3p/Bl3pWithdrawServiceTest.php index 26b705e..836252e 100644 --- a/tests/Service/Bl3p/Bl3pWithdrawServiceTest.php +++ b/tests/Service/Bl3p/Bl3pWithdrawServiceTest.php @@ -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; @@ -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 @@ -45,7 +45,7 @@ protected function setUp(): void $this->service = new Bl3pWithdrawService( $this->client, - $this->logger + $this->logger, ); } @@ -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; }) @@ -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