mixin_bot 0.12.1 → 1.0.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.
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