Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to validate Ethereum addresses in PHP

I'm using PHP and curl with json to interact with my geth server.

I'm able to do all I want except one thing: checking if user's inputted address is valid according to ethereum wallet format.

I saw a javascript function here, but I'm mostly using PHP, I'm not into JS at all.

Any ideas how to validate ethereum addresses in PHP?

like image 832
btc4cash Avatar asked Jul 08 '17 20:07

btc4cash


2 Answers

Here's a PHP implementation for Ethereum address validation against the EIP 55 specification. For details of how it works, please go through the comments.

<?php

use kornrunner\Keccak; // composer require greensea/keccak

class EthereumValidator
{
    public function isAddress(string $address): bool
    {
        // See: https://github.com/ethereum/web3.js/blob/7935e5f/lib/utils/utils.js#L415
        if ($this->matchesPattern($address)) {
            return $this->isAllSameCaps($address) ?: $this->isValidChecksum($address);
        }

        return false;
    }

    protected function matchesPattern(string $address): int
    {
        return preg_match('/^(0x)?[0-9a-f]{40}$/i', $address);
    }

    protected function isAllSameCaps(string $address): bool
    {
        return preg_match('/^(0x)?[0-9a-f]{40}$/', $address) || preg_match('/^(0x)?[0-9A-F]{40}$/', $address);
    }

    protected function isValidChecksum($address)
    {
        $address = str_replace('0x', '', $address);
        $hash = Keccak::hash(strtolower($address), 256);

        // See: https://github.com/web3j/web3j/pull/134/files#diff-db8702981afff54d3de6a913f13b7be4R42
        for ($i = 0; $i < 40; $i++ ) {
            if (ctype_alpha($address{$i})) {
                // Each uppercase letter should correlate with a first bit of 1 in the hash char with the same index,
                // and each lowercase letter with a 0 bit.
                $charInt = intval($hash{$i}, 16);

                if ((ctype_upper($address{$i}) && $charInt <= 7) || (ctype_lower($address{$i}) && $charInt > 7)) {
                    return false;
                }
            }
        }

        return true;
    }
}

Dependencies

To validate checksum addresses, we need a keccak-256 implementation in place which is not supported by the built-in hash() function. You need to require the greensea/keccak composer package as a dependency.


Kudos to @WebSpanner for pointing out the issue with SHA3 hashing.

like image 100
sepehr Avatar answered Nov 20 '22 11:11

sepehr


Basically, you can convert the javascript entirely to PHP. Here i have been able to convert and test the code for validating an ethereum address in PHP.

/**
 * Checks if the given string is an address
 *
 * @method isAddress
 * @param {String} $address the given HEX adress
 * @return {Boolean}
*/
function isAddress($address) {
    if (!preg_match('/^(0x)?[0-9a-f]{40}$/i',$address)) {
        // check if it has the basic requirements of an address
        return false;
    } elseif (!preg_match('/^(0x)?[0-9a-f]{40}$/',$address) || preg_match('/^(0x)?[0-9A-F]{40}$/',$address)) {
        // If it's all small caps or all all caps, return true
        return true;
    } else {
        // Otherwise check each case
        return isChecksumAddress($address);
    }
}

/**
 * Checks if the given string is a checksummed address
 *
 * @method isChecksumAddress
 * @param {String} $address the given HEX adress
 * @return {Boolean}
*/
function isChecksumAddress($address) {
    // Check each case
    $address = str_replace('0x','',$address);
    $addressHash = hash('sha3',strtolower($address));
    $addressArray=str_split($address);
    $addressHashArray=str_split($addressHash);

    for($i = 0; $i < 40; $i++ ) {
        // the nth letter should be uppercase if the nth digit of casemap is 1
        if ((intval($addressHashArray[$i], 16) > 7 && strtoupper($addressArray[$i]) !== $addressArray[$i]) || (intval($addressHashArray[$i], 16) <= 7 && strtolower($addressArray[$i]) !== $addressArray[$i])) {
            return false;
        }
    }
    return true;
}

Meanwhile, for someone looking for a very simple regular expression for checking ethereum address validity (e.g to use is as a pattern attribute of an HTML field), this regular expression may suffice.

^(0x)?[0-9a-fA-F]{40}$
like image 28
tormuto Avatar answered Nov 20 '22 12:11

tormuto