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

Framework agnostic approach #38

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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
1 change: 0 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
"php": "^8.2",
"ext-gmp": "*",
"ext-bcmath": "*",
"laravel/framework": ">=v7.0.0|>=v10.0.0",
"spomky-labs/cbor-php": "^3.0"
},
"scripts": {
Expand Down
17 changes: 0 additions & 17 deletions src/AddressValidationServiceProvider.php

This file was deleted.

4 changes: 3 additions & 1 deletion src/Contracts/Validator.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace Merkeleon\PhpCryptocurrencyAddressValidation\Contracts;

use Merkeleon\PhpCryptocurrencyAddressValidation\Exception\AddressValidationException;

interface Validator
{
public function isValid(?string $address): bool;
Expand All @@ -16,4 +18,4 @@ public function isValid(?string $address): bool;
* @throws AddressValidationException
*/
public function validate(?string $address): void;
}
}
19 changes: 3 additions & 16 deletions src/DriverConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,18 @@
use Merkeleon\PhpCryptocurrencyAddressValidation\Drivers\AbstractDriver;
use function class_exists;

/**
* @template T
*/
readonly class DriverConfig
final readonly class DriverConfig
{
/**
* @param class-string<T> $driver
* @param class-string<AbstractDriver> $driver
* @param array $mainnet
* @param array $testnet
*/
public function __construct(
private string $driver,
private array $mainnet = [],
private array $testnet = []
)
{
) {
}

public function makeDriver(bool $isMainNet): ?AbstractDriver
Expand All @@ -45,13 +41,4 @@ private function getDriverOptions(bool $isMainNet): array
?: $this->mainnet
?: [];
}

public static function __set_state(array $state): DriverConfig
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems like there is not usage for this magic method in a code anymore. feel free to correct me if I missed something

{
return new self(
$state['driver'],
$state['mainnet'],
$state['testnet']
);
}
}
13 changes: 9 additions & 4 deletions src/Drivers/Base32Driver.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

namespace Merkeleon\PhpCryptocurrencyAddressValidation\Drivers;

use Illuminate\Support\Str;
use Merkeleon\PhpCryptocurrencyAddressValidation\Utils\Base32Decoder;
use RuntimeException;
use Throwable;
Expand All @@ -16,7 +15,6 @@
use function pack;
use function preg_match;
use function sprintf;
use function str_contains;
use function strtolower;

class Base32Driver extends AbstractDriver
Expand Down Expand Up @@ -45,7 +43,14 @@ public function match(string $address): bool
public function check(string $address, array $networks = []): bool
{
try {
$hasPrefix = Str::contains($address, array_keys($this->options));
$hasPrefix = false;

foreach (array_keys($this->options) as $key) {
if (str_contains($address, (string) $key)) {
$hasPrefix = true;
break;
}
}

$address = strtolower($address);

Expand Down Expand Up @@ -111,4 +116,4 @@ protected function decodeVersion(int $version): array

return [$scriptType, $hashLength];
}
}
}
4 changes: 1 addition & 3 deletions src/Drivers/CardanoDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@
use Merkeleon\PhpCryptocurrencyAddressValidation\Utils\Bech32Decoder;
use function array_keys;
use function implode;
use function in_array;
use function preg_match;

class CardanoDriver extends AbstractDriver
{

public function match(string $address): bool
{
$prefix = implode('|', array_keys($this->options));
Expand All @@ -32,4 +30,4 @@ public function check(string $address): bool
return false;
}
}
}
}
11 changes: 8 additions & 3 deletions src/Drivers/CborDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
use CBOR\Tag\GenericTag;
use CBOR\Tag\TagManager;
use CBOR\Tag\UnsignedBigIntegerTag;
use Illuminate\Support\Str;
use Merkeleon\PhpCryptocurrencyAddressValidation\Utils\Base58Decoder;
use Throwable;
use function array_keys;
Expand All @@ -38,7 +37,13 @@ public function __construct(array $options)

public function match(string $address): bool
{
return Str::startsWith($address, array_keys($this->options));
foreach (array_keys($this->options) as $key) {
if (str_starts_with($address, (string) $key)) {
return true;
}
}

return false;
}

public function check(string $address): bool
Expand Down Expand Up @@ -86,4 +91,4 @@ public function check(string $address): bool
return false;
}
}
}
}
5 changes: 1 addition & 4 deletions src/Drivers/KeccakStrictDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,12 @@

namespace Merkeleon\PhpCryptocurrencyAddressValidation\Drivers;

use InvalidArgumentException;
use Merkeleon\PhpCryptocurrencyAddressValidation\Utils\KeccakDecoder;
use function intval;
use function is_string;
use function preg_match;
use function str_replace;
use function str_split;
use function str_starts_with;
use function strpos;
use function strtolower;
use function strtoupper;
use function substr;
Expand Down Expand Up @@ -56,4 +53,4 @@ public function isZeroPrefixed(string $value): bool
{
return str_starts_with(haystack: $value, needle: '0x');
}
}
}
6 changes: 4 additions & 2 deletions src/Exception/AddressValidationException.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@

namespace Merkeleon\PhpCryptocurrencyAddressValidation\Exception;

class AddressValidationException extends \RuntimeException
use RuntimeException;

class AddressValidationException extends RuntimeException
{
public function __construct(string $chain, string $notValidAddress, bool $matchedPattern)
{
$text = "Incorrect {$chain} address [{$notValidAddress}]";
$text .= $matchedPattern ? ": address have wrong encoding" : ": address does not matched pattern";
parent::__construct($text);
}
}
}
6 changes: 4 additions & 2 deletions src/Exception/Bech32Exception.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

namespace Merkeleon\PhpCryptocurrencyAddressValidation\Exception;

class Bech32Exception extends \Exception
use Exception;

class Bech32Exception extends Exception
{

}
}
4 changes: 1 addition & 3 deletions src/Utils/Base32Decoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,10 @@
use function gmp_init;
use function gmp_mul;
use function gmp_pow;
use function gmp_strval;
use function gmp_xor;
use function ord;
use function pack;
use function strlen;
use function strtolower;
use function unpack;

class Base32Decoder
Expand Down Expand Up @@ -304,4 +302,4 @@ protected static function convertBits(array $data, $inLen, $fromBits, $toBits, $

return $ret;
}
}
}
40 changes: 29 additions & 11 deletions src/Validator.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,25 @@
use Merkeleon\PhpCryptocurrencyAddressValidation\Contracts\Driver;
use Merkeleon\PhpCryptocurrencyAddressValidation\Enums\CurrencyEnum;
use Merkeleon\PhpCryptocurrencyAddressValidation\Exception\AddressValidationException;
use function app;
use function config;

readonly class Validator implements Contracts\Validator
class Validator implements Contracts\Validator
{
private const CONFIG_PATH = __DIR__ . '/../config/address_validation.php';

/**
* @param DriverConfig[] $options
*/
public function __construct(
private string $chain,
private array $options,
private bool $isMainnet = true
private readonly string $chain,
private readonly bool $isMainnet = true,
private ?array $options = null,
) {
$this->options = $options ?? $this->resolveConfigForCurrency(CurrencyEnum::from($this->chain));
}

public static function make(CurrencyEnum $currency): Validator
public static function make(CurrencyEnum $currency, bool $isMainnet = true, ?array $config = null): self
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since this factory method copies current construct method I was thinking if it would make sense to remove it. But I left it here for backwards compatibility reasons. Maybe we can docblock it with @deprecated to encourage developers to use constructor instead?

{
return new Validator($currency->value, config("address_validation.{$currency->value}"), app()->isProduction());
return new self($currency->value, $isMainnet, $config);
}

public function isValid(?string $address): bool
Expand All @@ -32,6 +36,7 @@ public function isValid(?string $address): bool
}

$drivers = $this->getDrivers();

// if there is no drivers we force address to be valid
if (null === $drivers || !$drivers->valid()) {
return true;
Expand All @@ -47,6 +52,7 @@ public function validate(?string $address): void
}

$drivers = $this->getDrivers();

// if there is no drivers we force address to be valid
if (null === $drivers || !$drivers->valid()) {
return;
Expand All @@ -68,7 +74,6 @@ public function validate(?string $address): void
*/
protected function getDrivers(): ?Generator
{
/** @var DriverConfig $driverConfig */
foreach ($this->options as $driverConfig) {
if ($driver = $driverConfig->makeDriver($this->isMainnet)) {
yield $driver;
Expand All @@ -78,9 +83,11 @@ protected function getDrivers(): ?Generator
return null;
}

/**
* @param Driver[] $drivers
*/
protected function getDriver(iterable $drivers, string $address): ?Driver
{
/** @var Driver $driver */
foreach ($drivers as $driver) {
if ($driver->match($address)) {
return $driver;
Expand All @@ -89,4 +96,15 @@ protected function getDriver(iterable $drivers, string $address): ?Driver

return null;
}
}

/**
* @return DriverConfig[]
*/
protected function resolveConfigForCurrency(CurrencyEnum $currency): array
{
/** @var array<string, DriverConfig[]> $config */
$config = require self::CONFIG_PATH;

return $config[$currency->value];
}
}
4 changes: 2 additions & 2 deletions tests/KeccakDriverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public function test_keccak_driver(string $net, bool $expected, string $address)
{
$config = [new DriverConfig(KeccakDriver::class)];

$validator = new Validator(CurrencyEnum::ETHEREUM->value, $config, $net === 'mainnet');
$validator = new Validator(CurrencyEnum::ETHEREUM->value, $net === 'mainnet', $config);

self::assertEquals($expected, $validator->isValid($address));
}
Expand All @@ -42,4 +42,4 @@ public function addressesProvider(): array
'Ethereum #4' => ['testnet', true, '0x799ad3ff7ef43dfd1473f9b8a8c4237c22d8113f'],
];
}
}
}
8 changes: 2 additions & 6 deletions tests/ValidatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,7 @@ class ValidatorTest extends TestCase
*/
public function test_currency_validator(CurrencyEnum $currency, string $net, bool $expected, string $address): void
{
$config = require __DIR__ . '/../config/address_validation.php';

$options = $config[$currency->value];

$validator = new Validator($currency->value, $options, $net === 'mainnet');
$validator = new Validator($currency->value, $net === 'mainnet');

$this->assertEquals(
$expected,
Expand Down Expand Up @@ -120,4 +116,4 @@ public function currencyAddressProvider(): array
'EOS #2' => [CurrencyEnum::EOS, 'mainnet', true, 'bitfinexeos1'],
];
}
}
}