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.
- checksums.yaml +4 -4
- data/lib/mixin_bot/api/address.rb +21 -0
- data/lib/mixin_bot/api/app.rb +5 -11
- data/lib/mixin_bot/api/asset.rb +9 -16
- data/lib/mixin_bot/api/attachment.rb +27 -22
- data/lib/mixin_bot/api/auth.rb +29 -51
- data/lib/mixin_bot/api/blaze.rb +4 -3
- data/lib/mixin_bot/api/collectible.rb +60 -58
- data/lib/mixin_bot/api/conversation.rb +29 -49
- data/lib/mixin_bot/api/encrypted_message.rb +17 -17
- data/lib/mixin_bot/api/legacy_multisig.rb +87 -0
- data/lib/mixin_bot/api/legacy_output.rb +50 -0
- data/lib/mixin_bot/api/legacy_payment.rb +31 -0
- data/lib/mixin_bot/api/legacy_snapshot.rb +39 -0
- data/lib/mixin_bot/api/legacy_transaction.rb +173 -0
- data/lib/mixin_bot/api/legacy_transfer.rb +42 -0
- data/lib/mixin_bot/api/me.rb +13 -17
- data/lib/mixin_bot/api/message.rb +13 -10
- data/lib/mixin_bot/api/multisig.rb +16 -221
- data/lib/mixin_bot/api/output.rb +46 -0
- data/lib/mixin_bot/api/payment.rb +9 -20
- data/lib/mixin_bot/api/pin.rb +57 -65
- data/lib/mixin_bot/api/rpc.rb +9 -11
- data/lib/mixin_bot/api/snapshot.rb +15 -29
- data/lib/mixin_bot/api/tip.rb +43 -0
- data/lib/mixin_bot/api/transaction.rb +184 -60
- data/lib/mixin_bot/api/transfer.rb +64 -32
- data/lib/mixin_bot/api/user.rb +83 -53
- data/lib/mixin_bot/api/withdraw.rb +52 -53
- data/lib/mixin_bot/api.rb +78 -45
- data/lib/mixin_bot/cli/api.rb +149 -5
- data/lib/mixin_bot/cli/utils.rb +14 -4
- data/lib/mixin_bot/cli.rb +13 -10
- data/lib/mixin_bot/client.rb +76 -127
- data/lib/mixin_bot/configuration.rb +98 -0
- data/lib/mixin_bot/nfo.rb +174 -0
- data/lib/mixin_bot/transaction.rb +505 -0
- data/lib/mixin_bot/utils/address.rb +108 -0
- data/lib/mixin_bot/utils/crypto.rb +182 -0
- data/lib/mixin_bot/utils/decoder.rb +58 -0
- data/lib/mixin_bot/utils/encoder.rb +63 -0
- data/lib/mixin_bot/utils.rb +8 -109
- data/lib/mixin_bot/uuid.rb +41 -0
- data/lib/mixin_bot/version.rb +1 -1
- data/lib/mixin_bot.rb +39 -14
- data/lib/mvm/bridge.rb +2 -19
- data/lib/mvm/client.rb +11 -33
- data/lib/mvm/nft.rb +4 -4
- data/lib/mvm/registry.rb +9 -9
- data/lib/mvm/scan.rb +3 -5
- data/lib/mvm.rb +5 -6
- metadata +101 -44
- data/lib/mixin_bot/utils/nfo.rb +0 -176
- data/lib/mixin_bot/utils/transaction.rb +0 -478
- 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 '
|
4
|
-
require_relative '
|
5
|
-
require_relative '
|
6
|
-
require_relative '
|
7
|
-
require_relative '
|
8
|
-
require_relative '
|
9
|
-
require_relative '
|
10
|
-
require_relative '
|
11
|
-
require_relative '
|
12
|
-
require_relative '
|
13
|
-
require_relative '
|
14
|
-
require_relative '
|
15
|
-
require_relative '
|
16
|
-
require_relative '
|
17
|
-
require_relative '
|
18
|
-
require_relative '
|
19
|
-
require_relative '
|
20
|
-
require_relative '
|
21
|
-
require_relative '
|
22
|
-
require_relative '
|
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 :
|
27
|
-
|
28
|
-
def initialize(
|
29
|
-
@
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
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
|
51
|
-
|
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
|
-
|
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
|
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
|
data/lib/mixin_bot/cli/api.rb
CHANGED
@@ -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:
|
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: {
|
44
|
+
api_instance.client.post(path, headers: { Authorization: authorization }, json: payload)
|
45
45
|
when :get
|
46
|
-
api_instance.client.get(path, headers: {
|
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 :
|
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[:
|
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
|
data/lib/mixin_bot/cli/utils.rb
CHANGED
@@ -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
|
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
|
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
|
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
|
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 '
|
9
|
-
require_relative '
|
10
|
-
require_relative '
|
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
|
-
|
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
|
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
|
-
|
54
|
+
app_id: @keystore['app_id'] || @keystore['client_id'],
|
52
55
|
session_id: @keystore['session_id'],
|
53
|
-
|
54
|
-
|
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
|
|
data/lib/mixin_bot/client.rb
CHANGED
@@ -4,153 +4,102 @@ module MixinBot
|
|
4
4
|
class Client
|
5
5
|
SERVER_SCHEME = 'https'
|
6
6
|
|
7
|
-
attr_reader :
|
8
|
-
|
9
|
-
def initialize(
|
10
|
-
@
|
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,
|
14
|
-
request(:get, path,
|
25
|
+
def get(path, *, **)
|
26
|
+
request(:get, path, *, **)
|
15
27
|
end
|
16
28
|
|
17
|
-
def post(path,
|
18
|
-
request(:post, path,
|
29
|
+
def post(path, *, **)
|
30
|
+
request(:post, path, *, **)
|
19
31
|
end
|
20
32
|
|
21
33
|
private
|
22
34
|
|
23
|
-
def request(verb, path,
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
-
|
46
|
+
kwargs.to_json
|
91
47
|
end
|
92
48
|
else
|
93
|
-
|
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
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
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
|
-
|
117
|
-
result = JSON.parse(response&.body&.to_s)
|
118
|
-
result && yield(:json, result)
|
74
|
+
result = response.body
|
119
75
|
|
120
|
-
|
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
|
-
|
124
|
-
|
125
|
-
|
126
|
-
when
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
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
|
-
|
101
|
+
raise ResponseError, errmsg
|
151
102
|
end
|
152
|
-
|
153
|
-
yield(parse_as, result)
|
154
103
|
end
|
155
104
|
end
|
156
105
|
end
|