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
data/lib/mixin_bot/api.rb CHANGED
@@ -1,62 +1,86 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './client'
4
- require_relative './api/app'
5
- require_relative './api/asset'
6
- require_relative './api/attachment'
7
- require_relative './api/auth'
8
- require_relative './api/blaze'
9
- require_relative './api/collectible'
10
- require_relative './api/conversation'
11
- require_relative './api/encrypted_message'
12
- require_relative './api/me'
13
- require_relative './api/message'
14
- require_relative './api/multisig'
15
- require_relative './api/payment'
16
- require_relative './api/pin'
17
- require_relative './api/rpc'
18
- require_relative './api/snapshot'
19
- require_relative './api/transaction'
20
- require_relative './api/transfer'
21
- require_relative './api/user'
22
- require_relative './api/withdraw'
3
+ require_relative 'client'
4
+ require_relative 'configuration'
5
+ require_relative 'api/address'
6
+ require_relative 'api/app'
7
+ require_relative 'api/asset'
8
+ require_relative 'api/attachment'
9
+ require_relative 'api/auth'
10
+ require_relative 'api/blaze'
11
+ require_relative 'api/collectible'
12
+ require_relative 'api/conversation'
13
+ require_relative 'api/encrypted_message'
14
+ require_relative 'api/legacy_multisig'
15
+ require_relative 'api/legacy_output'
16
+ require_relative 'api/legacy_payment'
17
+ require_relative 'api/legacy_snapshot'
18
+ require_relative 'api/legacy_transaction'
19
+ require_relative 'api/legacy_transfer'
20
+ require_relative 'api/me'
21
+ require_relative 'api/message'
22
+ require_relative 'api/multisig'
23
+ require_relative 'api/output'
24
+ require_relative 'api/payment'
25
+ require_relative 'api/pin'
26
+ require_relative 'api/rpc'
27
+ require_relative 'api/snapshot'
28
+ require_relative 'api/tip'
29
+ require_relative 'api/transaction'
30
+ require_relative 'api/transfer'
31
+ require_relative 'api/user'
32
+ require_relative 'api/withdraw'
23
33
 
24
34
  module MixinBot
25
35
  class API
26
- attr_reader :client_id, :client_secret, :session_id, :pin_token, :private_key, :client, :blaze_host, :key_type
27
-
28
- def initialize(options = {})
29
- @client_id = options[:client_id] || MixinBot.client_id
30
- @client_secret = options[:client_secret] || MixinBot.client_secret
31
- @session_id = options[:session_id] || MixinBot.session_id
32
- @client = Client.new(MixinBot.api_host || 'api.mixin.one')
33
- @blaze_host = MixinBot.blaze_host || 'blaze.mixin.one'
34
- @pin_token =
35
- begin
36
- Base64.urlsafe_decode64 options[:pin_token] || MixinBot.pin_token
37
- rescue StandardError
38
- ''
36
+ attr_reader :config, :client
37
+
38
+ def initialize(**kwargs)
39
+ @config =
40
+ if kwargs.present?
41
+ MixinBot::Configuration.new(**kwargs)
42
+ else
43
+ MixinBot.config
39
44
  end
40
- _private_key = options[:private_key] || MixinBot.private_key
41
- if /^-----BEGIN RSA PRIVATE KEY-----/.match? _private_key
42
- @private_key = _private_key.gsub('\\r\\n', "\n").gsub("\r\n", "\n")
43
- @key_type = :rsa
44
- else
45
- @private_key = Base64.urlsafe_decode64 _private_key
46
- @key_type = :ed25519
47
- end
45
+
46
+ @client = Client.new(@config)
47
+ end
48
+
49
+ def utils
50
+ MixinBot::Utils
51
+ end
52
+
53
+ def client_id
54
+ config.app_id
48
55
  end
49
56
 
50
- def sign_raw_transaction(tx)
51
- MixinBot::Utils.sign_raw_transaction tx
57
+ def access_token(method, uri, body, **kwargs)
58
+ utils.access_token(
59
+ method,
60
+ uri,
61
+ body,
62
+ exp_in: kwargs.delete(:exp_in) || 600,
63
+ scp: kwargs.delete(:scp) || 'FULL',
64
+ app_id: config.app_id,
65
+ session_id: config.session_id,
66
+ private_key: config.session_private_key
67
+ )
68
+ end
69
+
70
+ def encode_raw_transaction(tx)
71
+ utils.encode_raw_transaction tx
52
72
  end
53
73
 
54
74
  def decode_raw_transaction(raw)
55
- MixinBot::Utils.decode_raw_transaction raw
75
+ utils.decode_raw_transaction raw
76
+ end
77
+
78
+ def generate_trace_from_hash(hash, output_index = 0)
79
+ utils.generate_trace_from_hash hash, output_index
56
80
  end
57
81
 
58
82
  # Use a mixin software to implement transaction build
59
- def sign_raw_transaction_native(json)
83
+ def encode_raw_transaction_native(json)
60
84
  ensure_mixin_command_exist
61
85
  command = format("mixin signrawtransaction --raw '%<arg>s'", arg: json)
62
86
 
@@ -77,6 +101,7 @@ module MixinBot
77
101
  JSON.parse output.chomp
78
102
  end
79
103
 
104
+ include MixinBot::API::Address
80
105
  include MixinBot::API::App
81
106
  include MixinBot::API::Asset
82
107
  include MixinBot::API::Attachment
@@ -85,13 +110,21 @@ module MixinBot
85
110
  include MixinBot::API::Collectible
86
111
  include MixinBot::API::Conversation
87
112
  include MixinBot::API::EncryptedMessage
113
+ include MixinBot::API::LegacyMultisig
114
+ include MixinBot::API::LegacyOutput
115
+ include MixinBot::API::LegacyPayment
116
+ include MixinBot::API::LegacySnapshot
117
+ include MixinBot::API::LegacyTransaction
118
+ include MixinBot::API::LegacyTransfer
88
119
  include MixinBot::API::Me
89
120
  include MixinBot::API::Message
90
121
  include MixinBot::API::Multisig
122
+ include MixinBot::API::Output
91
123
  include MixinBot::API::Payment
92
124
  include MixinBot::API::Pin
93
125
  include MixinBot::API::Rpc
94
126
  include MixinBot::API::Snapshot
127
+ include MixinBot::API::Tip
95
128
  include MixinBot::API::Transaction
96
129
  include MixinBot::API::Transfer
97
130
  include MixinBot::API::User
@@ -34,16 +34,16 @@ module MixinBot
34
34
  end
35
35
 
36
36
  access_token = options[:accesstoken] || api_instance.access_token(options[:method].upcase, path, payload.blank? ? '' : payload.to_json)
37
- authorization = format('Bearer %<access_token>s', access_token: access_token)
37
+ authorization = format('Bearer %<access_token>s', access_token:)
38
38
  res = {}
39
39
 
40
40
  CLI::UI::Spinner.spin("#{options[:method]} #{path}, payload: #{payload}") do |_spinner|
41
41
  res =
42
42
  case options[:method].downcase.to_sym
43
43
  when :post
44
- api_instance.client.post(path, headers: { 'Authorization': authorization }, json: payload)
44
+ api_instance.client.post(path, headers: { Authorization: authorization }, json: payload)
45
45
  when :get
46
- api_instance.client.get(path, headers: { 'Authorization': authorization })
46
+ api_instance.client.get(path, headers: { Authorization: authorization })
47
47
  end
48
48
  end
49
49
 
@@ -52,19 +52,163 @@ module MixinBot
52
52
 
53
53
  desc 'authcode', 'code to authorize other mixin account'
54
54
  option :keystore, type: :string, aliases: '-k', required: true, desc: 'keystore or keystore.json file path'
55
- option :client_id, type: :string, required: true, aliases: '-c', desc: 'client_id of bot to authorize'
55
+ option :app_id, type: :string, required: true, aliases: '-c', desc: 'app_id of bot to authorize'
56
56
  option :scope, type: :array, default: ['PROFILE:READ'], aliases: '-s', desc: 'scope to authorize'
57
57
  def authcode
58
58
  res = {}
59
59
  CLI::UI::Spinner.spin('POST /oauth/authorize') do |_spinner|
60
60
  res =
61
61
  api_instance.authorize_code(
62
- user_id: options[:client_id],
62
+ user_id: options[:app_id],
63
63
  scope: options[:scope],
64
64
  pin: keystore['pin']
65
65
  )
66
66
  end
67
67
  log res['data']
68
68
  end
69
+
70
+ desc 'updatetip PIN', 'update TIP pin'
71
+ option :keystore, type: :string, aliases: '-k', required: true, desc: 'keystore or keystore.json file path'
72
+ def updatetip(pin)
73
+ profile = api_instance.me
74
+ log UI.fmt "{{v}} #{profile['full_name']}, TIP counter: #{profile['tip_counter']}"
75
+
76
+ counter = profile['tip_counter']
77
+ key = api_instance.prepare_tip_key counter
78
+ log UI.fmt "{{v}} Generated key: #{key[:private_key]}"
79
+
80
+ res = api_instance.update_pin old_pin: pin.to_s, pin: key[:public_key]
81
+
82
+ log({
83
+ pin: key[:private_key],
84
+ tip_key_base64: res['tip_key_base64']
85
+ })
86
+ rescue StandardError => e
87
+ log UI.fmt "{{x}} #{e.inspect}"
88
+ end
89
+
90
+ desc 'verifypin PIN', 'verify pin'
91
+ option :keystore, type: :string, aliases: '-k', required: true, desc: 'keystore or keystore.json file path'
92
+ def verifypin(pin)
93
+ res = api_instance.verify_pin pin.to_s
94
+
95
+ log res
96
+ rescue StandardError => e
97
+ log UI.fmt "{{x}} #{e.inspect}"
98
+ end
99
+
100
+ desc 'transfer USER_ID', 'transfer asset to USER_ID'
101
+ option :asset, type: :string, required: true, desc: 'Asset ID'
102
+ option :amount, type: :numeric, required: true, desc: 'Amount'
103
+ option :memo, type: :string, required: false, desc: 'memo'
104
+ option :keystore, type: :string, aliases: '-k', required: true, desc: 'keystore or keystore.json file path'
105
+ def transfer(user_id)
106
+ res = {}
107
+
108
+ CLI::UI::Spinner.spin "Try to transfer #{options[:amount]} #{options[:asset]} to #{user_id}" do |_spinner|
109
+ res = api_instance.create_transfer(
110
+ keystore['pin'],
111
+ {
112
+ asset_id: options[:asset],
113
+ opponent_id: user_id,
114
+ amount: options[:amount],
115
+ memo: options[:memo]
116
+ }
117
+ )
118
+ end
119
+
120
+ return unless res['snapshot_id'].present?
121
+
122
+ log UI.fmt "{{v}} Finished: https://mixin.one/snapshots/#{res['snapshot_id']}"
123
+ end
124
+
125
+ desc 'saferegister', 'register SAFE network'
126
+ option :spend_key, type: :string, required: true, desc: 'spend_key'
127
+ option :keystore, type: :string, aliases: '-k', required: true, desc: 'keystore or keystore.json file path'
128
+ def saferegister
129
+ res = api_instance.safe_register options[:spend_key]
130
+ log res
131
+ end
132
+
133
+ desc 'pay', 'generate payment url'
134
+ option :members, type: :array, required: true, desc: 'Reveivers, maybe multisig'
135
+ option :threshold, type: :numeric, required: false, default: 1, desc: 'Threshold of multisig'
136
+ option :asset, type: :string, required: true, desc: 'Asset ID'
137
+ option :amount, type: :numeric, required: true, desc: 'Amount'
138
+ option :trace, type: :string, required: false, desc: 'Trace ID'
139
+ option :memo, type: :string, required: false, desc: 'memo'
140
+ def pay
141
+ url = api_instance.safe_pay_url(
142
+ members: options[:members],
143
+ threshold: options[:threshold],
144
+ asset_id: options[:asset],
145
+ amount: options[:amount],
146
+ trace_id: options[:trace],
147
+ memo: options[:memo]
148
+ )
149
+
150
+ log UI.fmt "{{v}} #{url}"
151
+ end
152
+
153
+ desc 'safetransfer USER_ID', 'transfer asset to USER_ID with SAFE network'
154
+ option :asset, type: :string, required: true, desc: 'Asset ID'
155
+ option :amount, type: :numeric, required: true, desc: 'Amount'
156
+ option :trace, type: :string, required: false, desc: 'Trace ID'
157
+ option :memo, type: :string, required: false, desc: 'memo'
158
+ option :keystore, type: :string, aliases: '-k', required: true, desc: 'keystore or keystore.json file path'
159
+ def safetransfer(user_id)
160
+ amount = options[:amount].to_d
161
+ asset = options[:asset]
162
+ memo = options[:memo] || ''
163
+
164
+ # step 1: select inputs
165
+ outputs = api_instance.safe_outputs(state: 'unspent', asset_id: asset, limit: 500)['data'].sort_by { |o| o['amount'].to_d }
166
+ balance = outputs.sum(&->(output) { output['amount'].to_d })
167
+
168
+ utxos = []
169
+ outputs.each do |output|
170
+ break if utxos.sum { |o| o['amount'].to_d } >= amount
171
+
172
+ utxos.shift if utxos.size >= 255
173
+ utxos << output
174
+ end
175
+
176
+ log UI.fmt "Step 1/7: {{v}} Found #{outputs.count} unspent outputs, balance: #{balance}, selected #{utxos.count} outputs"
177
+
178
+ # step 2: build transaction
179
+ tx = api_instance.build_safe_transaction(
180
+ utxos:,
181
+ receivers: [
182
+ members: [user_id],
183
+ threshold: 1,
184
+ amount:
185
+ ],
186
+ extra: memo
187
+ )
188
+ raw = MixinBot::Utils.encode_raw_transaction tx
189
+ log UI.fmt "Step 2/5: {{v}} Built raw: #{raw}"
190
+
191
+ # step 3: verify transaction
192
+ request_id = SecureRandom.uuid
193
+ request = api_instance.create_safe_transaction_request(request_id, raw)['data']
194
+ log UI.fmt "Step 3/5: {{v}} Verified transaction, request_id: #{request[0]['request_id']}"
195
+
196
+ # step 4: sign transaction
197
+ signed_raw = api_instance.sign_safe_transaction(
198
+ raw:,
199
+ utxos:,
200
+ request: request[0]
201
+ )
202
+ log UI.fmt "Step 4/5: {{v}} Signed transaction: #{signed_raw}"
203
+
204
+ # step 5: submit transaction
205
+ r = api_instance.send_safe_transaction(
206
+ request_id,
207
+ signed_raw
208
+ )
209
+ log UI.fmt "Step 5/5: {{v}} Submit transaction, hash: #{r['data'].first['transaction_hash']}"
210
+ rescue StandardError => e
211
+ log UI.fmt "{{x}} #{e.inspect}"
212
+ end
69
213
  end
70
214
  end
@@ -11,17 +11,17 @@ module MixinBot
11
11
 
12
12
  desc 'unique UUIDS', 'generate unique UUID for two or more UUIDs'
13
13
  def unique(*uuids)
14
- log MixinBot::Utils.unique_uuid(*uuids)
14
+ log MixinBot.utils.unique_uuid(*uuids)
15
15
  end
16
16
 
17
17
  desc 'generatetrace HASH', 'generate trace ID from Tx hash'
18
18
  def generatetrace(hash)
19
- log MixinBot::Utils.generate_trace_from_hash(hash)
19
+ log MixinBot.utils.generate_trace_from_hash(hash)
20
20
  end
21
21
 
22
22
  desc 'decodetx TRANSACTION', 'decode raw transaction'
23
23
  def decodetx(transaction)
24
- log MixinBot::Utils.decode_raw_transaction(transaction)
24
+ log MixinBot.utils.decode_raw_transaction(transaction)
25
25
  end
26
26
 
27
27
  desc 'nftmemo', 'memo for mint NFT'
@@ -29,7 +29,17 @@ module MixinBot
29
29
  option :token, type: :numeric, required: true, aliases: '-t', desc: 'Token ID, Integer'
30
30
  option :hash, type: :string, required: true, aliases: '-h', desc: 'Hash of NFT metadata, 256-bit string'
31
31
  def nftmemo
32
- log MixinBot::Utils.nft(options[:collection], options[:token], options[:hash])
32
+ log MixinBot.utils.nft(options[:collection], options[:token], options[:hash])
33
+ end
34
+
35
+ desc 'rsa', 'generate RSA key'
36
+ def rsa
37
+ log MixinBot.utils.generate_rsa_key
38
+ end
39
+
40
+ desc 'ed25519', 'generate Ed25519 key'
41
+ def ed25519
42
+ log MixinBot.utils.generate_ed25519_key
33
43
  end
34
44
  end
35
45
  end
data/lib/mixin_bot/cli.rb CHANGED
@@ -5,9 +5,9 @@ require 'cli/ui'
5
5
  require 'thor'
6
6
  require 'yaml'
7
7
  require 'json'
8
- require_relative './cli/api'
9
- require_relative './cli/node'
10
- require_relative './cli/utils'
8
+ require_relative 'cli/api'
9
+ require_relative 'cli/node'
10
+ require_relative 'cli/utils'
11
11
 
12
12
  module MixinBot
13
13
  class CLI < Thor
@@ -21,7 +21,10 @@ module MixinBot
21
21
 
22
22
  def initialize(*args)
23
23
  super
24
- return if options[:keystore].blank?
24
+ if options[:keystore].blank?
25
+ @api_instance = MixinBot::API.new
26
+ return
27
+ end
25
28
 
26
29
  keystore =
27
30
  if File.file? options[:keystore]
@@ -33,7 +36,7 @@ module MixinBot
33
36
  @keystore =
34
37
  begin
35
38
  JSON.parse keystore
36
- rescue JSON::ParserError => e
39
+ rescue JSON::ParserError
37
40
  log UI.fmt(
38
41
  format(
39
42
  '{{x}} falied to parse keystore.json: %<keystore>s',
@@ -44,17 +47,17 @@ module MixinBot
44
47
 
45
48
  return unless @keystore
46
49
 
47
- MixinBot.api_host = options[:apihost]
50
+ MixinBot.config.api_host = options[:apihost]
48
51
  @api_instance ||=
49
52
  begin
50
53
  MixinBot::API.new(
51
- client_id: @keystore['client_id'],
54
+ app_id: @keystore['app_id'] || @keystore['client_id'],
52
55
  session_id: @keystore['session_id'],
53
- pin_token: @keystore['pin_token'],
54
- private_key: @keystore['private_key']
56
+ server_public_key: @keystore['server_public_key'] || @keystore['pin_token'],
57
+ session_private_key: @keystore['session_private_key'] || @keystore['private_key']
55
58
  )
56
59
  rescue StandardError => e
57
- log UI.fmt '{{x}}: Failed to initialize api, maybe your keystore is incorrect.'
60
+ log UI.fmt '{{x}}: Failed to initialize api, maybe your keystore is incorrect: %<error>s', error: e.message
58
61
  end
59
62
  end
60
63
 
@@ -4,153 +4,102 @@ module MixinBot
4
4
  class Client
5
5
  SERVER_SCHEME = 'https'
6
6
 
7
- attr_reader :host
8
-
9
- def initialize(host = 'api.mixin.one')
10
- @host = host
7
+ attr_reader :config, :conn
8
+
9
+ def initialize(config)
10
+ @config = config || MixinBot.config
11
+ @conn = Faraday.new(
12
+ url: "#{SERVER_SCHEME}://#{config.api_host}",
13
+ headers: {
14
+ 'Content-Type' => 'application/json',
15
+ 'User-Agent' => "mixin_bot/#{MixinBot::VERSION}"
16
+ }
17
+ ) do |f|
18
+ f.request :json
19
+ f.request :retry
20
+ f.response :json
21
+ f.response :logger if config.debug
22
+ end
11
23
  end
12
24
 
13
- def get(path, options = {})
14
- request(:get, path, options)
25
+ def get(path, *, **)
26
+ request(:get, path, *, **)
15
27
  end
16
28
 
17
- def post(path, options = {})
18
- request(:post, path, options)
29
+ def post(path, *, **)
30
+ request(:post, path, *, **)
19
31
  end
20
32
 
21
33
  private
22
34
 
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
33
- end
34
-
35
- raise RequestError, response.to_s unless response.status.success?
36
-
37
- parse_response(response) do |parse_as, result|
38
- case parse_as
39
- when :json
40
- if result['error'].nil?
41
- result.merge! result['data'] if result['data'].is_a? Hash
42
- break result
43
- end
35
+ def request(verb, path, *args, **kwargs)
36
+ access_token = kwargs.delete :access_token
37
+ exp_in = kwargs.delete(:exp_in) || 600
38
+ scp = kwargs.delete(:scp) || 'FULL'
44
39
 
45
- errmsg = "errcode: #{result['error']['code']}, errmsg: #{result['error']['description']}, request_id: #{response&.[]('X-Request-Id')}, server_time: #{response&.[]('X-Server-Time')}'"
46
-
47
- # status code description
48
- # 202 400 The request body can’t be pasred as valid data.
49
- # 202 401 Unauthorized.
50
- # 202 403 Forbidden.
51
- # 202 404 The endpoint is not found.
52
- # 202 429 Too Many Requests.
53
- # 202 10006 App update required.
54
- # 202 20116 The group chat is full.
55
- # 500 500 Internal Server Error.
56
- # 500 7000 Blaze server error.
57
- # 500 7001 The blaze operation timeout.
58
- # 202 10002 Illegal request paramters.
59
- # 202 20117 Insufficient balance。
60
- # 202 20118 PIN format error.
61
- # 202 20119 PIN error.
62
- # 202 20120 Transfer amount is too small.
63
- # 202 20121 Authorization code has expired.
64
- # 202 20124 Insufficient withdrawal fee.
65
- # 202 20125 The transfer has been paid by someone else.
66
- # 202 20127 The withdrawal amount is too small.
67
- # 202 20131 Withdrawal Memo format error.
68
- # 500 30100 The current asset's public chain synchronization error.
69
- # 500 30101 Wrong private key.
70
- # 500 30102 Wrong withdrawal address.
71
- # 500 30103 Insufficient pool.
72
- # 500 7000 WebSocket server error.
73
- # 500 7001 WebSocket operation timeout.
74
- case result['error']['code']
75
- when 401, 20121
76
- raise UnauthorizedError, errmsg
77
- when 403, 20116, 10002, 429
78
- raise ForbiddenError, errmsg
79
- when 404
80
- raise NotFoundError, errmsg
81
- when 400, 10006, 20133, 500, 7000, 7001
82
- raise ResponseError, errmsg
83
- when 20117
84
- raise InsufficientBalanceError, errmsg
85
- when 20118, 20119
86
- raise PinError, errmsg
87
- when 30103
88
- raise InsufficientPoolError, errmsg
40
+ kwargs.compact!
41
+ body =
42
+ if verb == :post
43
+ if args.present?
44
+ args.to_json
89
45
  else
90
- raise ResponseError, errmsg
46
+ kwargs.to_json
91
47
  end
92
48
  else
93
- result
49
+ ''
94
50
  end
95
- end
96
- end
97
-
98
- def uri_for(path)
99
- uri_options = {
100
- scheme: SERVER_SCHEME,
101
- host: host,
102
- path: path
103
- }
104
- Addressable::URI.new(uri_options)
105
- end
106
51
 
107
- def parse_response(response)
108
- content_type = response.headers[:content_type]
109
- parse_as = {
110
- %r{^application/json} => :json,
111
- %r{^image/.*} => :file,
112
- %r{^text/html} => :xml,
113
- %r{^text/plain} => :plain
114
- }.each_with_object([]) { |match, memo| memo << match[1] if content_type =~ match[0] }.first || :plain
52
+ path = "#{path}?#{URI.encode_www_form(kwargs.sort_by { |k, _v| k })}" if verb == :get && kwargs.present?
53
+ access_token ||=
54
+ MixinBot.utils.access_token(
55
+ verb.to_s.upcase,
56
+ path,
57
+ body,
58
+ exp_in:,
59
+ scp:,
60
+ app_id: config.app_id,
61
+ session_id: config.session_id,
62
+ private_key: config.session_private_key
63
+ )
64
+ authorization = format('Bearer %<access_token>s', access_token:)
65
+
66
+ response =
67
+ case verb
68
+ when :get
69
+ @conn.get path, nil, { Authorization: authorization }
70
+ when :post
71
+ @conn.post path, body, { Authorization: authorization }
72
+ end
115
73
 
116
- if parse_as == :plain
117
- result = JSON.parse(response&.body&.to_s)
118
- result && yield(:json, result)
74
+ result = response.body
119
75
 
120
- yield(:plain, response.body)
76
+ if result['error'].blank?
77
+ result.merge! result['data'] if result['data'].is_a? Hash
78
+ return result
121
79
  end
122
80
 
123
- case parse_as
124
- when :json
125
- result = JSON.parse(response.body.to_s)
126
- when :file
127
- extension =
128
- if response.headers[:content_type] =~ %r{^image/.*}
129
- {
130
- 'image/gif': '.gif',
131
- 'image/jpeg': '.jpg',
132
- 'image/png': '.png'
133
- }[response.headers['content-type']]
134
- else
135
- ''
136
- end
137
-
138
- begin
139
- file = Tempfile.new(['mixin-file-', extension])
140
- file.binmode
141
- file.write(response.body)
142
- ensure
143
- file&.close
144
- end
145
-
146
- result = file
147
- when :xml
148
- result = Hash.from_xml(response.body.to_s)
81
+ errmsg = "#{verb.upcase}|#{path}|#{body}, errcode: #{result['error']['code']}, errmsg: #{result['error']['description']}, request_id: #{response&.[]('X-Request-Id')}, server_time: #{response&.[]('X-Server-Time')}'"
82
+
83
+ case result['error']['code']
84
+ when 401, 20121
85
+ raise UnauthorizedError, errmsg
86
+ when 403, 20116, 10002, 429
87
+ raise ForbiddenError, errmsg
88
+ when 404
89
+ raise NotFoundError, errmsg
90
+ when 400, 10006, 20133, 500, 7000, 7001
91
+ raise ResponseError, errmsg
92
+ when 20117
93
+ raise InsufficientBalanceError, errmsg
94
+ when 20118, 20119
95
+ raise PinError, errmsg
96
+ when 30103
97
+ raise InsufficientPoolError, errmsg
98
+ when 10404
99
+ raise UserNotFoundError, errmsg
149
100
  else
150
- result = response.body
101
+ raise ResponseError, errmsg
151
102
  end
152
-
153
- yield(parse_as, result)
154
103
  end
155
104
  end
156
105
  end