AdequateCryptoAddress
Simple wallet address validator and normalizer for cryptocurrencies addresses in Ruby.
Inspired by ognus/wallet-address-validator.
Installation
Add this line to your application's Gemfile:
gem 'adequate_crypto_address'
Or install it yourself as:
gem install adequate_crypto_address
Upgrading from 0.1.x to 0.2.0
0.2.0 hardens the validators against malformed input and makes the
address_type API consistent. Most callers of .valid? need no changes, but a
few behaviors changed. See the CHANGELOG for the full list.
Stricter validation (addresses that used to pass may now fail)
Some malformed addresses that earlier versions incorrectly accepted are now rejected. If your app stored or allow-listed such values, re-validate them:
- BCH legacy addresses with a bad checksum/length, CashAddr addresses with a
non-
bitcoincash/bchtest/bchregprefix (e.g.evil:...), or a payload shorter than the version byte declares. - TON any 48-character string (e.g.
"A" * 48) — a valid tag, workchain, and CRC16 checksum are now required. - Monero anything that is not a checksum-valid Base58 address (e.g.
"4" + "!" * 94).
If you rely on a currency name, note that unknown names still raise
AdequateCryptoAddress::UnknownCurrency, but a nil or malformed address
now consistently returns false from .valid? instead of raising.
address_type is now public and consistent
AdequateCryptoAddress.address_type(address, currency) is a supported public
method for every currency. It returns the detected type as a Symbol when the
address is valid, or nil when it is not.
Some returned type symbols changed. Update any code that compared against the old values:
| Currency | 0.1.x | 0.2.0 |
|---|---|---|
| TON | :TON |
:ton_mainnet / :ton_testnet |
| Monero | :monero |
:standard / :integrated / :subaddress |
| ETH | nil |
:eth |
| SOL | nil |
:solana |
| XLM | nil |
:account / :muxed |
# 0.1.x
AdequateCryptoAddress::Ton.new(addr).send(:address_type) # private, => :TON
# 0.2.0
AdequateCryptoAddress.address_type(addr, :TON) # public, => :ton_mainnet
Type symbols passed to .valid? for these currencies changed accordingly, e.g.
valid?(addr, :TON, :ton_mainnet).
New network accessors
SegWit Bitcoin types are network-agnostic; call .network to enforce a network:
AdequateCryptoAddress.address('bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq', :btc).network #=> :mainnet
AdequateCryptoAddress.address('tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx', :btc).network #=> :testnet
Xmr#network reports :mainnet / :testnet / :stagenet.
Internal rename (only if you subclassed Bch)
The BCH internal helper address_type(address_code, address_type) was renamed
to type_mapping(address_code, address_type). The public address_type (no
arguments) now returns the detected type.
Main API
.valid? (address, currency [, type = nil])
Parameters
- address - Wallet address to validate.
- currency - Currency name string or symbol in any case,
:bitcoinor'BTC'or:btcor'BitCoin' - type - Optional. You can enforce validation with specific type. Not all currencies support types.
Returns true if the address (string) is a valid wallet address for the crypto currency specified, see below for supported currencies. An unknown currency raises
AdequateCryptoAddress::UnknownCurrency; anilor malformed address returnsfalse.
.address_type (address, currency)
Returns the detected address type as a
Symbolwhen the address is valid for the currency, ornilwhen it is not. The type vocabulary per currency is listed below.
AdequateCryptoAddress.address_type('bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq', :btc) #=> :segwit_v0_keyhash
AdequateCryptoAddress.address_type('not a real address', :btc) #=> nil
Supported crypto currencies
- Bitcoin/BTC,
'bitcoin'or'BTC'types::segwit_v0_keyhash :segwit_v0_scripthash :taproot :hash160 :p2sh :hash160test :p2shtest. SegWit types are network-agnostic; callAdequateCryptoAddress.address(addr, :btc).networkto get:mainnet/:testnet. - BitcoinCash/BCH,
'bitcoincash'or'BCH'types::p2sh :p2pkh :p2pkhtest :p2shtest - Cardano/ADA,
'cardano'or'ADA'types::prod :test - Dash,
'dash'or'DASH'types::prod :test - Dogecoin/DOGE,
'dogecoin'or'DOGE'types::prod :test - Zcash/ZEC,
'zcash'or'ZEC'types::prod :test - Ethereum/ETH,
'ethereum'or'ETH'type::eth - Litecoin/LTC,
'litecoin'or'LTC'types::prod :test - Ripple/XRP,
'ripple'or'XRP'type::common - Solana/SOL,
'solana'or'SOL'type::solana - Stellar/XLM,
'stellar'or'XLM'types::account :muxed - Toncoin/TON,
'TON'or'Toncoin'types::ton_mainnet :ton_testnet - Monero/XMR,
'monero'or'XMR'types::standard :integrated :subaddress(.networkreports:mainnet/:testnet/:stagenet) - TRON/TRX,
'tron'or'TRX'type::prod - Tezos/XTZ,
'tezos'or'XTZ'types::implicit :originated - Cosmos/ATOM,
'cosmos'or'ATOM'type::prod - BNB Smart Chain/BSC,
'bsc'or'binancesmartchain'type::bsc(Ethereum-format address)
Format coverage
This gem validates the mainstream, checksum-bearing payment address for each
supported chain. The following related formats are currently out of scope and
are rejected (they return false) rather than silently accepted. They may be
added in future releases:
- Bitcoin witness versions 2–16 and newer output forms (e.g. P2MR / BIP-360); only v0 (Bech32) and v1 Taproot (Bech32m) are recognized.
- Litecoin MWEB (
ltcmweb1...) addresses and future witness versions. - Zcash Sapling, Unified, Orchard, and TEX addresses; only transparent
t-addresses are validated. - XRP X-addresses (with embedded destination tags); only classic
r...addresses are validated. - Dash Platform Bech32m addresses; only Core Base58Check addresses are validated.
- Cardano Byron and stake/reward addresses; only Shelley payment address types 0–7 are validated. Stake addresses are intentionally excluded from a payment-address validator.
- Stellar contract (
C...) and other SEP-23 StrKey types; onlyG(account) andM(muxed) addresses are validated. - TON raw
workchain:account_idaddresses; only the Base64URL user-friendly form is validated.
Usage
Validation
require 'adequate_crypto_address'
# BTC
AdequateCryptoAddress.valid?('12QeMLzSrB8XH8FvEzPMVoRxVAzTr5XM2y', 'BTC') #=> true
AdequateCryptoAddress.valid?('3NJZLcZEEYBpxYEUGewU4knsQRn1WM5Fkt', :bitcoin, :p2sh) #=> true
# BCH
AdequateCryptoAddress.valid?('bitcoincash:qrtj3rd8524cndt2eew3s6wljqggmne00sgh4kfypk', :bch) #=> true
AdequateCryptoAddress.valid?('mmRH4e9WW4ekZUP5HvBScfUyaSUjfQRyvD', :BCH, :p2pkhtest) #=> true
# ETH
AdequateCryptoAddress.valid?('0xde709f2102306220921060314715629080e2fb77', :ETH) #=> true
AdequateCryptoAddress.valid?('de709f2102306220921060314715629080e2fb77', :ethereum) #=> true
Normalization
*Not all currencies support this feature.
require 'adequate_crypto_address'
# BCH
AdequateCryptoAddress.address('mmRH4e9WW4ekZUP5HvBScfUyaSUjfQRyvD', 'bch').cash_address #=> "bchtest:qpqtmmfpw79thzq5z7s0spcd87uhn6d34uqqem83hf"
AdequateCryptoAddress.address('bitcoincash:qrtj3rd8524cndt2eew3s6wljqggmne00sgh4kfypk', 'bch').legacy_address #=> "1LcerwTc1oPsMtByDCNUXFxReZpN1EXHoe"
address_string = 'qrtj3rd8524cndt2eew3s6wljqggmne00sgh4kfypk'
addr = AdequateCryptoAddress.address(address_string, 'bch')
addr.prefix #=> "bitcoincash"
addr.type #=> :p2pkh
addr.address #=> "bitcoincash:qrtj3rd8524cndt2eew3s6wljqggmne00sgh4kfypk"
# ETH
AdequateCryptoAddress.address('D1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb', 'eth').address #=> "0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb"
ActiveRecord validation example
class Model < ActiveRecord::Base
attribute :address, :string
attribute :dest_tag, :string
attribute :currency, :string
validate :validate_address_type
validate :validate_destination_tag
def validate_address_type
errors.add(:address, 'invalid address') unless AdequateCryptoAddress.valid?(address, currency)
end
# for Ripple
def validate_destination_tag
errors.add(:dest_tag, 'invalid destination tag') if dest_tag.present? && !(dest_tag =~ /\A\d{1,10}\z/)
end
end
Add your currency
# frozen_string_literal: true
# for Rails /config/initializers/adequate_crypto_address.rb
module AdequateCryptoAddress
class Coin
attr_reader :address
def initialize(address_sring)
@address = address_sring
end
def valid?(_type)
address.present?
end
end
end
AdequateCryptoAddress.valid?('addr', :coin) #=> true
Development
The repository pins Ruby with mise. Install the toolchain and dependencies, then run the complete local quality gate:
mise trust
mise install
mise run setup
mise run check
Individual tasks are available as mise run test, mise run lint, and mise run build. Use
mise run console for an interactive prompt.
Contributing
- Fork the repo
- Trust and install the toolchain:
mise trust && mise install - Grab dependencies:
mise run setup - Make your changes
- Make sure everything is working:
mise run check - Create a pull request
Notes
Bug reports and pull requests are welcome on GitHub at https://github.com/vtm9/adequate_crypto_address/issues