adequate_crypto_address 0.1.11 → 0.3.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.
@@ -5,6 +5,7 @@ require 'digest'
5
5
 
6
6
  module AdequateCryptoAddress
7
7
  class InvalidAddress < StandardError; end
8
+
8
9
  class Bch
9
10
  class InvalidLegacyAddress < ::AdequateCryptoAddress::InvalidAddress; end
10
11
  class InvalidCashAddress < ::AdequateCryptoAddress::InvalidAddress; end
@@ -25,6 +26,11 @@ module AdequateCryptoAddress
25
26
  ]
26
27
  }.freeze
27
28
  DEFAULT_PREFIX = :bitcoincash
29
+ CASH_PREFIXES = %w[bitcoincash bchtest bchreg].freeze
30
+ # CashAddr size bits (version byte bits 2..0) mapped to the hash length in bytes.
31
+ CASH_HASH_SIZES = { 0 => 20, 1 => 24, 2 => 28, 3 => 32, 4 => 40, 5 => 48, 6 => 56, 7 => 64 }.freeze
32
+ LEGACY_LENGTH = 25 # 1 version byte + 20 hash bytes + 4 checksum bytes
33
+ MAX_LENGTH = 120
28
34
 
29
35
  attr_reader :raw_address, :type, :payload, :prefix, :digest
30
36
 
@@ -41,23 +47,20 @@ module AdequateCryptoAddress
41
47
  end
42
48
  end
43
49
 
44
- def address_type(address_code, address_type)
45
- TYPE_MAP[address_code].each do |mapping|
46
- return mapping if mapping.include?(address_type)
47
- end
48
-
49
- raise(AdequateCryptoAddress::InvalidAddress, 'Could not determine address type')
50
+ # Public contract: the detected legacy/cash type Symbol, or nil when invalid.
51
+ def address_type
52
+ type
50
53
  end
51
54
 
52
55
  def legacy_address
53
- type_int = address_type(:legacy, type)[1]
56
+ type_int = type_mapping(:legacy, type)[1]
54
57
  input = code_list_to_string([type_int] + payload + Array(digest))
55
58
  input += Digest::SHA256.digest(Digest::SHA256.digest(input))[0..3] unless digest
56
59
  Base58.binary_to_base58(input, :bitcoin)
57
60
  end
58
61
 
59
62
  def cash_address
60
- type_int = address_type(:cash, type)[1]
63
+ type_int = type_mapping(:cash, type)[1]
61
64
  p = [type_int] + payload
62
65
  p = convertbits(p, 8, 5)
63
66
  checksum = calculate_cash_checksum(p)
@@ -68,7 +71,18 @@ module AdequateCryptoAddress
68
71
 
69
72
  private
70
73
 
74
+ def type_mapping(address_code, address_type)
75
+ TYPE_MAP[address_code].each do |mapping|
76
+ return mapping if mapping.include?(address_type)
77
+ end
78
+
79
+ raise(AdequateCryptoAddress::InvalidAddress, 'Could not determine address type')
80
+ end
81
+
71
82
  def normalize
83
+ return unless raw_address.is_a?(String)
84
+ return if raw_address.length > MAX_LENGTH
85
+
72
86
  begin
73
87
  from_cash_string
74
88
  rescue InvalidCashAddress
@@ -79,43 +93,83 @@ module AdequateCryptoAddress
79
93
  end
80
94
 
81
95
  def from_cash_string
82
- if (raw_address.upcase != raw_address) && (raw_address.downcase != raw_address)
83
- raise(InvalidCashAddress, 'Cash address contains uppercase and lowercase characters')
84
- end
96
+ validate_cash_address_case!
85
97
 
86
98
  @raw_address = raw_address.downcase
87
- @raw_address = "#{DEFAULT_PREFIX}:#{raw_address}" if !raw_address.include?(':')
99
+ @raw_address = "#{DEFAULT_PREFIX}:#{raw_address}" unless raw_address.include?(':')
88
100
 
89
101
  @prefix, base32string = raw_address.split(':')
90
- decoded = b32decode(base32string)
102
+ raise(InvalidCashAddress, 'Unsupported cash address prefix') unless CASH_PREFIXES.include?(prefix)
103
+
104
+ assign_cash_payload(decode_cash_payload(base32string))
105
+ end
91
106
 
92
- raise(InvalidCashAddress, 'Bad cash address checksum') if !verify_cash_checksum(decoded)
107
+ def assign_cash_payload(payload_bytes)
108
+ version = payload_bytes.first
109
+ raise(InvalidCashAddress, 'Invalid cash address version byte') unless valid_cash_version?(version, payload_bytes)
110
+
111
+ @payload = payload_bytes[1..]
112
+ @type = cash_type(version)
113
+ @type = testnet_type(type) if prefix == 'bchtest'
114
+ end
93
115
 
94
- converted = convertbits(decoded, 5, 8)
95
- @type = address_type(:cash, converted[0].to_i)[0]
96
- @payload = converted[1..-7]
116
+ # Version byte layout: bit 7 reserved (must be 0), bits 6..3 type, bits 2..0 size.
117
+ def valid_cash_version?(version, payload_bytes)
118
+ return false unless version&.nobits?(0x80)
119
+ return false unless cash_type(version)
97
120
 
98
- @type = :p2shtest if prefix == 'bchtest' && type == :p2sh
99
- @type = :p2pkhtest if prefix == 'bchtest' && type == :p2pkh
121
+ expected = CASH_HASH_SIZES[version & 0x07]
122
+ (payload_bytes.length - 1) == expected
123
+ end
124
+
125
+ def cash_type(version)
126
+ { 0 => :p2pkh, 1 => :p2sh }[(version >> 3) & 0x0f]
100
127
  end
101
128
 
102
129
  def from_legacy_string
103
- decoded = nil
104
- begin
105
- decoded = Base58.base58_to_binary(raw_address, :bitcoin).bytes
106
- rescue StandardError
107
- raise(InvalidLegacyAddress, 'Could not decode legacy address')
108
- end
130
+ decoded = decode_legacy_address
131
+ raise(InvalidLegacyAddress, 'Invalid legacy address length') unless decoded.length == LEGACY_LENGTH
132
+ raise(InvalidLegacyAddress, 'Bad legacy address checksum') unless valid_legacy_checksum?(decoded)
109
133
 
110
- @type = address_type(:legacy, decoded[0].to_i)[0]
134
+ @type = type_mapping(:legacy, decoded[0].to_i)[0]
111
135
  @payload = decoded[1..-5]
112
- @digest = decoded[-4..-1]
136
+ @digest = decoded[-4..]
113
137
  @prefix = DEFAULT_PREFIX
114
-
115
- @type = :p2shtest if prefix == 'bchtest' && type == :p2sh
116
- @type = :p2pkhtest if prefix == 'bchtest' && type == :p2pkh
117
138
  @prefix = 'bchtest' if [:p2shtest, :p2pkhtest].include?(type)
118
139
  end
140
+
141
+ def valid_legacy_checksum?(decoded)
142
+ body = code_list_to_string(decoded[0...-4])
143
+ Digest::SHA256.digest(Digest::SHA256.digest(body))[0, 4] == code_list_to_string(decoded[-4..])
144
+ end
145
+
146
+ def validate_cash_address_case!
147
+ return if raw_address.upcase == raw_address || raw_address.downcase == raw_address
148
+
149
+ raise(InvalidCashAddress, 'Cash address contains uppercase and lowercase characters')
150
+ end
151
+
152
+ def decode_cash_payload(base32string)
153
+ decoded = b32decode(base32string)
154
+ raise(InvalidCashAddress, 'Invalid cash address encoding') if decoded.empty? || decoded.include?(nil)
155
+ raise(InvalidCashAddress, 'Bad cash address checksum') unless verify_cash_checksum(decoded)
156
+
157
+ # Drop the eight 5-bit checksum symbols, then map the payload back to bytes.
158
+ payload = convertbits(decoded[0...-8], 5, 8, pad: false)
159
+ raise(InvalidCashAddress, 'Invalid cash address payload') unless payload
160
+
161
+ payload
162
+ end
163
+
164
+ def decode_legacy_address
165
+ Base58.base58_to_binary(raw_address, :bitcoin).bytes
166
+ rescue StandardError
167
+ raise(InvalidLegacyAddress, 'Could not decode legacy address')
168
+ end
169
+
170
+ def testnet_type(address_type)
171
+ { p2sh: :p2shtest, p2pkh: :p2pkhtest }.fetch(address_type)
172
+ end
119
173
  end
120
174
 
121
175
  Bitcoincash = Bch
@@ -2,97 +2,124 @@
2
2
 
3
3
  module AdequateCryptoAddress
4
4
  class Btc
5
- attr_reader :address, :type
5
+ BASE58_TYPES = {
6
+ '00' => :hash160,
7
+ '05' => :p2sh,
8
+ '6f' => :hash160test,
9
+ 'c4' => :p2shtest
10
+ }.freeze
11
+ BASE58_NETWORKS = { '00' => :mainnet, '05' => :mainnet, '6f' => :testnet, 'c4' => :testnet }.freeze
12
+ SEGWIT_NETWORKS = { 'bc' => :mainnet, 'tb' => :testnet }.freeze
13
+ MAX_LENGTH = 100
14
+
15
+ attr_reader :address, :type, :network
6
16
  alias raw_address address
7
17
 
8
18
  def initialize(address)
9
19
  @address = address
10
- @type = address_type
20
+ @type = detect_type
11
21
  end
12
22
 
13
- def valid?(type = nil)
14
- if type
15
- address_type == type.to_sym
16
- else
17
- !address_type.nil?
18
- end
23
+ def valid?(validated_type = nil)
24
+ return !type.nil? unless validated_type
25
+
26
+ type == validated_type.to_sym
27
+ end
28
+
29
+ # Public contract: the detected type Symbol, or nil when invalid. The
30
+ # SegWit type is network-agnostic; use #network to enforce mainnet/testnet.
31
+ def address_type
32
+ type
19
33
  end
20
34
 
21
35
  private
22
36
 
23
- def address_type
24
- segwit_decoded = begin
25
- decode_segwit_address
26
- rescue StandardError
27
- nil
28
- end
29
- if segwit_decoded
30
- witness_version, witness_program_hex = segwit_decoded
31
- witness_program = [witness_program_hex].pack('H*')
32
-
33
- return :segwit_v0_keyhash if witness_version == 0 && witness_program.bytesize == 20
34
-
35
- return :segwit_v0_scripthash if witness_version == 0 && witness_program.bytesize == 32
36
-
37
- return :taproot if witness_version == 1 && witness_program.bytesize == 32
38
- end
39
-
40
- base58_decoded = begin
41
- decode_base58(address)
42
- rescue StandardError
43
- nil
44
- end
45
- if base58_decoded && base58_decoded.bytesize == 50 && valid_base58_address_checksum?(base58_decoded)
46
- case base58_decoded[0...2]
47
- when '00'
48
- return :hash160
49
- when '05'
50
- return :p2sh
51
- when '6f'
52
- return :hash160test
53
- when 'c4'
54
- return :p2shtest
55
- end
56
- end
37
+ def detect_type
38
+ return nil if address.to_s.length > MAX_LENGTH
57
39
 
58
- nil
40
+ segwit_address_type || base58_address_type
41
+ end
42
+
43
+ def segwit_address_type
44
+ hrp, witness_version, witness_program_hex = safely_decode_segwit_address
45
+ return unless witness_version
46
+
47
+ witness_program_size = witness_program_hex.length / 2
48
+ type = segwit_type(witness_version, witness_program_size)
49
+ @network = SEGWIT_NETWORKS[hrp] if type
50
+ type
51
+ end
52
+
53
+ def segwit_type(witness_version, witness_program_size)
54
+ return { 20 => :segwit_v0_keyhash, 32 => :segwit_v0_scripthash }[witness_program_size] if witness_version.zero?
55
+
56
+ :taproot if witness_version == 1 && witness_program_size == 32
57
+ end
58
+
59
+ def base58_address_type
60
+ decoded = safely_decode_base58
61
+ return unless decoded&.bytesize == 50
62
+ return unless valid_base58_address_checksum?(decoded)
63
+
64
+ version = decoded[0...2]
65
+ @network = BASE58_NETWORKS[version]
66
+ BASE58_TYPES[version]
59
67
  end
60
68
 
61
69
  def decode_segwit_address
62
- actual_hrp, data = Utils::Bech32.decode(address)
70
+ actual_hrp, data, encoding = Utils::Bech32.decode(address, include_encoding: true)
71
+ return nil unless %w[bc tb].include?(actual_hrp)
72
+ return nil if data.empty? || data.size > 65
73
+
74
+ witness_version = data.first
75
+ return nil unless valid_witness_encoding?(witness_version, encoding)
76
+
77
+ program = Utils::Bech32.convert_bits(data[1..], from_bits: 5, to_bits: 8, pad: false)
78
+ return nil unless valid_witness_program?(witness_version, program)
79
+
80
+ [actual_hrp, witness_version, program.pack('C*').unpack1('H*')]
81
+ end
63
82
 
64
- return nil if actual_hrp.nil?
83
+ def valid_witness_encoding?(witness_version, encoding)
84
+ return false unless witness_version&.between?(0, 16)
65
85
 
66
- length = data.size
67
- return nil if length == 0 || length > 65
68
- return nil if actual_hrp != 'bc'
69
- return nil if data[0] > 16
86
+ encoding == (witness_version.zero? ? :bech32 : :bech32m)
87
+ end
88
+
89
+ def valid_witness_program?(witness_version, program)
90
+ return false unless program&.size&.between?(2, 40)
91
+ return [20, 32].include?(program.size) if witness_version.zero?
92
+ return program.size == 32 if witness_version == 1
70
93
 
71
- program = Utils::Bech32.convert_bits(data[1..-1], from_bits: 5, to_bits: 8, pad: false)
72
- return nil if program.nil?
94
+ true
95
+ end
73
96
 
74
- length = program.size
75
- return nil if length < 2 || length > 40
76
- return nil if data[0] == 0 && length != 20 && length != 32
77
- return nil if data[0] == 1 && length != 32
97
+ def safely_decode_segwit_address
98
+ decode_segwit_address
99
+ rescue StandardError
100
+ []
101
+ end
78
102
 
79
- program_hex = program.pack('C*').unpack('H*').first
80
- [data[0], program_hex]
103
+ def safely_decode_base58
104
+ decode_base58
105
+ rescue StandardError
106
+ nil
81
107
  end
82
108
 
83
- def decode_base58(_base58_val)
109
+ def decode_base58
84
110
  Base58.base58_to_binary(address, :bitcoin).each_byte.map { |b| b.to_s(16).rjust(2, '0') }.join
85
111
  end
86
112
 
87
113
  def valid_base58_address_checksum?(base58_decoded)
88
114
  return false unless base58_decoded
89
115
 
90
- checksum(base58_decoded[0...-8]) == base58_decoded[-8..-1]
116
+ checksum(base58_decoded[0...-8]) == base58_decoded[-8..]
91
117
  end
92
118
 
93
119
  def checksum(hex)
94
120
  Digest::SHA256.hexdigest(Digest::SHA256.digest([hex].pack('H*')))[0...8]
95
121
  end
96
122
  end
123
+
97
124
  Bitcoin = Btc
98
125
  end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AdequateCryptoAddress
4
+ class Doge < Altcoin
5
+ ADDRESS_TYPES = {
6
+ prod: %w[1e 16],
7
+ test: %w[71 c4]
8
+ }.freeze
9
+ end
10
+
11
+ Dogecoin = Doge
12
+ end
@@ -23,7 +23,10 @@ module AdequateCryptoAddress
23
23
  end
24
24
  end
25
25
 
26
- def address_type; end
26
+ # :eth when the address is a valid (optionally checksummed) address, else nil.
27
+ def address_type
28
+ valid? ? :eth : nil
29
+ end
27
30
 
28
31
  private
29
32
 
@@ -37,8 +40,6 @@ module AdequateCryptoAddress
37
40
  normalize(cased.join)
38
41
  end
39
42
 
40
- private
41
-
42
43
  def checksum_matches?
43
44
  address == checksummed
44
45
  end
@@ -67,17 +68,26 @@ module AdequateCryptoAddress
67
68
  remove_hex_prefix address
68
69
  end
69
70
 
70
- def remove_hex_prefix(s)
71
- s[0, 2] == '0x' ? s[2..-1] : s
71
+ def remove_hex_prefix(string)
72
+ string[0, 2] == '0x' ? string[2..] : string
72
73
  end
73
74
 
74
75
  def bin_to_hex(string)
75
76
  string.unpack1('H*')
76
77
  end
77
78
 
78
- def keccak256(x)
79
- Digest::Keccak.new(256).digest(x)
79
+ def keccak256(input)
80
+ Digest::Keccak.new(256).digest(input)
80
81
  end
81
82
  end
82
83
  Ethereum = Eth
84
+
85
+ # BNB Smart Chain uses the same address format and EIP-55 checksum as Ethereum.
86
+ class Bsc < Eth
87
+ def address_type
88
+ valid? ? :bsc : nil
89
+ end
90
+ end
91
+
92
+ Binancesmartchain = Bsc
83
93
  end
@@ -6,51 +6,59 @@ module AdequateCryptoAddress
6
6
 
7
7
  private
8
8
 
9
- def address_type
10
- segwit_decoded = begin
11
- decode_segwit_address
12
- rescue StandardError
13
- nil
14
- end
15
-
16
- if segwit_decoded
17
- witness_version, witness_program_hex, hrp = segwit_decoded
18
- witness_program = [witness_program_hex].pack('H*')
19
-
20
- if witness_version == 0
21
- return :prod if hrp == 'ltc' && [20, 32].include?(witness_program.bytesize)
22
- return :test if hrp == 'tltc' && [20, 32].include?(witness_program.bytesize)
23
- end
24
-
25
- if witness_version == 1 && witness_program.bytesize == 32
26
- return :prod if hrp == 'ltc'
27
- return :test if hrp == 'tltc'
28
- end
29
- end
30
-
31
- super
9
+ def detect_type
10
+ return nil if too_long?
11
+
12
+ segwit_address_type || super
32
13
  end
33
14
 
34
- def decode_segwit_address
35
- actual_hrp, data = Utils::Bech32.decode(address)
15
+ def segwit_address_type
16
+ witness_version, witness_program_hex, hrp = safely_decode_segwit_address
17
+ return unless witness_version
36
18
 
37
- return nil if actual_hrp.nil?
19
+ witness_program_size = witness_program_hex.length / 2
20
+ return unless valid_known_witness_program?(witness_version, witness_program_size)
21
+
22
+ { 'ltc' => :prod, 'tltc' => :test }[hrp]
23
+ end
38
24
 
39
- length = data.size
40
- return nil if length == 0 || length > 65
25
+ def decode_segwit_address
26
+ actual_hrp, data, encoding = Utils::Bech32.decode(address, include_encoding: true)
41
27
  return nil unless %w[ltc tltc].include?(actual_hrp)
42
- return nil if data[0] > 16
28
+ return nil if data.empty? || data.size > 65
29
+
30
+ witness_version = data.first
31
+ return nil unless valid_witness_encoding?(witness_version, encoding)
43
32
 
44
- program = Utils::Bech32.convert_bits(data[1..-1], from_bits: 5, to_bits: 8, pad: false)
45
- return nil if program.nil?
33
+ program = Utils::Bech32.convert_bits(data[1..], from_bits: 5, to_bits: 8, pad: false)
34
+ return nil unless valid_witness_program?(witness_version, program)
35
+
36
+ program_hex = program.pack('C*').unpack1('H*')
37
+ [witness_version, program_hex, actual_hrp]
38
+ end
46
39
 
47
- length = program.size
48
- return nil if length < 2 || length > 40
49
- return nil if data[0] == 0 && length != 20 && length != 32
50
- return nil if data[0] == 1 && length != 32
40
+ def valid_witness_encoding?(witness_version, encoding)
41
+ return false unless witness_version&.between?(0, 16)
42
+
43
+ encoding == (witness_version.zero? ? :bech32 : :bech32m)
44
+ end
45
+
46
+ def valid_witness_program?(witness_version, program)
47
+ return false unless program&.size&.between?(2, 40)
48
+ return [20, 32].include?(program.size) if witness_version.zero?
49
+ return program.size == 32 if witness_version == 1
50
+
51
+ true
52
+ end
53
+
54
+ def valid_known_witness_program?(witness_version, program_size)
55
+ (witness_version.zero? && [20, 32].include?(program_size)) || (witness_version == 1 && program_size == 32)
56
+ end
51
57
 
52
- program_hex = program.pack('C*').unpack('H*').first
53
- [data[0], program_hex, actual_hrp]
58
+ def safely_decode_segwit_address
59
+ decode_segwit_address
60
+ rescue StandardError
61
+ []
54
62
  end
55
63
  end
56
64
  Litecoin = Ltc
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AdequateCryptoAddress
4
+ class Sol
5
+ ALPHABET_TYPE = :bitcoin
6
+ MAX_LENGTH = 64 # a 32-byte key encodes to at most 44 Base58 characters
7
+
8
+ attr_reader :address
9
+
10
+ def initialize(address_string)
11
+ @address = address_string
12
+ end
13
+
14
+ def valid?(_type = nil)
15
+ valid_format?
16
+ end
17
+
18
+ # :solana when the address decodes to a 32-byte key, otherwise nil.
19
+ def address_type
20
+ valid_format? ? :solana : nil
21
+ end
22
+
23
+ private
24
+
25
+ def valid_format?
26
+ return false if address.to_s.length > MAX_LENGTH
27
+
28
+ decoded = Base58.base58_to_binary(address, ALPHABET_TYPE)
29
+ decoded.bytesize == 32
30
+ rescue StandardError
31
+ false
32
+ end
33
+ end
34
+
35
+ Solana = Sol
36
+ end
@@ -1,7 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AdequateCryptoAddress
4
+ # Validates TON user-friendly addresses (TEP-2). The 48-character Base64URL
5
+ # form decodes to 36 bytes: tag(1) + workchain(1) + account_id(32) + crc16(2).
4
6
  class Ton
7
+ ADDRESS_LENGTH = 48
8
+ DECODED_LENGTH = 36
9
+ BOUNCEABLE_TAG = 0x11
10
+ NON_BOUNCEABLE_TAG = 0x51
11
+ TEST_FLAG = 0x80
12
+ VALID_WORKCHAINS = [0x00, 0xff].freeze
13
+
5
14
  attr_reader :address, :type
6
15
 
7
16
  def initialize(address)
@@ -17,14 +26,55 @@ module AdequateCryptoAddress
17
26
  end
18
27
  end
19
28
 
29
+ # :ton_mainnet or :ton_testnet when valid, otherwise nil.
30
+ def address_type
31
+ bytes = decode
32
+ return nil unless bytes
33
+
34
+ tag = bytes[0]
35
+ return nil unless valid_tag?(tag)
36
+ return nil unless VALID_WORKCHAINS.include?(bytes[1])
37
+ return nil unless checksum_valid?(bytes)
38
+
39
+ tag.anybits?(TEST_FLAG) ? :ton_testnet : :ton_mainnet
40
+ end
41
+
20
42
  private
21
43
 
22
- def address_type
23
- return :TON if address.size == 48 && pattern_valid?
44
+ def decode
45
+ return nil unless valid_encoding?
46
+
47
+ # Base64URL decode via core unpack (the base64 gem is only a wrapper).
48
+ bytes = address.tr('-_', '+/').unpack1('m0')&.bytes
49
+ bytes if bytes && bytes.length == DECODED_LENGTH
50
+ rescue ArgumentError
51
+ nil
24
52
  end
25
53
 
26
- def pattern_valid?
27
- address.match(/([0-9a-zA-Z]|-|_){48}/)
54
+ def valid_encoding?
55
+ address.is_a?(String) &&
56
+ address.length == ADDRESS_LENGTH &&
57
+ address.match?(/\A[A-Za-z0-9_-]{#{ADDRESS_LENGTH}}\z/)
58
+ end
59
+
60
+ def valid_tag?(tag)
61
+ [BOUNCEABLE_TAG, NON_BOUNCEABLE_TAG].include?(tag & ~TEST_FLAG)
62
+ end
63
+
64
+ def checksum_valid?(bytes)
65
+ crc = crc16(bytes[0...34])
66
+ [(crc >> 8) & 0xff, crc & 0xff] == bytes[34..35]
67
+ end
68
+
69
+ # CRC16-CCITT (XMODEM): polynomial 0x1021, initial value 0x0000.
70
+ def crc16(bytes)
71
+ bytes.reduce(0) do |crc, byte|
72
+ crc ^= byte << 8
73
+ 8.times { crc = crc.anybits?(0x8000) ? ((crc << 1) ^ 0x1021) & 0xffff : (crc << 1) & 0xffff }
74
+ crc
75
+ end
28
76
  end
29
77
  end
78
+
79
+ Toncoin = Ton
30
80
  end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AdequateCryptoAddress
4
+ # TRON mainnet addresses are Base58Check with the 0x41 version byte and a
5
+ # four-byte double-SHA256 checksum (25 bytes total, rendered as a 34-character
6
+ # string starting with "T"). https://developers.tron.network/docs/account
7
+ class Trx < Altcoin
8
+ ADDRESS_TYPES = { prod: %w[41] }.freeze
9
+ end
10
+
11
+ Tron = Trx
12
+ end