mixin_bot 0.12.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mixin_bot/api/address.rb +21 -0
  3. data/lib/mixin_bot/api/app.rb +5 -11
  4. data/lib/mixin_bot/api/asset.rb +9 -16
  5. data/lib/mixin_bot/api/attachment.rb +27 -22
  6. data/lib/mixin_bot/api/auth.rb +29 -51
  7. data/lib/mixin_bot/api/blaze.rb +4 -3
  8. data/lib/mixin_bot/api/collectible.rb +60 -58
  9. data/lib/mixin_bot/api/conversation.rb +29 -49
  10. data/lib/mixin_bot/api/encrypted_message.rb +17 -17
  11. data/lib/mixin_bot/api/legacy_multisig.rb +87 -0
  12. data/lib/mixin_bot/api/legacy_output.rb +50 -0
  13. data/lib/mixin_bot/api/legacy_payment.rb +31 -0
  14. data/lib/mixin_bot/api/legacy_snapshot.rb +39 -0
  15. data/lib/mixin_bot/api/legacy_transaction.rb +173 -0
  16. data/lib/mixin_bot/api/legacy_transfer.rb +42 -0
  17. data/lib/mixin_bot/api/me.rb +13 -17
  18. data/lib/mixin_bot/api/message.rb +13 -10
  19. data/lib/mixin_bot/api/multisig.rb +16 -221
  20. data/lib/mixin_bot/api/output.rb +46 -0
  21. data/lib/mixin_bot/api/payment.rb +9 -20
  22. data/lib/mixin_bot/api/pin.rb +57 -65
  23. data/lib/mixin_bot/api/rpc.rb +9 -11
  24. data/lib/mixin_bot/api/snapshot.rb +15 -29
  25. data/lib/mixin_bot/api/tip.rb +43 -0
  26. data/lib/mixin_bot/api/transaction.rb +184 -60
  27. data/lib/mixin_bot/api/transfer.rb +64 -32
  28. data/lib/mixin_bot/api/user.rb +83 -53
  29. data/lib/mixin_bot/api/withdraw.rb +52 -53
  30. data/lib/mixin_bot/api.rb +78 -45
  31. data/lib/mixin_bot/cli/api.rb +149 -5
  32. data/lib/mixin_bot/cli/utils.rb +14 -4
  33. data/lib/mixin_bot/cli.rb +13 -10
  34. data/lib/mixin_bot/client.rb +76 -127
  35. data/lib/mixin_bot/configuration.rb +98 -0
  36. data/lib/mixin_bot/nfo.rb +174 -0
  37. data/lib/mixin_bot/transaction.rb +505 -0
  38. data/lib/mixin_bot/utils/address.rb +108 -0
  39. data/lib/mixin_bot/utils/crypto.rb +182 -0
  40. data/lib/mixin_bot/utils/decoder.rb +58 -0
  41. data/lib/mixin_bot/utils/encoder.rb +63 -0
  42. data/lib/mixin_bot/utils.rb +8 -109
  43. data/lib/mixin_bot/uuid.rb +41 -0
  44. data/lib/mixin_bot/version.rb +1 -1
  45. data/lib/mixin_bot.rb +39 -14
  46. data/lib/mvm/bridge.rb +2 -19
  47. data/lib/mvm/client.rb +11 -33
  48. data/lib/mvm/nft.rb +4 -4
  49. data/lib/mvm/registry.rb +9 -9
  50. data/lib/mvm/scan.rb +3 -5
  51. data/lib/mvm.rb +5 -6
  52. metadata +101 -44
  53. data/lib/mixin_bot/utils/nfo.rb +0 -176
  54. data/lib/mixin_bot/utils/transaction.rb +0 -478
  55. data/lib/mixin_bot/utils/uuid.rb +0 -43
@@ -0,0 +1,182 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MixinBot
4
+ module Utils
5
+ module Crypto
6
+ def access_token(method, uri, body = '', **kwargs)
7
+ sig = Digest::SHA256.hexdigest(method + uri + body.to_s)
8
+ iat = Time.now.utc.to_i
9
+ exp = (Time.now.utc + (kwargs[:exp_in] || 600)).to_i
10
+ scp = kwargs[:scp] || 'FULL'
11
+ jti = SecureRandom.uuid
12
+ uid = kwargs[:app_id] || MixinBot.config.app_id
13
+ sid = kwargs[:session_id] || MixinBot.config.session_id
14
+ private_key = kwargs[:private_key] || MixinBot.config.session_private_key
15
+
16
+ payload = {
17
+ uid:,
18
+ sid:,
19
+ iat:,
20
+ exp:,
21
+ jti:,
22
+ sig:,
23
+ scp:
24
+ }
25
+
26
+ if private_key.blank?
27
+ raise ConfigurationNotValidError, 'private_key is required'
28
+ elsif private_key.size == 64
29
+ jwk = JOSE::JWK.from_okp [:Ed25519, private_key]
30
+ jws = JOSE::JWS.from({ 'alg' => 'EdDSA' })
31
+ else
32
+ jwk = JOSE::JWK.from_pem private_key
33
+ jws = JOSE::JWS.from({ 'alg' => 'RS512' })
34
+ end
35
+
36
+ jwt = JOSE::JWT.from payload
37
+ JOSE::JWT.sign(jwk, jws, jwt).compact
38
+ end
39
+
40
+ def generate_ed25519_key
41
+ ed25519_key = JOSE::JWA::Ed25519.keypair
42
+ {
43
+ private_key: Base64.urlsafe_encode64(ed25519_key[1], padding: false),
44
+ public_key: Base64.urlsafe_encode64(ed25519_key[0], padding: false)
45
+ }
46
+ end
47
+
48
+ def generate_rsa_key
49
+ rsa_key = OpenSSL::PKey::RSA.new 1024
50
+ {
51
+ private_key: rsa_key.to_pem,
52
+ public_key: rsa_key.public_key.to_pem
53
+ }
54
+ end
55
+
56
+ def generate_public_key(key)
57
+ point = JOSE::JWA::FieldElement.new(
58
+ OpenSSL::BN.new(key.reverse, 2),
59
+ JOSE::JWA::Edwards25519Point::L
60
+ )
61
+
62
+ (JOSE::JWA::Edwards25519Point.stdbase * point.x.to_i).encode
63
+ end
64
+
65
+ def sign(msg, key:)
66
+ msg = Digest::Blake3.digest msg
67
+
68
+ pub = generate_public_key key
69
+
70
+ y_point = JOSE::JWA::FieldElement.new(
71
+ OpenSSL::BN.new(key.reverse, 2),
72
+ JOSE::JWA::Edwards25519Point::L
73
+ )
74
+
75
+ key_digest = Digest::SHA512.digest key
76
+ msg_digest = Digest::SHA512.digest(key_digest[-32...] + msg)
77
+
78
+ z_point = JOSE::JWA::FieldElement.new(
79
+ OpenSSL::BN.new(msg_digest[...64].reverse, 2),
80
+ JOSE::JWA::Edwards25519Point::L
81
+ )
82
+
83
+ r_point = JOSE::JWA::Edwards25519Point.stdbase * z_point.x.to_i
84
+ hram_digest = Digest::SHA512.digest(r_point.encode + pub + msg)
85
+
86
+ x_point = JOSE::JWA::FieldElement.new(
87
+ OpenSSL::BN.new(hram_digest[...64].reverse, 2),
88
+ JOSE::JWA::Edwards25519Point::L
89
+ )
90
+ s_point = (x_point * y_point) + z_point
91
+
92
+ r_point.encode + s_point.to_bytes(36)
93
+ end
94
+
95
+ def generate_unique_uuid(uuid_1, uuid_2)
96
+ md5 = Digest::MD5.new
97
+ md5 << [uuid_1, uuid_2].min
98
+ md5 << [uuid_1, uuid_2].max
99
+ digest = md5.digest
100
+ digest6 = ((digest[6].ord & 0x0f) | 0x30).chr
101
+ digest8 = ((digest[8].ord & 0x3f) | 0x80).chr
102
+ cipher = digest[0...6] + digest6 + digest[7] + digest8 + digest[9..]
103
+
104
+ MixinBot::UUID.new(raw: cipher).unpacked
105
+ end
106
+
107
+ def unique_uuid(*uuids)
108
+ uuids = uuids.flatten.compact
109
+ uuids.sort
110
+ r = uuids.first
111
+ uuids.each_with_index do |uuid, i|
112
+ r = generate_unique_uuid(r, uuid) if i.positive?
113
+ end
114
+
115
+ r
116
+ end
117
+
118
+ def generate_trace_from_hash(hash, output_index = 0)
119
+ md5 = Digest::MD5.new
120
+ md5 << hash
121
+ md5 << [output_index].pack('c*') if output_index.positive? && output_index < 256
122
+ digest = md5.digest
123
+ digest[6] = ((digest[6].ord & 0x0f) | 0x30).chr
124
+ digest[8] = ((digest[8].ord & 0x3f) | 0x80).chr
125
+
126
+ MixinBot::UUID.new(raw: digest).unpacked
127
+ end
128
+
129
+ # decrypt the encrpted pin, just for test
130
+ def decrypt_pin(msg, shared_key:)
131
+ msg = Base64.urlsafe_decode64 msg
132
+ iv = msg[0..15]
133
+ cipher = msg[16..47]
134
+ alg = 'AES-256-CBC'
135
+ decode_cipher = OpenSSL::Cipher.new(alg)
136
+ decode_cipher.decrypt
137
+ decode_cipher.iv = iv
138
+ decode_cipher.key = shared_key
139
+ decode_cipher.update(cipher)
140
+ end
141
+
142
+ # use timestamp(timestamp) for iterator as default: must be bigger than the previous, the first time must be greater than 0. After a new session created, it will be reset to 0.
143
+ def encrypt_pin(pin, **kwargs)
144
+ pin = MixinBot.utils.decode_key pin
145
+
146
+ shared_key = kwargs[:shared_key]
147
+ raise ArgumentError, 'shared_key is required' if shared_key.blank?
148
+
149
+ iterator ||= kwargs[:iterator] || Time.now.utc.to_i
150
+ tszero = iterator % 0x100
151
+ tsone = (iterator % 0x10000) >> 8
152
+ tstwo = (iterator % 0x1000000) >> 16
153
+ tsthree = (iterator % 0x100000000) >> 24
154
+ tsstring = "#{tszero.chr}#{tsone.chr}#{tstwo.chr}#{tsthree.chr}\u0000\u0000\u0000\u0000"
155
+ encrypt_content = pin + tsstring + tsstring
156
+ pad_count = 16 - (encrypt_content.length % 16)
157
+ padded_content =
158
+ if pad_count.positive?
159
+ encrypt_content + (pad_count.chr * pad_count)
160
+ else
161
+ encrypt_content
162
+ end
163
+
164
+ alg = 'AES-256-CBC'
165
+ aes = OpenSSL::Cipher.new(alg)
166
+ iv = OpenSSL::Cipher.new(alg).random_iv
167
+ aes.encrypt
168
+ aes.key = shared_key
169
+ aes.iv = iv
170
+ cipher = aes.update(padded_content)
171
+ msg = iv + cipher
172
+ Base64.urlsafe_encode64 msg, padding: false
173
+ end
174
+
175
+ def tip_public_key(key, counter: 0)
176
+ raise ArgumentError, 'invalid key' if key.size < 32
177
+
178
+ (key[0...32].bytes + MixinBot::Utils.encode_uint_64(counter + 1)).pack('c*').unpack1('H*')
179
+ end
180
+ end
181
+ end
182
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MixinBot
4
+ module Utils
5
+ module Decoder
6
+ def decode_key(key)
7
+ return if key.blank?
8
+
9
+ if key.match?(/\A[\h]{64,}\z/i)
10
+ [key].pack('H*')
11
+ elsif key.match?(/\A[a-zA-Z0-9\-\_=]{43,}\z/)
12
+ Base64.urlsafe_decode64 key
13
+ elsif key.match?(/^-----BEGIN RSA PRIVATE KEY-----/)
14
+ key.gsub('\\r\\n', "\n").gsub("\r\n", "\n")
15
+ elsif key.match?(/\d{6}/) || (key.size % 32).zero?
16
+ key
17
+ else
18
+ raise ArgumentError, "Invalid key #{key}"
19
+ end
20
+ end
21
+
22
+ def decode_raw_transaction(hex)
23
+ MixinBot::Transaction.new(hex:).decode.to_h
24
+ end
25
+
26
+ def decode_uint_16(bytes)
27
+ raise ArgumentError, "only support bytes #{bytes}" unless bytes.is_a?(Array)
28
+
29
+ bytes.reverse.pack('C*').unpack1('S*')
30
+ end
31
+
32
+ def decode_uint_32(bytes)
33
+ raise ArgumentError, "only support bytes #{bytes}" unless bytes.is_a?(Array)
34
+
35
+ bytes.reverse.pack('C*').unpack1('L*')
36
+ end
37
+
38
+ def decode_uint_64(bytes)
39
+ raise ArgumentError, "only support bytes #{bytes}" unless bytes.is_a?(Array)
40
+
41
+ bytes.reverse.pack('C*').unpack1('Q*')
42
+ end
43
+
44
+ def decode_int(bytes)
45
+ int = 0
46
+ bytes.each do |byte|
47
+ int = (int * (2**8)) + byte
48
+ end
49
+
50
+ int
51
+ end
52
+
53
+ def hex_to_uuid(hex)
54
+ MixinBot::UUID.new(hex:).unpacked
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MixinBot
4
+ module Utils
5
+ module Encoder
6
+ def encode_raw_transaction(tx)
7
+ if tx.is_a? String
8
+ begin
9
+ tx = JSON.parse tx
10
+ rescue JSON::ParserError
11
+ tx
12
+ end
13
+ end
14
+
15
+ raise ArgumentError, "#{tx} is not a valid json" unless tx.is_a? Hash
16
+
17
+ tx = tx.with_indifferent_access
18
+
19
+ MixinBot::Transaction.new(**tx).encode.hex
20
+ end
21
+
22
+ def encode_uint_16(int)
23
+ raise ArgumentError, "only support int #{int}" unless int.is_a?(Integer)
24
+
25
+ [int].pack('S*').bytes.reverse
26
+ end
27
+
28
+ def encode_uint_32(int)
29
+ raise ArgumentError, "only support int #{int}" unless int.is_a?(Integer)
30
+
31
+ [int].pack('L*').bytes.reverse
32
+ end
33
+
34
+ def encode_uint_64(int)
35
+ raise ArgumentError, "only support int #{int}" unless int.is_a?(Integer)
36
+
37
+ [int].pack('Q*').bytes.reverse
38
+ end
39
+
40
+ def encode_int(int)
41
+ raise ArgumentError, 'not integer' unless int.is_a?(Integer)
42
+
43
+ bytes = []
44
+ loop do
45
+ break if int === 0
46
+
47
+ bytes.push int & 255
48
+ int = (int / (2**8)) | 0
49
+ end
50
+
51
+ bytes.reverse
52
+ end
53
+
54
+ def nft_memo(collection, token, extra)
55
+ MixinBot::Nfo.new(
56
+ collection:,
57
+ token:,
58
+ extra:
59
+ ).mint_memo
60
+ end
61
+ end
62
+ end
63
+ end
@@ -1,116 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './utils/nfo'
4
- require_relative './utils/uuid'
5
- require_relative './utils/transaction'
3
+ require_relative 'utils/address'
4
+ require_relative 'utils/crypto'
5
+ require_relative 'utils/decoder'
6
+ require_relative 'utils/encoder'
6
7
 
7
8
  module MixinBot
8
9
  module Utils
9
- class << self
10
- MAGIC = [0x77, 0x77].freeze
11
- TX_VERSION = 2
12
- MAX_ENCODE_INT = 0xFFFF
13
- NULL_BYTES = [0x00, 0x00].freeze
14
- AGGREGATED_SIGNATURE_PREFIX = 0xFF01
15
- AGGREGATED_SIGNATURE_ORDINAY_MASK = [0x00].freeze
16
- AGGREGATED_SIGNATURE_SPARSE_MASK = [0x01].freeze
17
-
18
- def generate_unique_uuid(uuid_1, uuid_2)
19
- md5 = Digest::MD5.new
20
- md5 << [uuid_1, uuid_2].min
21
- md5 << [uuid_1, uuid_2].max
22
- digest = md5.digest
23
- digest6 = (digest[6].ord & 0x0f | 0x30).chr
24
- digest8 = (digest[8].ord & 0x3f | 0x80).chr
25
- cipher = digest[0...6] + digest6 + digest[7] + digest8 + digest[9..]
26
-
27
- UUID.new(raw: cipher).unpacked
28
- end
29
-
30
- def unique_uuid(*uuids)
31
- uuids.sort
32
- r = uuids.first
33
- uuids.each_with_index do |uuid, i|
34
- r = MixinBot::Utils.generate_unique_uuid(r, uuid) if i.positive?
35
- end
36
-
37
- r
38
- end
39
-
40
- def generate_trace_from_hash(hash, output_index = 0)
41
- md5 = Digest::MD5.new
42
- md5 << hash
43
- md5 << [output_index].pack('c*') if output_index.positive? && output_index < 256
44
- digest = md5.digest
45
- digest[6] = ((digest[6].ord & 0x0f) | 0x30).chr
46
- digest[8] = ((digest[8].ord & 0x3f) | 0x80).chr
47
-
48
- UUID.new(raw: digest).unpacked
49
- end
50
-
51
- def hex_to_uuid(hex)
52
- UUID.new(hex: hex).unpacked
53
- end
54
-
55
- def sign_raw_transaction(tx)
56
- tx = JSON.parse tx if tx.is_a? String
57
- raise ArgumentError, "#{tx} is not a valid json" unless tx.is_a? Hash
58
-
59
- tx = tx.with_indifferent_access
60
-
61
- Transaction.new(
62
- asset: tx[:asset],
63
- inputs: tx[:inputs],
64
- outputs: tx[:outputs],
65
- extra: tx[:extra]
66
- ).encode.hex
67
- end
68
-
69
- def decode_raw_transaction(hex)
70
- Transaction.new(hex: hex).decode.to_h
71
- end
72
-
73
- def nft_memo(collection, token, extra)
74
- MixinBot::Utils::Nfo.new(
75
- collection: collection,
76
- token: token,
77
- extra: extra
78
- ).mint_memo
79
- end
80
-
81
- def encode_int(int)
82
- raise ArgumentError, "only support int #{int}" unless int.is_a?(Integer)
83
- raise ArgumentError, "int #{int} is larger than MAX_ENCODE_INT #{MAX_ENCODE_INT}" if int > MAX_ENCODE_INT
84
-
85
- [int].pack('S*').bytes.reverse
86
- end
87
-
88
- def encode_unit_64(int)
89
- [int].pack('Q*').bytes.reverse
90
- end
91
-
92
- def bytes_of(int)
93
- raise ArgumentError, 'not integer' unless int.is_a?(Integer)
94
-
95
- bytes = []
96
- loop do
97
- break if int === 0
98
-
99
- bytes.push int & 255
100
- int = int / (2**8) | 0
101
- end
102
-
103
- bytes.reverse
104
- end
105
-
106
- def bytes_to_int(bytes)
107
- int = 0
108
- bytes.each do |byte|
109
- int = int * (2**8) + byte
110
- end
111
-
112
- int
113
- end
114
- end
10
+ extend MixinBot::Utils::Address
11
+ extend MixinBot::Utils::Crypto
12
+ extend MixinBot::Utils::Decoder
13
+ extend MixinBot::Utils::Encoder
115
14
  end
116
15
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MixinBot
4
+ class UUID
5
+ attr_accessor :hex, :raw
6
+
7
+ def initialize(**args)
8
+ @hex = args[:hex]
9
+ @raw = args[:raw]
10
+
11
+ raise MixinBot::InvalidUuidFormatError if raw.present? && raw.size != 16
12
+ raise MixinBot::InvalidUuidFormatError if hex.present? && hex.gsub('-', '').size != 32
13
+ end
14
+
15
+ def packed
16
+ if raw.present?
17
+ raw
18
+ elsif hex.present?
19
+ [hex.gsub('-', '')].pack('H*')
20
+ end
21
+ end
22
+
23
+ def unpacked
24
+ _hex =
25
+ if hex.present?
26
+ hex.gsub('-', '')
27
+ elsif raw.present?
28
+ _hex = raw.unpack1('H*')
29
+ end
30
+
31
+ format(
32
+ '%<first>s-%<second>s-%<third>s-%<forth>s-%<fifth>s',
33
+ first: _hex[0..7],
34
+ second: _hex[8..11],
35
+ third: _hex[12..15],
36
+ forth: _hex[16..19],
37
+ fifth: _hex[20..]
38
+ )
39
+ end
40
+ end
41
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MixinBot
4
- VERSION = '0.12.1'
4
+ VERSION = '1.0.0'
5
5
  end
data/lib/mixin_bot.rb CHANGED
@@ -3,12 +3,16 @@
3
3
  # third-party dependencies
4
4
  require 'English'
5
5
  require 'active_support/all'
6
+ require 'base58'
6
7
  require 'base64'
7
8
  require 'bigdecimal'
8
9
  require 'bigdecimal/util'
9
10
  require 'digest'
11
+ require 'digest/blake3'
10
12
  require 'faye/websocket'
11
- require 'http'
13
+ require 'faraday'
14
+ require 'faraday/multipart'
15
+ require 'faraday/retry'
12
16
  require 'jose'
13
17
  require 'msgpack'
14
18
  require 'open3'
@@ -16,19 +20,38 @@ require 'openssl'
16
20
  require 'rbnacl'
17
21
  require 'sha3'
18
22
 
19
- require_relative './mixin_bot/api'
20
- require_relative './mixin_bot/cli'
21
- require_relative './mixin_bot/utils'
22
- require_relative './mixin_bot/version'
23
- require_relative './mvm'
23
+ require_relative 'mixin_bot/api'
24
+ require_relative 'mixin_bot/cli'
25
+ require_relative 'mixin_bot/utils'
26
+ require_relative 'mixin_bot/nfo'
27
+ require_relative 'mixin_bot/uuid'
28
+ require_relative 'mixin_bot/transaction'
29
+ require_relative 'mixin_bot/version'
30
+ require_relative 'mvm'
24
31
 
25
32
  module MixinBot
26
- class<< self
27
- attr_accessor :client_id, :client_secret, :session_id, :pin_token, :private_key, :scope, :api_host, :blaze_host
28
- end
33
+ class << self
34
+ def api
35
+ return @api if defined?(@api)
36
+
37
+ @api = MixinBot::API.new
38
+ @api
39
+ end
40
+
41
+ def config
42
+ return @config if defined?(@config)
43
+
44
+ @config = MixinBot::Configuration.new
45
+ @config
46
+ end
47
+
48
+ def configure(&)
49
+ config.instance_exec(&)
50
+ end
29
51
 
30
- def self.api
31
- @api ||= MixinBot::API.new
52
+ def utils
53
+ MixinBot::Utils
54
+ end
32
55
  end
33
56
 
34
57
  class Error < StandardError; end
@@ -37,12 +60,14 @@ module MixinBot
37
60
  class RequestError < Error; end
38
61
  class ResponseError < Error; end
39
62
  class NotFoundError < Error; end
63
+ class UserNotFoundError < Error; end
40
64
  class UnauthorizedError < Error; end
41
65
  class ForbiddenError < Error; end
42
66
  class InsufficientBalanceError < Error; end
43
67
  class InsufficientPoolError < Error; end
44
68
  class PinError < Error; end
45
- class InvalidNfoFormatError < MixinBot::Error; end
46
- class InvalidUuidFormatError < MixinBot::Error; end
47
- class InvalidTransactionFormatError < MixinBot::Error; end
69
+ class InvalidNfoFormatError < Error; end
70
+ class InvalidUuidFormatError < Error; end
71
+ class InvalidTransactionFormatError < Error; end
72
+ class ConfigurationNotValidError < Error; end
48
73
  end
data/lib/mvm/bridge.rb CHANGED
@@ -16,27 +16,10 @@ module MVM
16
16
  path = '/users'
17
17
 
18
18
  payload = {
19
- public_key: public_key
19
+ public_key:
20
20
  }
21
21
 
22
- client.post path, json: payload
23
- end
24
-
25
- def extra(receivers: [], threshold: 1, extra: '')
26
- return if receivers.blank?
27
-
28
- path = '/extra'
29
-
30
- payload = {
31
- receivers: receivers,
32
- threshold: threshold,
33
- extra: extra
34
- }
35
-
36
- client.post path, json: payload
37
- end
38
-
39
- def mirror
22
+ client.post path, **payload
40
23
  end
41
24
  end
42
25
  end
data/lib/mvm/client.rb CHANGED
@@ -8,43 +8,21 @@ module MVM
8
8
 
9
9
  def initialize(host)
10
10
  @host = host
11
- end
12
-
13
- def get(path, options = {})
14
- request(:get, path, options)
15
- end
16
-
17
- def post(path, options = {})
18
- request(:post, path, options)
19
- end
20
-
21
- private
22
-
23
- def request(verb, path, options = {})
24
- uri = uri_for path
25
-
26
- options[:headers] ||= {}
27
- options[:headers]['Content-Type'] ||= 'application/json'
28
-
29
- begin
30
- response = HTTP.timeout(connect: 5, write: 5, read: 5).request(verb, uri, options)
31
- rescue HTTP::Error => e
32
- raise HttpError, e.message
11
+ @conn = Faraday.new(url: "#{SERVER_SCHEME}://#{host}") do |f|
12
+ f.request :json
13
+ f.request :retry
14
+ f.response :raise_error
15
+ f.response :logger
16
+ f.response :json
33
17
  end
18
+ end
34
19
 
35
- raise ResponseError, response.to_s if response.status.server_error?
36
- return response.status.to_s unless response.status.success?
37
-
38
- JSON.parse(response.body.to_s)
20
+ def get(path, **options)
21
+ @conn.get(path, **options).body
39
22
  end
40
23
 
41
- def uri_for(path)
42
- uri_options = {
43
- scheme: SERVER_SCHEME,
44
- host: host,
45
- path: path
46
- }
47
- Addressable::URI.new(uri_options)
24
+ def post(path, **options)
25
+ @conn.post(path, **options).body
48
26
  end
49
27
  end
50
28
  end
data/lib/mvm/nft.rb CHANGED
@@ -6,14 +6,14 @@ module MVM
6
6
 
7
7
  def initialize(rpc_url: MVM::RPC_URL, mirror_address: MVM::MIRROR_ADDRESS)
8
8
  @rpc = Eth::Client.create rpc_url
9
- @mirror = Eth::Contract.from_abi name: 'Mirror', address: mirror_address, abi: File.open(File.expand_path('./abis/mirror.json', __dir__)).read
9
+ @mirror = Eth::Contract.from_abi name: 'Mirror', address: mirror_address, abi: File.read(File.expand_path('./abis/mirror.json', __dir__))
10
10
  end
11
11
 
12
12
  def collection_from_contract(address)
13
13
  collection = @rpc.call @mirror, 'collections', address
14
14
  return if collection.zero?
15
15
 
16
- MixinBot::Utils::UUID.new(hex: collection.to_fs(16)).unpacked
16
+ MixinBot::UUID.new(hex: collection.to_fs(16)).unpacked
17
17
  end
18
18
 
19
19
  def contract_from_collection(uuid)
@@ -29,7 +29,7 @@ module MVM
29
29
  address = contract_from_collection collection_id
30
30
  return if address.blank? || address.to_i(16).zero?
31
31
 
32
- contract = Eth::Contract.from_abi name: 'Collectible', address: address, abi: File.open(File.expand_path('./abis/erc721.json', __dir__)).read
32
+ contract = Eth::Contract.from_abi name: 'Collectible', address:, abi: File.read(File.expand_path('./abis/erc721.json', __dir__))
33
33
  owner = @rpc.call contract, 'ownerOf', token_id.to_i
34
34
  address = Eth::Address.new owner
35
35
  return unless address.valid?
@@ -40,7 +40,7 @@ module MVM
40
40
  end
41
41
 
42
42
  def token_of_owner_by_index(contract, owner, index)
43
- contract = Eth::Contract.from_abi name: 'Collectible', address: contract, abi: File.open(File.expand_path('./abis/erc721.json', __dir__)).read
43
+ contract = Eth::Contract.from_abi name: 'Collectible', address: contract, abi: File.read(File.expand_path('./abis/erc721.json', __dir__))
44
44
 
45
45
  @rpc.call contract, 'tokenOfOwnerByIndex', owner, index
46
46
  rescue IOError