adequate_crypto_address 0.1.11 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6fee1d177fb87fdb5e97bb27ac01606349f567b99bd1d7f1a2553c8d6a17e1de
4
- data.tar.gz: 7968e28889bc532c2705322a5d15ddeaf386c82f13fc41114bca6df0d375c49f
3
+ metadata.gz: b1b6ee057501d6aee79d635204e6171784b6628e48663a74b0961ca837ebcf3c
4
+ data.tar.gz: c85732ee34eee9d27ac63ee4fe4a22079278011acda4fe1aeadfa215b8d5f85f
5
5
  SHA512:
6
- metadata.gz: 62df3006c3a130904b82d42cad6b94259fc6461dbdcbae0f0e0227716fccf0200aa6d6b698c7ae76ffde13fc85b23df993f20ddd2444f8a6e12648320c051f2f
7
- data.tar.gz: bd5e6981b62cd15be7ccac5c9604865fa272d9bec754f2ba8ef4ed0dff9b6c3d4f6d1c31648b14c4cb7b64e244869a3b8808c6bf851b72a84c2cc2362ccd20bf
6
+ metadata.gz: 535343adea99012947178355228daf705b9f01bd97b550ee8e01e0d3e71077df9f3b1b7aab26f249b8972ea1c4ccf82b276ef1c5ca054bdd398126f4aad8f5de
7
+ data.tar.gz: 4602af0c018876b2d3ad041253fd97de6c8d3c7709097ca68ddf5a95c09d983cd1623d6c38aa272fb07fd7503cf04ed5f8b4bfa99fecde28e55bd7b19e3c8781
data/CHANGELOG.md ADDED
@@ -0,0 +1,70 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.2.0] - 2026-09-02
9
+
10
+ This release hardens the validators against malformed input found in the
11
+ September 2026 security review. Several addresses that previous versions
12
+ incorrectly accepted are now rejected, and the public `address_type` API is
13
+ consistent across every currency. See the "Upgrading from 0.1.x to 0.2.0"
14
+ section of the README for migration guidance.
15
+
16
+ ### Security
17
+
18
+ - **BCH legacy Base58Check** now verifies the four-byte double-SHA256 checksum
19
+ and requires the exact 25-byte decoded length. One-character checksum
20
+ mutations are rejected.
21
+ - **BCH CashAddr** now accepts only the `bitcoincash`, `bchtest`, and `bchreg`
22
+ prefixes (an `evil:` prefix with a valid checksum is rejected) and validates
23
+ the reserved bit, type bits, declared hash size, and decoded payload length
24
+ (a shortened payload with a recomputed checksum is rejected).
25
+ - **TON** addresses are fully decoded: Base64URL payload, tag, workchain,
26
+ 32-byte account ID, CRC16-CCITT checksum, and testnet flag. Arbitrary
27
+ 48-character strings such as `"A" * 48` are no longer accepted.
28
+ - **Monero** is validated by decoding the Monero Base58 payload and checking the
29
+ network byte, structural length, and Keccak-256 checksum for standard,
30
+ integrated, and subaddress forms. Non-Base58 input such as `"4" + "!" * 94`
31
+ is rejected.
32
+ - **Bounded input**: all validators enforce a practical length cap before
33
+ Base58/Bech32 decoding, preventing pathological slowdowns on oversized input.
34
+ - Currency resolution now uses an explicit allowlist, so a `NoMethodError` from
35
+ malformed input can no longer be misreported as `UnknownCurrency`.
36
+
37
+ ### Added
38
+
39
+ - Public `AdequateCryptoAddress.address_type(address, currency)` with a
40
+ consistent contract: it returns the detected type as a `Symbol` when the
41
+ address is valid, or `nil` when it is not, for every supported currency.
42
+ - `Btc#network` and `Xmr#network` expose `:mainnet`/`:testnet`
43
+ (`:stagenet` for Monero) so callers can enforce a network.
44
+ - `Toncoin` alias for the `Ton` validator.
45
+ - `bundler-audit` dependency advisory check, wired into `mise run check` and CI.
46
+ - Regression specs for checksum mutations, oversized input, nil input, invalid
47
+ currency names, and the `address_type` contract across all currencies.
48
+
49
+ ### Changed
50
+
51
+ - **Breaking:** `address_type` is now public on every validator and returns a
52
+ type `Symbol` or `nil`. Previously it was private on most validators, returned
53
+ `nil` for ETH/SOL/XLM, and required arguments on BCH.
54
+ - **Breaking:** TON's detected type changed from `:TON` to `:ton_mainnet` /
55
+ `:ton_testnet`.
56
+ - **Breaking:** Monero's detected type is now `:standard`, `:integrated`, or
57
+ `:subaddress` (was `:monero`).
58
+ - ETH now reports `address_type` `:eth`, SOL reports `:solana`, and XLM reports
59
+ `:account` / `:muxed`.
60
+ - The BCH internal type-mapping helper was renamed from `address_type(code, type)`
61
+ to `type_mapping(code, type)`.
62
+
63
+ ### Documentation
64
+
65
+ - Documented supported per-currency types, the `address_type` API, network
66
+ accessors, and an explicit "Format coverage" section listing address formats
67
+ that are intentionally out of scope (and rejected rather than silently
68
+ accepted).
69
+
70
+ [0.2.0]: https://github.com/vtm9/adequate_crypto_address/releases/tag/v0.2.0
data/README.md CHANGED
@@ -29,27 +29,132 @@ Or install it yourself as:
29
29
  gem install adequate_crypto_address
30
30
  ```
31
31
 
32
+ ## Upgrading from 0.1.x to 0.2.0
33
+
34
+ 0.2.0 hardens the validators against malformed input and makes the
35
+ `address_type` API consistent. Most callers of `.valid?` need no changes, but a
36
+ few behaviors changed. See the [CHANGELOG](CHANGELOG.md) for the full list.
37
+
38
+ ### Stricter validation (addresses that used to pass may now fail)
39
+
40
+ Some malformed addresses that earlier versions incorrectly accepted are now
41
+ rejected. If your app stored or allow-listed such values, re-validate them:
42
+
43
+ - **BCH** legacy addresses with a bad checksum/length, CashAddr addresses with a
44
+ non-`bitcoincash`/`bchtest`/`bchreg` prefix (e.g. `evil:...`), or a payload
45
+ shorter than the version byte declares.
46
+ - **TON** any 48-character string (e.g. `"A" * 48`) — a valid tag, workchain,
47
+ and CRC16 checksum are now required.
48
+ - **Monero** anything that is not a checksum-valid Base58 address (e.g.
49
+ `"4" + "!" * 94`).
50
+
51
+ If you rely on a currency name, note that unknown names still raise
52
+ `AdequateCryptoAddress::UnknownCurrency`, but a `nil` or malformed **address**
53
+ now consistently returns `false` from `.valid?` instead of raising.
54
+
55
+ ### `address_type` is now public and consistent
56
+
57
+ `AdequateCryptoAddress.address_type(address, currency)` is a supported public
58
+ method for every currency. It returns the detected type as a `Symbol` when the
59
+ address is valid, or `nil` when it is not.
60
+
61
+ Some returned type symbols changed. Update any code that compared against the
62
+ old values:
63
+
64
+ | Currency | 0.1.x | 0.2.0 |
65
+ | -------- | --------------- | -------------------------------------------- |
66
+ | TON | `:TON` | `:ton_mainnet` / `:ton_testnet` |
67
+ | Monero | `:monero` | `:standard` / `:integrated` / `:subaddress` |
68
+ | ETH | `nil` | `:eth` |
69
+ | SOL | `nil` | `:solana` |
70
+ | XLM | `nil` | `:account` / `:muxed` |
71
+
72
+ ```ruby
73
+ # 0.1.x
74
+ AdequateCryptoAddress::Ton.new(addr).send(:address_type) # private, => :TON
75
+
76
+ # 0.2.0
77
+ AdequateCryptoAddress.address_type(addr, :TON) # public, => :ton_mainnet
78
+ ```
79
+
80
+ Type symbols passed to `.valid?` for these currencies changed accordingly, e.g.
81
+ `valid?(addr, :TON, :ton_mainnet)`.
82
+
83
+ ### New network accessors
84
+
85
+ SegWit Bitcoin types are network-agnostic; call `.network` to enforce a network:
86
+
87
+ ```ruby
88
+ AdequateCryptoAddress.address('bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq', :btc).network #=> :mainnet
89
+ AdequateCryptoAddress.address('tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx', :btc).network #=> :testnet
90
+ ```
91
+
92
+ `Xmr#network` reports `:mainnet` / `:testnet` / `:stagenet`.
93
+
94
+ ### Internal rename (only if you subclassed `Bch`)
95
+
96
+ The BCH internal helper `address_type(address_code, address_type)` was renamed
97
+ to `type_mapping(address_code, address_type)`. The public `address_type` (no
98
+ arguments) now returns the detected type.
99
+
32
100
  ## Main API
33
101
 
34
- ##### .valid? (address, currency [, type = :prod])
102
+ ##### .valid? (address, currency [, type = nil])
35
103
 
36
104
  ###### Parameters
37
105
  * address - Wallet address to validate.
38
106
  * currency - Currency name string or symbol in any case, `:bitcoin` or `'BTC'` or `:btc` or `'BitCoin'`
39
107
  * type - Optional. You can enforce validation with specific type. Not all currencies support types.
40
108
 
41
- > Returns true if the address (string) is a valid wallet address for the crypto currency specified, see below for supported currencies.
109
+ > 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`; a `nil` or malformed address returns `false`.
110
+
111
+ ##### .address_type (address, currency)
112
+
113
+ > Returns the detected address type as a `Symbol` when the address is valid for the currency, or `nil` when it is not. The type vocabulary per currency is listed below.
114
+
115
+ ```ruby
116
+ AdequateCryptoAddress.address_type('bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq', :btc) #=> :segwit_v0_keyhash
117
+ AdequateCryptoAddress.address_type('not a real address', :btc) #=> nil
118
+ ```
42
119
 
43
120
  ### Supported crypto currencies
44
121
 
45
- * Bitcoin/BTC, `'bitcoin'` or `'BTC'` types: `:segwit_v0_keyhash :segwit_v0_scripthash :hash160 :p2sh`
122
+ * Bitcoin/BTC, `'bitcoin'` or `'BTC'` types: `:segwit_v0_keyhash :segwit_v0_scripthash :taproot :hash160 :p2sh :hash160test :p2shtest`. SegWit types are network-agnostic; call `AdequateCryptoAddress.address(addr, :btc).network` to get `:mainnet`/`:testnet`.
46
123
  * BitcoinCash/BCH, `'bitcoincash'` or `'BCH'` types: `:p2sh :p2pkh :p2pkhtest :p2shtest`
124
+ * Cardano/ADA, `'cardano'` or `'ADA'` types: `:prod :test`
47
125
  * Dash, `'dash'` or `'DASH'` types: `:prod :test`
126
+ * Dogecoin/DOGE, `'dogecoin'` or `'DOGE'` types: `:prod :test`
48
127
  * Zcash/ZEC, `'zcash'` or `'ZEC'` types: `:prod :test`
49
- * Ethereum/ETH, `'ethereum'` or `'ETH'`
50
- * Ripple/XRP, `'ripple'` or `'XRP'`
51
- * Toncoin, `'TON'`
52
- * Monero/XRM, `'monero'`
128
+ * Ethereum/ETH, `'ethereum'` or `'ETH'` type: `:eth`
129
+ * Litecoin/LTC, `'litecoin'` or `'LTC'` types: `:prod :test`
130
+ * Ripple/XRP, `'ripple'` or `'XRP'` type: `:common`
131
+ * Solana/SOL, `'solana'` or `'SOL'` type: `:solana`
132
+ * Stellar/XLM, `'stellar'` or `'XLM'` types: `:account :muxed`
133
+ * Toncoin/TON, `'TON'` or `'Toncoin'` types: `:ton_mainnet :ton_testnet`
134
+ * Monero/XMR, `'monero'` or `'XMR'` types: `:standard :integrated :subaddress` (`.network` reports `:mainnet`/`:testnet`/`:stagenet`)
135
+
136
+ ### Format coverage
137
+
138
+ This gem validates the mainstream, checksum-bearing **payment** address for each
139
+ supported chain. The following related formats are currently **out of scope** and
140
+ are rejected (they return `false`) rather than silently accepted. They may be
141
+ added in future releases:
142
+
143
+ * Bitcoin witness versions 2–16 and newer output forms (e.g. P2MR / BIP-360);
144
+ only v0 (Bech32) and v1 Taproot (Bech32m) are recognized.
145
+ * Litecoin MWEB (`ltcmweb1...`) addresses and future witness versions.
146
+ * Zcash Sapling, Unified, Orchard, and TEX addresses; only transparent
147
+ `t`-addresses are validated.
148
+ * XRP X-addresses (with embedded destination tags); only classic `r...`
149
+ addresses are validated.
150
+ * Dash Platform Bech32m addresses; only Core Base58Check addresses are validated.
151
+ * Cardano Byron and stake/reward addresses; only Shelley payment address
152
+ types 0–7 are validated. Stake addresses are intentionally excluded from a
153
+ payment-address validator.
154
+ * Stellar contract (`C...`) and other SEP-23 StrKey types; only `G` (account)
155
+ and `M` (muxed) addresses are validated.
156
+ * TON raw `workchain:account_id` addresses; only the Base64URL user-friendly
157
+ form is validated.
53
158
 
54
159
  ## Usage
55
160
 
@@ -110,7 +215,7 @@ class Model < ActiveRecord::Base
110
215
  end
111
216
 
112
217
  ```
113
- ### Add your currnecy
218
+ ### Add your currency
114
219
  ``` ruby
115
220
  # frozen_string_literal: true
116
221
  # for Rails /config/initializers/adequate_crypto_address.rb
@@ -133,34 +238,43 @@ AdequateCryptoAddress.valid?('addr', :coin) #=> true
133
238
 
134
239
  ## Development
135
240
 
136
- Run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
241
+ The repository pins Ruby with [mise](https://mise.jdx.dev/). Install the toolchain and dependencies, then run
242
+ the complete local quality gate:
243
+
244
+ ```bash
245
+ mise trust
246
+ mise install
247
+ mise run setup
248
+ mise run check
249
+ ```
250
+
251
+ Individual tasks are available as `mise run test`, `mise run lint`, and `mise run build`. Use
252
+ `mise run console` for an interactive prompt.
137
253
 
138
254
  ## Contributing
139
255
 
140
256
  1. Fork [the repo](https://github.com/vtm9/adequate_crypto_address)
141
- 2. Grab dependencies: `bundle install`
142
- 3. Make sure everything is working: `bundle exec rake spec`
257
+ 2. Trust and install the toolchain: `mise trust && mise install`
258
+ 3. Grab dependencies: `mise run setup`
143
259
  4. Make your changes
144
- 5. Test your changes
145
- 5. Create a Pull Request
146
- 6. Celebrate!!!!!
260
+ 5. Make sure everything is working: `mise run check`
261
+ 6. Create a pull request
147
262
 
148
263
  ## Notes
149
264
 
150
265
  Bug reports and pull requests are welcome on GitHub at https://github.com/vtm9/adequate_crypto_address/issues
151
266
 
152
- [gem-version-svg]: https://badge.fury.io/rb/adequate_crypto_address.svg
153
- [gem-version-link]: https://rubygems.org/gems/adequate_crypto_address
154
- [downloads-svg]: http://ruby-gem-downloads-badge.herokuapp.com/adequate_crypto_address
155
- [downloads-link]: https://rubygems.org/gems/adequate_crypto_address
156
- [build-status-svg]: https://travis-ci.org/vtm9/adequate_crypto_address.svg?branch=master
157
- [build-status-link]:https://travis-ci.org/vtm9/adequate_crypto_address
158
- [coverage-status-svg]: https://coveralls.io/repos/vtm9/adequate_crypto_address/badge.svg?branch=master
159
- [coverage-status-link]: https://coveralls.io/r/vtm9/adequate_crypto_address?branch=master
160
- [codeclimate-status-svg]: https://codeclimate.com/github/vtm9/adequate_crypto_address.svg
161
- [codeclimate-status-link]: https://codeclimate.com/github/vtm9/adequate_crypto_address
162
- [docs-rubydoc-svg]: https://img.shields.io/badge/docs-rubydoc-blue.svg
163
- [docs-rubydoc-link]: http://www.rubydoc.info/gems/adequate_crypto_address/
164
- [license-svg]: https://img.shields.io/badge/license-MIT-blue.svg
165
- [license-link]: https://github.com/vtm9/adequate_crypto_address/blob/master/LICENSE.txt
166
-
267
+ [gem-version-svg]: https://badge.fury.io/rb/adequate_crypto_address.svg
268
+ [gem-version-link]: https://rubygems.org/gems/adequate_crypto_address
269
+ [downloads-svg]: http://ruby-gem-downloads-badge.herokuapp.com/adequate_crypto_address
270
+ [downloads-link]: https://rubygems.org/gems/adequate_crypto_address
271
+ [build-status-svg]: https://github.com/vtm9/adequate_crypto_address/actions/workflows/ci.yml/badge.svg?branch=master
272
+ [build-status-link]: https://github.com/vtm9/adequate_crypto_address/actions/workflows/ci.yml
273
+ [coverage-status-svg]: https://coveralls.io/repos/vtm9/adequate_crypto_address/badge.svg?branch=master
274
+ [coverage-status-link]: https://coveralls.io/r/vtm9/adequate_crypto_address?branch=master
275
+ [codeclimate-status-svg]: https://codeclimate.com/github/vtm9/adequate_crypto_address.svg
276
+ [codeclimate-status-link]: https://codeclimate.com/github/vtm9/adequate_crypto_address
277
+ [docs-rubydoc-svg]: https://img.shields.io/badge/docs-rubydoc-blue.svg
278
+ [docs-rubydoc-link]: http://www.rubydoc.info/gems/adequate_crypto_address/
279
+ [license-svg]: https://img.shields.io/badge/license-MIT-blue.svg
280
+ [license-link]: https://github.com/vtm9/adequate_crypto_address/blob/master/LICENSE.txt
data/bin/console CHANGED
@@ -5,7 +5,6 @@ $LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
5
5
  require 'adequate_crypto_address'
6
6
 
7
7
  ACA = AdequateCryptoAddress
8
- include AdequateCryptoAddress
9
8
 
10
9
  require 'pry'
11
10
  Pry.start
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AdequateCryptoAddress
4
+ class Ada
5
+ attr_reader :address, :type
6
+
7
+ BASE_ADDRESS_TYPES = [0, 1, 2, 3].freeze
8
+ POINTER_ADDRESS_TYPES = [4, 5].freeze
9
+ ENTERPRISE_ADDRESS_TYPES = [6, 7].freeze
10
+ VALID_ADDRESS_TYPES = (BASE_ADDRESS_TYPES + POINTER_ADDRESS_TYPES + ENTERPRISE_ADDRESS_TYPES).freeze
11
+ # Shelley payment addresses cap at ~103 chars (base); allow headroom while
12
+ # still bounding work, since Bech32 decoding runs with ignore_length: true.
13
+ MAX_LENGTH = 130
14
+
15
+ def initialize(address)
16
+ @address = address
17
+ @type = detect_type
18
+ end
19
+
20
+ def valid?(type = nil)
21
+ if type
22
+ address_type == type.to_sym
23
+ else
24
+ !address_type.nil?
25
+ end
26
+ end
27
+
28
+ # :prod or :test when valid, otherwise nil.
29
+ def address_type
30
+ type
31
+ end
32
+
33
+ private
34
+
35
+ def detect_type
36
+ decoded = safely_decode_address
37
+ return nil unless decoded
38
+
39
+ bytes, hrp = decoded
40
+
41
+ header = bytes.first
42
+ addr_type = header >> 4
43
+ network = header & 0x0f
44
+
45
+ return nil unless VALID_ADDRESS_TYPES.include?(addr_type)
46
+
47
+ return :prod if hrp == 'addr' && network == 1
48
+ return :test if hrp == 'addr_test' && network.zero?
49
+
50
+ nil
51
+ end
52
+
53
+ def safely_decode_address
54
+ return nil if address.to_s.length > MAX_LENGTH
55
+
56
+ decode_address
57
+ rescue StandardError
58
+ nil
59
+ end
60
+
61
+ def decode_address
62
+ actual_hrp, data, encoding = Utils::Bech32.decode(
63
+ address,
64
+ ignore_length: true,
65
+ include_encoding: true
66
+ )
67
+
68
+ return nil unless %w[addr addr_test].include?(actual_hrp)
69
+ return nil unless encoding == :bech32
70
+
71
+ bytes = Utils::Bech32.convert_bits(
72
+ data,
73
+ from_bits: 5,
74
+ to_bits: 8,
75
+ pad: false
76
+ )
77
+
78
+ return nil unless bytes
79
+ return nil if bytes.empty?
80
+
81
+ header = bytes.first
82
+ addr_type = header >> 4
83
+
84
+ return nil unless valid_length?(addr_type, bytes)
85
+
86
+ [bytes, actual_hrp]
87
+ end
88
+
89
+ def valid_length?(addr_type, bytes)
90
+ case addr_type
91
+ when *BASE_ADDRESS_TYPES
92
+ # header (1) + payment hash (28) + stake hash (28)
93
+ bytes.length == 57
94
+ when *POINTER_ADDRESS_TYPES
95
+ valid_pointer?(bytes.drop(29))
96
+ when *ENTERPRISE_ADDRESS_TYPES
97
+ # header (1) + payment hash (28)
98
+ bytes.length == 29
99
+ else
100
+ false
101
+ end
102
+ end
103
+
104
+ def valid_pointer?(pointer)
105
+ return false if pointer.length < 3
106
+
107
+ index = 0
108
+ 3.times do
109
+ first_byte = true
110
+ loop do
111
+ return false if index >= pointer.length
112
+
113
+ byte = pointer[index]
114
+ index += 1
115
+ return false if first_byte && byte == 0x80
116
+
117
+ first_byte = false
118
+ break if byte.nobits?(0x80)
119
+ end
120
+ end
121
+
122
+ index == pointer.length
123
+ end
124
+ end
125
+
126
+ Cardano = Ada
127
+ end
@@ -6,13 +6,14 @@ module AdequateCryptoAddress
6
6
  EXPECTED_LENGTH = 50
7
7
  ADDRESS_TYPES = {}.freeze
8
8
  ALPHABET_TYPE = :bitcoin
9
+ MAX_LENGTH = 100
9
10
 
10
11
  attr_reader :address, :type
11
12
  alias raw_address address
12
13
 
13
14
  def initialize(address)
14
15
  @address = address
15
- @type = address_type
16
+ @type = detect_type
16
17
  end
17
18
 
18
19
  def valid?(validated_type = nil)
@@ -23,26 +24,41 @@ module AdequateCryptoAddress
23
24
  end
24
25
  end
25
26
 
27
+ # Public contract: the detected network type Symbol, or nil when invalid.
28
+ def address_type
29
+ type
30
+ end
31
+
26
32
  private
27
33
 
28
34
  attr_reader :decoded
29
35
 
30
- def address_type
31
- @decoded = begin
32
- decode_base58
33
- rescue StandardError
34
- nil
35
- end
36
- if decoded && decoded.bytesize == self.class::EXPECTED_LENGTH && valid_address_checksum?
37
- self.class::ADDRESS_TYPES.each do |net_type, net_prefixes|
38
- net_prefixes.each do |net_prefix|
39
- return net_type if decoded.start_with?(net_prefix)
40
- end
41
- end
36
+ def too_long?
37
+ address.to_s.length > self.class::MAX_LENGTH
38
+ end
39
+
40
+ def detect_type
41
+ return nil if too_long?
42
+
43
+ @decoded = safely_decode_base58
44
+ return unless valid_decoded_address?
45
+
46
+ matching_type = self.class::ADDRESS_TYPES.find do |_network, prefixes|
47
+ prefixes.any? { |prefix| decoded.start_with?(prefix) }
42
48
  end
49
+ matching_type&.first
50
+ end
51
+
52
+ def safely_decode_base58
53
+ decode_base58
54
+ rescue StandardError
43
55
  nil
44
56
  end
45
57
 
58
+ def valid_decoded_address?
59
+ decoded&.bytesize == self.class::EXPECTED_LENGTH && valid_address_checksum?
60
+ end
61
+
46
62
  def decode_base58
47
63
  Base58.base58_to_binary(address, self.class::ALPHABET_TYPE).each_byte.map { |b| b.to_s(16).rjust(2, '0') }.join
48
64
  end
@@ -50,7 +66,7 @@ module AdequateCryptoAddress
50
66
  def valid_address_checksum?
51
67
  return false unless decoded
52
68
 
53
- checksum(decoded[0...-8]) == decoded[-8..-1]
69
+ checksum(decoded[0...-8]) == decoded[-8..]
54
70
  end
55
71
 
56
72
  def checksum(bytes)