trx 0.2.2
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 +7 -0
- data/lib/trx/abi/contract_event.rb +25 -0
- data/lib/trx/abi/decoder.rb +101 -0
- data/lib/trx/abi/encoder.rb +119 -0
- data/lib/trx/abi/function.rb +37 -0
- data/lib/trx/abi/function_input.rb +15 -0
- data/lib/trx/abi/function_output.rb +15 -0
- data/lib/trx/abi.rb +33 -0
- data/lib/trx/address.rb +63 -0
- data/lib/trx/http_client.rb +34 -0
- data/lib/trx/key.rb +82 -0
- data/lib/trx/signature.rb +14 -0
- data/lib/trx/utils.rb +48 -0
- data/lib/trx/version.rb +5 -0
- data/lib/trx.rb +29 -0
- metadata +201 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: dfcdbe41de11ca908dfaf1b12e58262dba9966cc7b319d57fc48c9e455d36360
|
4
|
+
data.tar.gz: f048ec52470a044ae883262dbfe51ee706bd2a8339a2298c014c642ac4e92e53
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 870645d6c67d40a2ca3eb7f6d70186408cfd622534eb89040008683298e68ae4c84a9137f6144771afe9003844b6fb3d7339fbb0eb1085f8c9c3835464ed54cc
|
7
|
+
data.tar.gz: 134d7401cfd15c6192f6f8974dd3fb13bf829e7e707632dba50166ecc26a422fe081a94b2cfccc78d37c749cc90293bc08abc50d3b07c9ce51a8fa2a8dc83408
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Trx
|
2
|
+
module Abi
|
3
|
+
class ContractEvent
|
4
|
+
|
5
|
+
attr_accessor :name, :signature, :input_types, :inputs, :event_string, :address, :client
|
6
|
+
|
7
|
+
def initialize(data)
|
8
|
+
@name = data["name"]
|
9
|
+
@input_types = data.fetch("inputs", []).map {|x| x["type"]}
|
10
|
+
@inputs = data.fetch("inputs", []).map {|x| x["name"]}
|
11
|
+
@event_string = "#{@name}(#{@input_types.join(",")})"
|
12
|
+
@signature = Digest::Keccak.hexdigest(@event_string, 256)
|
13
|
+
end
|
14
|
+
|
15
|
+
def set_address(address)
|
16
|
+
@address = address
|
17
|
+
end
|
18
|
+
|
19
|
+
def set_client(client)
|
20
|
+
@client = client
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module Trx
|
2
|
+
module Abi
|
3
|
+
class Decoder
|
4
|
+
|
5
|
+
def decode(type, value, start = 0)
|
6
|
+
is_array, arity, array_subtype = Abi::parse_array_type(type)
|
7
|
+
if is_array && arity
|
8
|
+
decode_static_array(arity, array_subtype, value, start)
|
9
|
+
elsif is_array
|
10
|
+
decode_dynamic_array(array_subtype, value, start)
|
11
|
+
else
|
12
|
+
value = value.gsub(/^0x/,'')
|
13
|
+
core, subtype = Abi::parse_type(type)
|
14
|
+
method_name = "decode_#{core}".to_sym
|
15
|
+
self.send(method_name, value, subtype, start)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def decode_static_array(arity, array_subtype, value, start)
|
20
|
+
(0..arity-1).map { |i| decode(array_subtype, value, start + i * 64) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def decode_dynamic_array(array_subtype, value, start)
|
24
|
+
location = decode_uint(value[start..(start+63)]) * 2
|
25
|
+
size = decode_uint(value[location..location+63])
|
26
|
+
(0..size-1).map { |i| decode(array_subtype, value, location + (i+1) * 64) }
|
27
|
+
end
|
28
|
+
|
29
|
+
def decode_fixed(value, subtype = "128x128", start = 0)
|
30
|
+
decode_int(trim(value, start, fixed_bitsize(subtype))).to_f / 2**exponent(subtype)
|
31
|
+
end
|
32
|
+
|
33
|
+
def decode_uint(value, subtype = "256", start = 0)
|
34
|
+
trim(value, start, bitsize(subtype)).hex
|
35
|
+
end
|
36
|
+
|
37
|
+
def decode_int(value, subtype = "256", start = 0)
|
38
|
+
raise ArgumentError if value.nil?
|
39
|
+
size = bitsize(subtype)
|
40
|
+
value = trim(value, start, size)
|
41
|
+
(value[0..1] == "ff") ? (value.hex - (2 ** size)) : value.hex
|
42
|
+
end
|
43
|
+
|
44
|
+
def decode_bool(value, _, start)
|
45
|
+
value = trim(value, start, 4)
|
46
|
+
return true if value == "1"
|
47
|
+
return false if value == "0"
|
48
|
+
raise ArgumentError
|
49
|
+
end
|
50
|
+
|
51
|
+
def decode_address(value, _ = nil, start)
|
52
|
+
raise ArgumentError if value.size-start < 64
|
53
|
+
value[start+24..start+63]
|
54
|
+
end
|
55
|
+
|
56
|
+
def decode_bytes(value, subtype, start)
|
57
|
+
subtype.present? ? decode_static_bytes(value, subtype, start) : decode_dynamic_bytes(value, start)
|
58
|
+
end
|
59
|
+
|
60
|
+
def decode_static_bytes(value, subtype = nil, start = 0)
|
61
|
+
trim(value, start, subtype.to_i*8).scan(/.{2}/).map {|x| x.hex}.pack('C*').strip
|
62
|
+
end
|
63
|
+
|
64
|
+
def decode_dynamic_bytes(value, start = 0)
|
65
|
+
location = decode_uint(value[start..(start+63)]) * 2
|
66
|
+
size = decode_uint(value[location..location+63]) * 2
|
67
|
+
value[location+64..location+63+size].scan(/.{2}/).map {|x| x.hex}.pack('C*')
|
68
|
+
end
|
69
|
+
|
70
|
+
def decode_string(value, _ = nil, start = 0)
|
71
|
+
decode_dynamic_bytes(value, start).force_encoding('utf-8')
|
72
|
+
end
|
73
|
+
|
74
|
+
def decode_arguments(arguments, data)
|
75
|
+
data = data.gsub(/^0x/,'')
|
76
|
+
types = arguments.map { |o| o.type }
|
77
|
+
types.each.with_index.map { |t , i| decode(t, data, i*64) }
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
def trim(value, start, bitsize = 256)
|
82
|
+
value[start+63-(bitsize/4-1)..start+63]
|
83
|
+
end
|
84
|
+
|
85
|
+
def bitsize(subtype, default = 256)
|
86
|
+
subtype.present? ? subtype.to_i : default
|
87
|
+
end
|
88
|
+
|
89
|
+
def fixed_bitsize(subtype = nil)
|
90
|
+
subtype ||= "128x128"
|
91
|
+
_, x, n = /(\d+)x(\d+)/.match(subtype).to_a
|
92
|
+
x.to_i + n.to_i
|
93
|
+
end
|
94
|
+
|
95
|
+
def exponent(subtype, default = 128)
|
96
|
+
subtype.nil? ? default : /(\d+)x(\d+)/.match(subtype)[2].to_i
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module Trx
|
2
|
+
|
3
|
+
module Abi
|
4
|
+
class Encoder
|
5
|
+
|
6
|
+
def encode(type, value)
|
7
|
+
is_array, arity, array_subtype = Abi::parse_array_type(type)
|
8
|
+
if is_array && arity
|
9
|
+
encode_static_array(arity, array_subtype, value)
|
10
|
+
elsif is_array
|
11
|
+
encode_dynamic_array(array_subtype, value)
|
12
|
+
else
|
13
|
+
core, subtype = Abi::parse_type(type)
|
14
|
+
method_name = "encode_#{core}".to_sym
|
15
|
+
self.send(method_name, value, subtype)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def encode_static_array(arity, array_subtype, array)
|
20
|
+
raise "Wrong number of arguments" if arity != array.size
|
21
|
+
array.inject("") { |a, e| a << encode(array_subtype, e) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def encode_dynamic_array(array_subtype, array)
|
25
|
+
location = encode_uint(@inputs ? size_of_inputs(@inputs) + @tail.size/2 : 32)
|
26
|
+
size = encode_uint(array.size)
|
27
|
+
data = array.inject("") { |a, e| a << encode(array_subtype, e) }
|
28
|
+
[location, size + data]
|
29
|
+
end
|
30
|
+
|
31
|
+
def encode_int(value, _ = nil)
|
32
|
+
to_twos_complement(value).to_s(16).rjust(64, '0')
|
33
|
+
end
|
34
|
+
|
35
|
+
def encode_uint(value, _ = nil)
|
36
|
+
raise ArgumentError if value < 0
|
37
|
+
encode_int(value)
|
38
|
+
end
|
39
|
+
|
40
|
+
def encode_bool(value, _)
|
41
|
+
(value ? "1" : "0").rjust(64, '0')
|
42
|
+
end
|
43
|
+
|
44
|
+
def encode_fixed(value, subtype)
|
45
|
+
n = subtype.nil? ? 128 : /(\d+)x(\d+)/.match(subtype)[2].to_i
|
46
|
+
do_encode_fixed(value, n)
|
47
|
+
end
|
48
|
+
|
49
|
+
def do_encode_fixed(value, n)
|
50
|
+
encode_uint((value * 2**n).to_i)
|
51
|
+
end
|
52
|
+
|
53
|
+
def encode_ufixed(_value, _)
|
54
|
+
raise NotImplementedError
|
55
|
+
end
|
56
|
+
|
57
|
+
def encode_bytes(value, subtype)
|
58
|
+
subtype.nil? ? encode_dynamic_bytes(value) : encode_static_bytes(value)
|
59
|
+
end
|
60
|
+
|
61
|
+
def encode_static_bytes(value)
|
62
|
+
value.bytes.map {|x| x.to_s(16).rjust(2, '0')}.join("").ljust(64, '0')
|
63
|
+
end
|
64
|
+
|
65
|
+
def encode_dynamic_bytes(value)
|
66
|
+
location = encode_uint(@inputs ? size_of_inputs(@inputs) + @tail.size/2 : 32)
|
67
|
+
size = encode_uint(value.size)
|
68
|
+
content = encode_static_bytes(value)
|
69
|
+
[location, size + content]
|
70
|
+
end
|
71
|
+
|
72
|
+
def encode_string(value, _)
|
73
|
+
location = encode_uint(@inputs ? size_of_inputs(@inputs) + @tail.size/2 : 32)
|
74
|
+
size = encode_uint(value.bytes.size)
|
75
|
+
content = value.bytes.map {|x| x.to_s(16).rjust(2, '0')}.join("").ljust(64, '0')
|
76
|
+
[location, size + content]
|
77
|
+
end
|
78
|
+
|
79
|
+
def encode_address(value, _)
|
80
|
+
value = "0" * 24 + value.gsub(/^0x/,'')
|
81
|
+
raise ArgumentError if value.size != 64
|
82
|
+
value
|
83
|
+
end
|
84
|
+
|
85
|
+
def ensure_prefix(value)
|
86
|
+
value.start_with?("0x") ? value : ("0x" + value)
|
87
|
+
end
|
88
|
+
|
89
|
+
def encode_arguments(inputs, args)
|
90
|
+
raise "Wrong number of arguments" if inputs.length != args.length
|
91
|
+
@head = ""
|
92
|
+
@tail = ""
|
93
|
+
@inputs = inputs
|
94
|
+
inputs.each.with_index do |input, index|
|
95
|
+
encoded = encode(input.type, args[index])
|
96
|
+
if encoded.is_a? Array
|
97
|
+
@head << encoded[0]
|
98
|
+
@tail << encoded[1]
|
99
|
+
else
|
100
|
+
@head << encoded
|
101
|
+
end
|
102
|
+
end
|
103
|
+
@head + @tail
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
def to_twos_complement(number)
|
108
|
+
(number & ((1 << 256) - 1))
|
109
|
+
end
|
110
|
+
|
111
|
+
def size_of_inputs(inputs)
|
112
|
+
inputs.map do |input|
|
113
|
+
_, arity, _ = Abi::parse_array_type(input.type)
|
114
|
+
arity.nil? ? 32 : arity * 32
|
115
|
+
end.inject(:+)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Trx
|
2
|
+
module Abi
|
3
|
+
class Function
|
4
|
+
|
5
|
+
attr_accessor :name, :inputs, :outputs, :signature, :constant, :function_string
|
6
|
+
|
7
|
+
def initialize(data)
|
8
|
+
@name = data["name"]
|
9
|
+
@constant = data["constant"]
|
10
|
+
@inputs = data.fetch("inputs", []).map do |input|
|
11
|
+
FunctionInput.new(input)
|
12
|
+
end
|
13
|
+
@outputs = data.fetch("outputs", []).map do |output|
|
14
|
+
FunctionOutput.new(output)
|
15
|
+
end
|
16
|
+
@function_string = self.class.calc_signature(@name, @inputs)
|
17
|
+
@signature = self.class.calc_id(@function_string)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.to_canonical_type(type)
|
21
|
+
type
|
22
|
+
.gsub(/(int)(\z|\D)/, '\1256\2')
|
23
|
+
.gsub(/(uint)(\z|\D)/, '\1256\2')
|
24
|
+
.gsub(/(fixed)(\z|\D)/, '\1128x128\2')
|
25
|
+
.gsub(/(ufixed)(\z|\D)/, '\1128x128\2')
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.calc_signature(name, inputs)
|
29
|
+
"#{name}(#{inputs.map {|x| self.to_canonical_type(x.type) }.join(",")})"
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.calc_id(signature)
|
33
|
+
Digest::Keccak.hexdigest(signature, 256)[0..7]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/trx/abi.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
module Trx
|
2
|
+
module Abi
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def parse_abi(abi)
|
6
|
+
constructor = abi.detect {|x| x["type"].downcase == "constructor"}
|
7
|
+
if constructor.present?
|
8
|
+
constructor_inputs = constructor["inputs"].map { |input| FunctionInput.new(input) }
|
9
|
+
else
|
10
|
+
constructor_inputs = []
|
11
|
+
end
|
12
|
+
functions = abi.select {|x| x["type"].downcase == "function" }.map { |fun| Function.new(fun) }
|
13
|
+
events = abi.select {|x| x["type"].downcase == "event" }.map { |evt| ContractEvent.new(evt) }
|
14
|
+
[constructor_inputs, functions, events]
|
15
|
+
end
|
16
|
+
|
17
|
+
def parse_type(type)
|
18
|
+
raise NotImplementedError if type.ends_with?("]")
|
19
|
+
match = /(\D+)(\d.*)?/.match(type)
|
20
|
+
[match[1], match[2]]
|
21
|
+
end
|
22
|
+
|
23
|
+
def parse_array_type(type)
|
24
|
+
match = /(.+)\[(\d*)\]\z/.match(type)
|
25
|
+
if match
|
26
|
+
[true, match[2].present? ? match[2].to_i : nil, match[1]]
|
27
|
+
else
|
28
|
+
[false, nil, nil]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
data/lib/trx/address.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Trx
|
4
|
+
class Address
|
5
|
+
ADDRESS_PREFIX = "41"
|
6
|
+
|
7
|
+
def self.from_public_hex(public_hex)
|
8
|
+
raise ArgumentError, "Expected String, got #{public_hex.class}" unless public_hex.is_a?(String)
|
9
|
+
|
10
|
+
bytes = Utils.hex_to_bin(public_hex)
|
11
|
+
address_bytes = Utils.keccak256(bytes[1..-1])[-20..-1]
|
12
|
+
prefixed_address_hex = ADDRESS_PREFIX + RLP::Utils.encode_hex(address_bytes)
|
13
|
+
prefixed_address_bytes = Utils.hex_to_bin(prefixed_address_hex)
|
14
|
+
checksum = Utils.base58check(prefixed_address_bytes).hexdigest[0..7]
|
15
|
+
base58check_adress = Base58.encode_hex(prefixed_address_hex + checksum)
|
16
|
+
|
17
|
+
Address.new(base58check_adress)
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :address
|
21
|
+
|
22
|
+
def initialize(address)
|
23
|
+
raise ArgumentError, "Expected String, got #{address.class}" unless address.is_a?(String)
|
24
|
+
|
25
|
+
@address = Utils.prefix_hex(address)
|
26
|
+
end
|
27
|
+
|
28
|
+
def checksum_matches?
|
29
|
+
computed_checksum = Utils.sha256(
|
30
|
+
Utils.sha256(to_bytes).digest
|
31
|
+
).hexdigest[0..7]
|
32
|
+
|
33
|
+
checksum_hex == computed_checksum
|
34
|
+
end
|
35
|
+
alias_method :valid?, :checksum_matches?
|
36
|
+
|
37
|
+
def to_bytes
|
38
|
+
Utils.hex_to_bin(to_base58_decode_hex)[0..-5]
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_base58_decode_hex
|
42
|
+
Utils.base58_decode_hex(address)
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_hex
|
46
|
+
Utils.base58_decode_hex(address)[0..-9]
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_s
|
50
|
+
Utils.remove_hex_prefix(address)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def checksum_bytes
|
56
|
+
bytes[0..3]
|
57
|
+
end
|
58
|
+
|
59
|
+
def checksum_hex
|
60
|
+
to_base58_decode_hex[-8..-1]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Trx
|
2
|
+
class HttpClient
|
3
|
+
attr_reader :host, :timeout, :trongrig_token, :headers, :raise_error
|
4
|
+
|
5
|
+
MAINNET_HOST = "https://api.trongrid.io"
|
6
|
+
SHASTA_HOST = "https://api.shasta.trongrid.io"
|
7
|
+
NILE_HOST = "https://nile.trongrid.io"
|
8
|
+
|
9
|
+
def initialize(host = nil, timeout: 10, trongrig_token: nil, headers: {}, raise_error: true)
|
10
|
+
@host = host
|
11
|
+
@timeout = timeout
|
12
|
+
@trongrig_token = trongrig_token
|
13
|
+
@headers = headers
|
14
|
+
@raise_error = raise_error
|
15
|
+
end
|
16
|
+
|
17
|
+
def perform_command(command, params, method: :post)
|
18
|
+
connection.public_send(method, command, params).body
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def connection
|
24
|
+
@_connection ||= Faraday.new(host) do |builder|
|
25
|
+
builder.options.timeout = timeout
|
26
|
+
builder.use Faraday::Response::RaiseError if raise_error
|
27
|
+
builder.request :url_encoded
|
28
|
+
builder.response :json
|
29
|
+
builder.adapter Faraday.default_adapter
|
30
|
+
builder.headers['TRON-PRO-API-KEY'] = trongrig_token unless trongrig_token.nil?
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/trx/key.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Trx
|
4
|
+
class Key
|
5
|
+
attr_reader :private_key, :public_key
|
6
|
+
|
7
|
+
def initialize(priv: nil)
|
8
|
+
|
9
|
+
# Creates a new, randomized libsecp256k1 context.
|
10
|
+
ctx = Secp256k1::Context.new context_randomization_bytes: SecureRandom.random_bytes(32)
|
11
|
+
|
12
|
+
key = if priv.nil?
|
13
|
+
# Creates a new random key pair (public, private).
|
14
|
+
ctx.generate_key_pair
|
15
|
+
else
|
16
|
+
|
17
|
+
# Converts hex private keys to binary strings.
|
18
|
+
priv = Utils.hex_to_bin priv if Utils.is_hex? priv
|
19
|
+
|
20
|
+
# Creates a keypair from existing private key data.
|
21
|
+
ctx.key_pair_from_private_key priv
|
22
|
+
end
|
23
|
+
|
24
|
+
# Sets the attributes.
|
25
|
+
@private_key = key.private_key
|
26
|
+
@public_key = key.public_key
|
27
|
+
end
|
28
|
+
|
29
|
+
def private_hex
|
30
|
+
Utils.bin_to_hex @private_key.data
|
31
|
+
end
|
32
|
+
|
33
|
+
def private_bytes
|
34
|
+
@private_key.data
|
35
|
+
end
|
36
|
+
|
37
|
+
def public_hex
|
38
|
+
Utils.bin_to_hex @public_key.uncompressed
|
39
|
+
end
|
40
|
+
|
41
|
+
def public_hex_compressed
|
42
|
+
Utils.bin_to_hex @public_key.compressed
|
43
|
+
end
|
44
|
+
|
45
|
+
def public_bytes
|
46
|
+
@public_key.uncompressed
|
47
|
+
end
|
48
|
+
|
49
|
+
def public_bytes_compressed
|
50
|
+
@public_key.compressed
|
51
|
+
end
|
52
|
+
|
53
|
+
def address
|
54
|
+
Address.from_public_hex public_hex
|
55
|
+
end
|
56
|
+
|
57
|
+
def sign(blob)
|
58
|
+
context = Secp256k1::Context.new
|
59
|
+
compact, recovery_id = context.sign_recoverable(@private_key, blob).compact
|
60
|
+
signature = compact.bytes
|
61
|
+
is_leading_zero = true
|
62
|
+
signature.append recovery_id
|
63
|
+
|
64
|
+
# TODO: WTF? It is necessary?
|
65
|
+
|
66
|
+
# [recovery_id].pack("N").unpack("C*").each do |byte|
|
67
|
+
# is_leading_zero = false if byte > 0 and is_leading_zero
|
68
|
+
# unless is_leading_zero and byte === 0
|
69
|
+
# signature.append recovery_id
|
70
|
+
# end
|
71
|
+
# end
|
72
|
+
|
73
|
+
Utils.bin_to_hex signature.pack "c*"
|
74
|
+
end
|
75
|
+
|
76
|
+
def personal_sign(message)
|
77
|
+
prefixed_message = Signature.prefix_message message
|
78
|
+
hashed_message = Utils.keccak256 prefixed_message
|
79
|
+
sign hashed_message
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Trx
|
4
|
+
module Signature
|
5
|
+
class SignatureError < StandardError; end
|
6
|
+
extend self
|
7
|
+
|
8
|
+
PREFIX_BYTE = "\x19".freeze
|
9
|
+
|
10
|
+
def prefix_message(message)
|
11
|
+
"#{PREFIX_BYTE}Tron Signed Message:\n#{message.size}#{message}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/trx/utils.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Trx
|
4
|
+
module Utils
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def hex_to_bin(string)
|
8
|
+
RLP::Utils.decode_hex remove_hex_prefix(string)
|
9
|
+
end
|
10
|
+
|
11
|
+
def bin_to_hex(bytes)
|
12
|
+
RLP::Utils.encode_hex bytes
|
13
|
+
end
|
14
|
+
|
15
|
+
def remove_hex_prefix(string)
|
16
|
+
string[0, 2] == "0x" ? string[2..-1] : string
|
17
|
+
end
|
18
|
+
|
19
|
+
def prefix_hex(hex)
|
20
|
+
hex.match(/\A0x/) ? hex : "0x#{hex}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def keccak256(value)
|
24
|
+
Digest::Keccak.new(256).digest(value)
|
25
|
+
end
|
26
|
+
|
27
|
+
def base58check(value)
|
28
|
+
sha256(sha256(value).digest)
|
29
|
+
end
|
30
|
+
|
31
|
+
def base58_decode_hex(hex)
|
32
|
+
Base58.decode_hex(remove_hex_prefix(hex))
|
33
|
+
end
|
34
|
+
|
35
|
+
def sha256(value)
|
36
|
+
hash = OpenSSL::Digest.new("SHA256")
|
37
|
+
hash.update(value)
|
38
|
+
|
39
|
+
hash
|
40
|
+
end
|
41
|
+
|
42
|
+
def is_hex?(str)
|
43
|
+
return false unless str.is_a? String
|
44
|
+
str = remove_hex_prefix str
|
45
|
+
str.match /\A[0-9a-fA-F]*\z/
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/trx/version.rb
ADDED
data/lib/trx.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "digest/keccak"
|
4
|
+
require "rlp"
|
5
|
+
require "openssl"
|
6
|
+
require "base58-alphabets"
|
7
|
+
require "rbsecp256k1"
|
8
|
+
require "securerandom"
|
9
|
+
require "faraday"
|
10
|
+
require "active_support"
|
11
|
+
require "active_support/core_ext"
|
12
|
+
|
13
|
+
require_relative "trx/utils"
|
14
|
+
require_relative "trx/signature"
|
15
|
+
require_relative "trx/key"
|
16
|
+
require_relative "trx/address"
|
17
|
+
require_relative "trx/version"
|
18
|
+
require_relative "trx/http_client"
|
19
|
+
require_relative "trx/abi"
|
20
|
+
require_relative "trx/abi/encoder"
|
21
|
+
require_relative "trx/abi/decoder"
|
22
|
+
require_relative "trx/abi/function"
|
23
|
+
require_relative "trx/abi/function_input"
|
24
|
+
require_relative "trx/abi/function_output"
|
25
|
+
require_relative "trx/abi/contract_event"
|
26
|
+
|
27
|
+
module Trx
|
28
|
+
class Error < StandardError; end
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: trx
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Anton Kostyuk
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-02-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.2'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: pry
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.14'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.14'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: ecdsa
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.2'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.2'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: faraday
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.2.0
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 2.2.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: keccak
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 1.3.0
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 1.3.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rlp
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.7.3
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.7.3
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rbsecp256k1
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '5.1'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '5.1'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: securerandom
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: base58-alphabets
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '1.0'
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '1.0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: activesupport
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '4.2'
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '4.2'
|
153
|
+
description: ''
|
154
|
+
email:
|
155
|
+
- anton.kostuk.2012@gmail.com
|
156
|
+
executables: []
|
157
|
+
extensions: []
|
158
|
+
extra_rdoc_files: []
|
159
|
+
files:
|
160
|
+
- lib/trx.rb
|
161
|
+
- lib/trx/abi.rb
|
162
|
+
- lib/trx/abi/contract_event.rb
|
163
|
+
- lib/trx/abi/decoder.rb
|
164
|
+
- lib/trx/abi/encoder.rb
|
165
|
+
- lib/trx/abi/function.rb
|
166
|
+
- lib/trx/abi/function_input.rb
|
167
|
+
- lib/trx/abi/function_output.rb
|
168
|
+
- lib/trx/address.rb
|
169
|
+
- lib/trx/http_client.rb
|
170
|
+
- lib/trx/key.rb
|
171
|
+
- lib/trx/signature.rb
|
172
|
+
- lib/trx/utils.rb
|
173
|
+
- lib/trx/version.rb
|
174
|
+
homepage: https://github.com/RainbowPonny/trx-rb
|
175
|
+
licenses:
|
176
|
+
- MIT
|
177
|
+
metadata:
|
178
|
+
homepage_uri: https://github.com/RainbowPonny/trx-rb
|
179
|
+
source_code_uri: https://github.com/RainbowPonny/trx-rb
|
180
|
+
changelog_uri: https://github.com/RainbowPonny/trx-rb
|
181
|
+
rubygems_mfa_required: 'true'
|
182
|
+
post_install_message:
|
183
|
+
rdoc_options: []
|
184
|
+
require_paths:
|
185
|
+
- lib
|
186
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
187
|
+
requirements:
|
188
|
+
- - ">="
|
189
|
+
- !ruby/object:Gem::Version
|
190
|
+
version: 2.5.0
|
191
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
192
|
+
requirements:
|
193
|
+
- - ">="
|
194
|
+
- !ruby/object:Gem::Version
|
195
|
+
version: '0'
|
196
|
+
requirements: []
|
197
|
+
rubygems_version: 3.3.7
|
198
|
+
signing_key:
|
199
|
+
specification_version: 4
|
200
|
+
summary: TRON HTTP API client
|
201
|
+
test_files: []
|