mixin_bot 0.7.7 → 0.7.10

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a6b6a8ef2f385b184baa4c0228bfb9a97867cff65aa625618ce08a7ff0570237
4
- data.tar.gz: 4d4f11a16d0bf135c56768f5658ac20e80db652474a9a5b55cb3700f41092196
3
+ metadata.gz: 222522068617e93a42e0b59a81d8afc8908ed3569217c643d781ba4b721f639a
4
+ data.tar.gz: 8d15656e77f7176c91b3daf7d6fc4901c257db8005d1adf27d526913c1646286
5
5
  SHA512:
6
- metadata.gz: e1af0e4eb4050880c4ad2593045cc911a3cd078649f4a1cd90d7a820fec799cb9d758a70160af6b94b40ae9c04592c2a2ffa00159535d9b8954095a161ad1329
7
- data.tar.gz: 571922c76b219488eb406836cb2d293365d16abe71f4a2fb7039d69ee53b52b71578b2f604264dd0f604d13dcebb53a40db06addef52dfff34cd21941088ae94
6
+ metadata.gz: 5fb562d8867fb8b06723367e9d1a38d4d7bd6c9d6d7c1af7444283855b316d2f18b5aae365f27eb14be85a1630541c81e790cc7aae94bbf2ac21591e029e7f76
7
+ data.tar.gz: b722aa66007d94077ef6b963b260066cfd02074469f58f70f2959f4cf6e1c4c8aaae9c6d18e2bf45af2903a0443f76a3989b172250e4a8c958cea2fcffdc42b8
@@ -27,15 +27,16 @@ module MixinBot
27
27
  HTTP
28
28
  .timeout(connect: 5, write: 5, read: 5)
29
29
  .request(
30
- :put,
31
- attachment.delete('upload_url'),
30
+ :put,
31
+ attachment.delete('upload_url'),
32
32
  {
33
33
  body: file,
34
34
  headers: {
35
35
  'x-amz-acl': 'public-read',
36
- 'Content-Type': 'application/octet-stream',
36
+ 'Content-Type': 'application/octet-stream'
37
37
  }
38
- })
38
+ }
39
+ )
39
40
 
40
41
  attachment
41
42
  end
@@ -38,6 +38,7 @@ module MixinBot
38
38
  COLLECTABLE_REQUEST_ACTIONS = %i[sign unlock].freeze
39
39
  def create_collectible_request(action, raw, access_token: nil)
40
40
  raise ArgumentError, "request action is limited in #{COLLECTABLE_REQUEST_ACTIONS.join(', ')}" unless COLLECTABLE_REQUEST_ACTIONS.include? action.to_sym
41
+
41
42
  path = '/collectibles/requests'
42
43
  payload = {
43
44
  action: action,
@@ -107,7 +108,7 @@ module MixinBot
107
108
 
108
109
  kwargs = kwargs.with_indifferent_access
109
110
  collectible = kwargs['collectible']
110
- raise "collectible is spent" if collectible['state'] == 'spent'
111
+ raise 'collectible is spent' if collectible['state'] == 'spent'
111
112
 
112
113
  build_raw_transaction(
113
114
  utxos: [collectible],
@@ -187,9 +187,9 @@ module MixinBot
187
187
  if outputs.empty?
188
188
  receivers_threshold = 1 if receivers.size == 1
189
189
  output0 = build_output(
190
- receivers: receivers,
191
- index: 0,
192
- amount: amount,
190
+ receivers: receivers,
191
+ index: 0,
192
+ amount: amount,
193
193
  threshold: receivers_threshold,
194
194
  hint: hint
195
195
  )
@@ -197,8 +197,8 @@ module MixinBot
197
197
 
198
198
  if input_amount > amount
199
199
  output1 = build_output(
200
- receivers: senders,
201
- index: 1,
200
+ receivers: senders,
201
+ index: 1,
202
202
  amount: input_amount - amount,
203
203
  threshold: senders_threshold,
204
204
  hint: hint
@@ -207,7 +207,7 @@ module MixinBot
207
207
  end
208
208
  end
209
209
 
210
- extra = extra || Digest.hexencode(memo.to_s.slice(0, 140))
210
+ extra ||= Digest.hexencode(memo.to_s.slice(0, 140))
211
211
  asset = asset_mixin_id || SHA3::Digest::SHA256.hexdigest(asset_id)
212
212
  tx = {
213
213
  version: 2,
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MixinBot
4
+ class CLI < Thor
5
+ desc 'api PATH', 'request PATH of Mixin API'
6
+ long_desc <<-LONGDESC
7
+ use `mixinbot api PATH` to request any Mixin API
8
+
9
+ Get user infomation:
10
+
11
+ $ mixinbot api /me -k ~/.mixinbot/keystore.json
12
+
13
+ Search user infomation:
14
+
15
+ $ mixinbot api /search/1051445 -k ~/.mixinbot/keystore.json
16
+
17
+ Generate a multisig payment:
18
+
19
+ $ mixinbot api /payments -k ~/.mixinbot/keystore.json -m POST -d '{"asset_id":"965e5c6e-434c-3fa9-b780-c50f43cd955c", "amount":"1", "trace_id": "37f16abb-0640-4d01-9423-a06121732d35", "memo":"test", "opponent_multisig":{"receivers":["0508a116-1239-4e28-b150-85a8e3e6b400", "7ed9292d-7c95-4333-aa48-a8c640064186", "a67c6e87-1c9e-4a1c-b81c-47a9f4f1bff1"], "threshold":2}}'
20
+ LONGDESC
21
+ option :keystore, type: :string, aliases: '-k', required: true, desc: 'keystore or keystore.json file path'
22
+ option :method, type: :string, aliases: '-m', default: 'GET', desc: 'HTTP method, GET or POST'
23
+ option :params, type: :hash, aliases: '-p', desc: 'HTTP GET params'
24
+ option :data, type: :string, aliases: '-d', default: '{}', desc: 'HTTP POST data'
25
+ option :accesstoken, type: :string, aliases: '-t', desc: 'Specify a accesstoken, or will generate by keystore'
26
+ def api(path)
27
+ path = "#{path}?#{URI.encode_www_form(options[:params])}" if options[:params].present?
28
+ payload =
29
+ begin
30
+ JSON.parse options[:data]
31
+ rescue JSON::ParserError => e
32
+ log UI.fmt("{{x}} #{e.inspect}")
33
+ {}
34
+ end
35
+
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)
38
+ res = {}
39
+
40
+ CLI::UI::Spinner.spin("#{options[:method]} #{path}, payload: #{payload}") do |_spinner|
41
+ res =
42
+ case options[:method].downcase.to_sym
43
+ when :post
44
+ api_instance.client.post(path, headers: { 'Authorization': authorization }, json: payload)
45
+ when :get
46
+ api_instance.client.get(path, headers: { 'Authorization': authorization })
47
+ end
48
+ end
49
+
50
+ log res['data']
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MixinBot
4
+ class CLI < Thor
5
+ desc 'encrypt PIN', 'encrypt PIN using private key'
6
+ option :keystore, type: :string, aliases: '-k', required: true, desc: 'keystore or keystore.json file path'
7
+ option :iterator, type: :string, aliases: '-i', desc: 'Iterator'
8
+ def encrypt(pin)
9
+ log api_instance.encrypt_pin options[:pin].to_s, iterator: options[:iterator]
10
+ end
11
+
12
+ desc 'unique UUIDS', 'generate unique UUID for two or more UUIDs'
13
+ def unique(*uuids)
14
+ uuids.sort
15
+ r = uuids.first
16
+ uuids.each_with_index do |uuid, i|
17
+ r = MixinBot::Utils.unique_uuid(r, uuid) if i.positive?
18
+ end
19
+
20
+ log r
21
+ end
22
+
23
+ desc 'generatetrace HASH', 'generate trace ID from Tx hash'
24
+ def generatetrace(hash)
25
+ log MixinBot::Utils.generate_trace_from_hash(hash)
26
+ end
27
+
28
+ desc 'decodetx TRANSACTION', 'decode raw transaction'
29
+ def decodetx(transaction)
30
+ log MixinBot::Utils.decode_raw_transaction(transaction)
31
+ end
32
+
33
+ desc 'nftmemo', 'memo for mint NFT'
34
+ option :collection, type: :string, required: true, aliases: '-c', desc: 'Collection ID, UUID'
35
+ option :token, type: :numeric, required: true, aliases: '-t', desc: 'Token ID, Integer'
36
+ option :hash, type: :string, required: true, aliases: '-h', desc: 'Hash of NFT metadata, 256-bit string'
37
+ def nftmemo
38
+ log MixinBot::Utils.nft(options[:collection], options[:token], options[:hash])
39
+ end
40
+ end
41
+ end
data/lib/mixin_bot/cli.rb CHANGED
@@ -5,77 +5,61 @@ require 'cli/ui'
5
5
  require 'thor'
6
6
  require 'yaml'
7
7
  require 'json'
8
+ require_relative './cli/api'
8
9
  require_relative './cli/node'
9
- require_relative './cli/me'
10
- require_relative './cli/multisig'
10
+ require_relative './cli/utils'
11
11
 
12
12
  module MixinBot
13
13
  class CLI < Thor
14
14
  # https://github.com/Shopify/cli-ui
15
15
  UI = ::CLI::UI
16
16
 
17
- class_option :apihost, type: :string, aliases: '-a', desc: 'Specify mixin api host, default as api.mixin.one'
18
- class_option :pretty, type: :boolean, aliases: '-p', desc: 'Print output in pretty'
17
+ class_option :apihost, type: :string, aliases: '-a', default: 'api.mixin.one', desc: 'Specify mixin api host'
18
+ class_option :pretty, type: :boolean, aliases: '-r', default: true, desc: 'Print output in pretty'
19
19
 
20
- attr_reader :config, :api
20
+ attr_reader :keystore, :api_instance
21
21
 
22
22
  def initialize(*args)
23
23
  super
24
- if File.exist? options[:config].to_s
25
- @config =
26
- begin
27
- YAML.load_file options[:config]
28
- rescue StandardError => e
29
- log UI.fmt(
30
- format(
31
- '{{x}} %<file>s is not a valid .yml file',
32
- file: options[:config]
33
- )
34
- )
35
- UI::Frame.open('{{x}}', color: :red) do
36
- log e
37
- end
38
- end
39
- elsif options[:config]
40
- @confg =
41
- begin
42
- JSON.parse options[:config]
43
- rescue StandardError => e
44
- log UI.fmt(
45
- format(
46
- '{{x}} Failed to parse %<config>s',
47
- config: options[:config]
48
- )
24
+ return if options[:keystore].blank?
25
+
26
+ keystore =
27
+ if File.file? options[:keystore]
28
+ File.read options[:keystore]
29
+ else
30
+ options[:keystore]
31
+ end
32
+
33
+ @keystore =
34
+ begin
35
+ JSON.parse keystore
36
+ rescue JSON::ParserError => e
37
+ log UI.fmt(
38
+ format(
39
+ '{{x}} falied to parse keystore.json: %<keystore>s',
40
+ keystore: options[:keystore]
49
41
  )
50
- UI::Frame.open('{{x}}', color: :red) do
51
- log e
52
- end
53
- end
54
- end
42
+ )
43
+ end
55
44
 
56
- return unless @config
45
+ return unless @keystore
57
46
 
58
47
  MixinBot.api_host = options[:apihost]
59
- @api ||=
48
+ @api_instance ||=
60
49
  begin
61
50
  MixinBot::API.new(
62
- client_id: @config['client_id'],
63
- client_secret: @config['client_secret'],
64
- session_id: @config['session_id'],
65
- pin_token: @config['pin_token'],
66
- private_key: @config['private_key'],
67
- pin_code: @config['pin_code']
51
+ client_id: @keystore['client_id'],
52
+ session_id: @keystore['session_id'],
53
+ pin_token: @keystore['pin_token'],
54
+ private_key: @keystore['private_key']
68
55
  )
69
56
  rescue StandardError => e
70
- log UI.fmt '{{x}}: Failed to initialize api, maybe your config is incorrect.'
71
- UI.Frame.open('{{x}}', color: :red) do
72
- log e
73
- end
57
+ log UI.fmt '{{x}}: Failed to initialize api, maybe your keystore is incorrect.'
74
58
  end
75
59
  end
76
60
 
77
- desc 'node', 'mixin node commands helper'
78
- subcommand 'node', MixinBot::NodeCLI
61
+ # desc 'node', 'mixin node commands helper'
62
+ # subcommand 'node', MixinBot::NodeCLI
79
63
 
80
64
  desc 'version', 'Distay MixinBot version'
81
65
  def version
@@ -88,31 +72,6 @@ module MixinBot
88
72
 
89
73
  private
90
74
 
91
- def api_method(method, *args, **params)
92
- if api.nil?
93
- log UI.fmt '{{x}} MixinBot api not initialized!'
94
- return
95
- end
96
-
97
- res = if args.empty? && params.empty?
98
- api&.public_send method
99
- elsif args.empty? && !params.empty?
100
- api&.public_send method params
101
- elsif !args.empty? && params.empty?
102
- api&.public_send method, args
103
- else
104
- args.push params
105
- api&.public_send method, args
106
- end
107
- log res
108
-
109
- [res, res && res['error'].nil?]
110
- rescue MixinBot::Errors => e
111
- UI::Frame.open('{{x}}', color: :red) do
112
- log e
113
- end
114
- end
115
-
116
75
  def log(obj)
117
76
  if options[:pretty]
118
77
  if obj.is_a? String
@@ -57,7 +57,7 @@ module MixinBot
57
57
  if tx.is_a? String
58
58
  tx = JSON.parse tx
59
59
  end
60
- raise "#{tx} is not a valid json" unless tx.is_a? Hash
60
+ raise ArgumentError, "#{tx} is not a valid json" unless tx.is_a? Hash
61
61
 
62
62
  tx = tx.with_indifferent_access
63
63
  bytes = []
@@ -99,7 +99,7 @@ module MixinBot
99
99
  tx = {}
100
100
 
101
101
  magic = bytes.shift(2)
102
- raise 'Not valid raw' unless magic == MAGIC
102
+ raise ArgumentError, 'Not valid raw' unless magic == MAGIC
103
103
 
104
104
  version = bytes.shift(2)
105
105
  tx['version'] = bytes_to_int version
@@ -121,7 +121,7 @@ module MixinBot
121
121
  # aggregated
122
122
  aggregated = {}
123
123
 
124
- raise 'invalid aggregated' unless bytes.shift(2).reverse.pack('C*').unpack1('S*') == AGGREGATED_SIGNATURE_PREFIX
124
+ raise ArgumentError, 'invalid aggregated' unless bytes.shift(2).reverse.pack('C*').unpack1('S*') == AGGREGATED_SIGNATURE_PREFIX
125
125
 
126
126
  aggregated['signature'] = bytes.shift(64).pack('C*').unpack1('H*')
127
127
 
@@ -162,7 +162,7 @@ module MixinBot
162
162
 
163
163
  def nft_memo_hash(collection, token_id, hash)
164
164
  collection = NULL_UUID if collection.empty?
165
- raise 'hash must be 256-bit string' unless hash.is_a?(String) && hash.size == 64
165
+ raise ArgumentError, 'hash must be 256-bit string' unless hash.is_a?(String) && hash.size == 64
166
166
 
167
167
  memo = {
168
168
  prefix: NFT_MEMO_PREFIX,
@@ -171,14 +171,14 @@ module MixinBot
171
171
  chain: NFT_MEMO_DEFAULT_CHAIN,
172
172
  class: NFT_MEMO_DEFAULT_CLASS,
173
173
  collection: collection,
174
- token: token_id,
174
+ token: token_id.to_i,
175
175
  extra: hash
176
176
  }
177
177
 
178
178
  mark = [0]
179
179
  mark.map do |index|
180
180
  if index >= 64
181
- raise "invalid NFO memo index #{index}"
181
+ raise ArgumentError, "invalid NFO memo index #{index}"
182
182
  end
183
183
  memo[:mask] = memo[:mask] ^ (1 << index)
184
184
  end
@@ -252,8 +252,8 @@ module MixinBot
252
252
  private
253
253
 
254
254
  def encode_int(int)
255
- raise "only support int #{int}" unless int.is_a?(Integer)
256
- raise "int #{int} is larger than MAX_ENCODE_INT #{MAX_ENCODE_INT}" if int > MAX_ENCODE_INT
255
+ raise ArgumentError, "only support int #{int}" unless int.is_a?(Integer)
256
+ raise ArgumentError,"int #{int} is larger than MAX_ENCODE_INT #{MAX_ENCODE_INT}" if int > MAX_ENCODE_INT
257
257
 
258
258
  [int].pack('S*').bytes.reverse
259
259
  end
@@ -263,7 +263,7 @@ module MixinBot
263
263
  end
264
264
 
265
265
  def bytes_of(int)
266
- raise 'not integer' unless int.is_a?(Integer)
266
+ raise ArgumentError, 'not integer' unless int.is_a?(Integer)
267
267
 
268
268
  bytes = []
269
269
  loop do
@@ -427,8 +427,8 @@ module MixinBot
427
427
  bytes += NULL_BYTES
428
428
  else
429
429
  signers.each do |sig, i|
430
- raise 'signers not sorted' if i > 0 && sig <= signers[i - 1]
431
- raise 'signers not sorted' if sig > MAX_ENCODE_INT
430
+ raise ArgumentError, 'signers not sorted' if i > 0 && sig <= signers[i - 1]
431
+ raise ArgumentError, 'signers not sorted' if sig > MAX_ENCODE_INT
432
432
  end
433
433
 
434
434
  max = signers.last
@@ -458,7 +458,7 @@ module MixinBot
458
458
  0
459
459
  end
460
460
 
461
- raise 'signatures overflow' if sl == MAX_ENCODE_INT
461
+ raise ArgumentError, 'signatures overflow' if sl == MAX_ENCODE_INT
462
462
  bytes += encode_int sl
463
463
 
464
464
  if sl > 0
@@ -493,7 +493,7 @@ module MixinBot
493
493
 
494
494
  if bytes[...2] != NULL_BYTES
495
495
  magic = bytes.shift(2)
496
- raise 'Not valid input' unless magic == MAGIC
496
+ raise ArgumentError, 'Not valid input' unless magic == MAGIC
497
497
 
498
498
  deposit = {}
499
499
  deposit['chain'] = bytes.shift(32).pack('C*').unpack1('H*')
@@ -516,7 +516,7 @@ module MixinBot
516
516
 
517
517
  if bytes[...2] != NULL_BYTES
518
518
  magic = bytes.shift(2)
519
- raise 'Not valid input' unless magic == MAGIC
519
+ raise ArgumentError, 'Not valid input' unless magic == MAGIC
520
520
 
521
521
  mint = {}
522
522
  if bytes[...2] != NULL_BYTES
@@ -567,7 +567,7 @@ module MixinBot
567
567
 
568
568
  if bytes[...2] != NULL_BYTES
569
569
  magic = bytes.shift(2)
570
- raise 'Not valid output' unless magic == MAGIC
570
+ raise ArgumentError, 'Not valid output' unless magic == MAGIC
571
571
 
572
572
  withdraw = {}
573
573
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MixinBot
4
- VERSION = '0.7.7'
4
+ VERSION = '0.7.10'
5
5
  end
data/lib/mixin_bot.rb CHANGED
@@ -30,12 +30,14 @@ module MixinBot
30
30
  @api ||= MixinBot::API.new
31
31
  end
32
32
 
33
- class HttpError < StandardError; end
34
- class RequestError < StandardError; end
35
- class ResponseError < StandardError; end
36
- class UnauthorizedError < StandardError; end
37
- class ForbiddenError < StandardError; end
38
- class InsufficientBalanceError < StandardError; end
39
- class InsufficientPoolError < StandardError; end
40
- class PinError < StandardError; end
33
+ class Error < StandardError; end
34
+ class ArgumentError < StandardError; end
35
+ class HttpError < Error; end
36
+ class RequestError < Error; end
37
+ class ResponseError < Error; end
38
+ class UnauthorizedError < Error; end
39
+ class ForbiddenError < Error; end
40
+ class InsufficientBalanceError < Error; end
41
+ class InsufficientPoolError < Error; end
42
+ class PinError < Error; end
41
43
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mixin_bot
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.7
4
+ version: 0.7.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - an-lee
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-01-31 00:00:00.000000000 Z
11
+ date: 2022-03-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -293,9 +293,9 @@ files:
293
293
  - lib/mixin_bot/api/user.rb
294
294
  - lib/mixin_bot/api/withdraw.rb
295
295
  - lib/mixin_bot/cli.rb
296
- - lib/mixin_bot/cli/me.rb
297
- - lib/mixin_bot/cli/multisig.rb
296
+ - lib/mixin_bot/cli/api.rb
298
297
  - lib/mixin_bot/cli/node.rb
298
+ - lib/mixin_bot/cli/utils.rb
299
299
  - lib/mixin_bot/client.rb
300
300
  - lib/mixin_bot/utils.rb
301
301
  - lib/mixin_bot/version.rb
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module MixinBot
4
- class CLI < Thor
5
- desc 'read_me', 'fetch mixin bot profile'
6
- option :config, required: true, aliases: '-c'
7
- def read_me
8
- api_method(:read_me)
9
- end
10
-
11
- desc 'read_assets', 'fetch mixin bot assets'
12
- option :config, required: true, aliases: '-c'
13
- def read_assets
14
- api_method(:read_assets)
15
- end
16
-
17
- desc 'cal_assets_as_usd', 'fetch mixin bot assets'
18
- option :config, required: true, aliases: '-c'
19
- def cal_assets_as_usd
20
- assets, success = read_assets
21
- return unless success
22
-
23
- sum = assets['data'].map(
24
- &lambda { |asset|
25
- asset['balance'].to_f * asset['price_usd'].to_f
26
- }
27
- ).sum
28
- UI::Frame.open('USD') do
29
- log sum
30
- end
31
- end
32
-
33
- desc 'read_asset', 'fetch specific asset of mixin bot'
34
- option :config, required: true, aliases: '-c'
35
- option :assetid, required: true, aliases: '-s'
36
- def read_asset
37
- api_method(:read_asset, options[:assetid])
38
- end
39
- end
40
- end
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module MixinBot
4
- class CLI < Thor
5
- desc 'get_all_multisigs', 'fetch all utxos'
6
- option :config, required: true, aliases: '-c'
7
- def all_multisigs
8
- api_method(:get_all_multisigs)
9
- end
10
- end
11
- end