mixin_bot 0.3.8 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 59e234762e65de8e0858a88eaae1c25a8951076345728b20e65d2c15294fc831
4
- data.tar.gz: 190b8385d4501ff7e603751670f160257f69651679cb4572f4037c4d7c955a67
3
+ metadata.gz: 769e68c946637d91db6a89c5583ab7c49763193ca8c7088fbd95038cb2bf5f20
4
+ data.tar.gz: 31405213d094fb3a3d39c4f2e1b2f4e51ce7555c591148c539eb669c7b9d7ccb
5
5
  SHA512:
6
- metadata.gz: 92d6704e15ce02a56e5f3451c75689b27caf6b7a782597a5535e736926c0c6090cc75d7f40d2190c29ddb32858b910f4b3f0bbcf7f295d474298258ae60c73d6
7
- data.tar.gz: 19b4de6e5645793750091cb311b38ecedf5e5ba5b6fd0ffaa1916d0be9f2c5fd53a33be658e21e992fdaffe01bbeee83c500b2cb0223ea04fdcd5fda1db87270
6
+ metadata.gz: 1105ff54db0cb2eef406ebeaed844227fbb009006ddc71c4ca96bd45967b87fadd8e4a596c2f383e6c4f37558c22fec33111aa051546abf046a2944f0a3d100e
7
+ data.tar.gz: 5bfce8c2db0ddfc6f204babffd5314988055a59529634d16657943c69373130640e706bb25c339c2dbb8e5f6045b796e636b76cea870746db04d3548086a52d4
data/lib/mixin_bot.rb CHANGED
@@ -11,6 +11,7 @@ require 'msgpack'
11
11
  require 'open3'
12
12
  require 'openssl'
13
13
  require 'rbnacl'
14
+ require 'sha3'
14
15
 
15
16
  require_relative './mixin_bot/api'
16
17
  require_relative './mixin_bot/cli'
data/lib/mixin_bot/api.rb CHANGED
@@ -13,6 +13,7 @@ require_relative './api/multisig'
13
13
  require_relative './api/payment'
14
14
  require_relative './api/pin'
15
15
  require_relative './api/snapshot'
16
+ require_relative './api/transaction'
16
17
  require_relative './api/transfer'
17
18
  require_relative './api/user'
18
19
  require_relative './api/withdraw'
@@ -65,6 +66,7 @@ module MixinBot
65
66
  include MixinBot::API::Payment
66
67
  include MixinBot::API::Pin
67
68
  include MixinBot::API::Snapshot
69
+ include MixinBot::API::Transaction
68
70
  include MixinBot::API::Transfer
69
71
  include MixinBot::API::User
70
72
  include MixinBot::API::Withdraw
@@ -17,19 +17,106 @@ module MixinBot
17
17
  end
18
18
  alias read_conversation_by_user_id conversation_by_user_id
19
19
 
20
- def create_contact_conversation(user_id)
20
+ def create_conversation(category:, conversation_id:, participants:, name: nil, access_token: nil)
21
21
  path = '/conversations'
22
22
  payload = {
23
+ category: category,
24
+ conversation_id: conversation_id || SecureRandom.uuid,
25
+ name: name,
26
+ participants: participants
27
+ }
28
+
29
+ access_token ||= access_token('POST', path, payload.to_json)
30
+ authorization = format('Bearer %<access_token>s', access_token: access_token)
31
+ client.post(path, headers: { 'Authorization': authorization }, json: payload)
32
+ end
33
+
34
+ def create_group_conversation(user_ids:, name:, conversation_id: nil, access_token: nil)
35
+ create_conversation(
36
+ category: 'GROUP',
37
+ conversation_id: conversation_id,
38
+ name: name,
39
+ participants: user_ids.map(&->(participant) { { user_id: participant } }),
40
+ access_token: access_token
41
+ )
42
+ end
43
+
44
+ def create_contact_conversation(user_id, access_token: nil)
45
+ create_conversation(
23
46
  category: 'CONTACT',
24
47
  conversation_id: unique_conversation_id(user_id),
25
48
  participants: [
26
49
  {
27
- action: 'ADD',
28
- role: '',
29
50
  user_id: user_id
30
51
  }
31
- ]
52
+ ],
53
+ access_token: access_token
54
+ )
55
+ end
56
+
57
+ def update_group_conversation_name(name:, conversation_id:, access_token: nil)
58
+ path = format('/conversations/%<id>s', id: conversation_id)
59
+ payload = {
60
+ name: name
61
+ }
62
+
63
+ access_token ||= access_token('POST', path, payload.to_json)
64
+ authorization = format('Bearer %<access_token>s', access_token: access_token)
65
+ client.post(path, headers: { 'Authorization': authorization }, json: payload)
66
+ end
67
+
68
+ def update_group_conversation_announcement(announcement:, conversation_id:, access_token: nil)
69
+ path = format('/conversations/%<id>s', id: conversation_id)
70
+ payload = {
71
+ announcement: announcement
32
72
  }
73
+
74
+ access_token ||= access_token('POST', path, payload.to_json)
75
+ authorization = format('Bearer %<access_token>s', access_token: access_token)
76
+ client.post(path, headers: { 'Authorization': authorization }, json: payload)
77
+ end
78
+
79
+ # participants = [{ user_id: "" }]
80
+ def add_conversation_participants(conversation_id:, user_ids:, access_token: nil)
81
+ path = format('/conversations/%<id>s/participants/ADD', id: conversation_id)
82
+ payload = user_ids.map(&->(participant) { { user_id: participant } })
83
+
84
+ access_token ||= access_token('POST', path, payload.to_json)
85
+ authorization = format('Bearer %<access_token>s', access_token: access_token)
86
+ client.post(path, headers: { 'Authorization': authorization }, json: payload)
87
+ end
88
+
89
+ # participants = [{ user_id: "" }]
90
+ def remove_conversation_participants(conversation_id:, user_ids:, access_token: nil)
91
+ path = format('/conversations/%<id>s/participants/REMOVE', id: conversation_id)
92
+ payload = user_ids.map(&->(participant) { { user_id: participant } })
93
+
94
+ access_token ||= access_token('POST', path, payload.to_json)
95
+ authorization = format('Bearer %<access_token>s', access_token: access_token)
96
+ client.post(path, headers: { 'Authorization': authorization }, json: payload)
97
+ end
98
+
99
+ def exit_conversation(conversation_id, access_token: nil)
100
+ path = format('/conversations/%<id>s/exit', id: conversation_id)
101
+
102
+ access_token ||= access_token('POST', path)
103
+ authorization = format('Bearer %<access_token>s', access_token: access_token)
104
+ client.post(path, headers: { 'Authorization': authorization })
105
+ end
106
+
107
+ def rotate_conversation(conversation_id, access_token: nil)
108
+ path = format('/conversations/%<id>s/rotate', id: conversation_id)
109
+
110
+ access_token ||= access_token('POST', path)
111
+ authorization = format('Bearer %<access_token>s', access_token: access_token)
112
+ client.post(path, headers: { 'Authorization': authorization })
113
+ end
114
+
115
+ # participants = [{ user_id: "", role: "ADMIN" }]
116
+ def update_conversation_participants_role(conversation_id:, participants:, access_token: nil)
117
+ path = format('/conversations/%<id>s/participants/ROLE', id: conversation_id)
118
+ payload = participants
119
+
33
120
  access_token ||= access_token('POST', path, payload.to_json)
34
121
  authorization = format('Bearer %<access_token>s', access_token: access_token)
35
122
  client.post(path, headers: { 'Authorization': authorization }, json: payload)
@@ -28,26 +28,28 @@ module MixinBot
28
28
  # "signed_tx":""
29
29
  # }
30
30
  # ]}
31
- def multisigs(limit: 100, offset: nil, access_token: nil)
32
- path = format('/multisigs?limit=%<limit>s&offset=%<offset>s', limit: limit, offset: offset)
31
+ def outputs(**kwargs)
32
+ limit = kwargs[:limit] || 100
33
+ offset = kwargs[:offset] || ''
34
+ state = kwargs[:state] || ''
35
+ members = kwargs[:members] || []
36
+ threshold = kwargs[:threshold] || ''
37
+ access_token = kwargs[:access_token]
38
+ members = SHA3::Digest::SHA256.hexdigest(members&.sort&.join)
39
+
40
+ path = format(
41
+ '/multisigs/outputs?limit=%<limit>s&offset=%<offset>s&state=%<state>s&members=%<members>s&threshold=%<threshold>s',
42
+ limit: limit,
43
+ offset: offset,
44
+ state: state,
45
+ members: members,
46
+ threshold: threshold
47
+ )
33
48
  access_token ||= access_token('GET', path, '')
34
49
  authorization = format('Bearer %<access_token>s', access_token: access_token)
35
50
  client.get(path, headers: { 'Authorization': authorization })
36
51
  end
37
-
38
- def all_multisigs(utxos: [], offset: nil, access_token: nil)
39
- res = multisigs(limit: 100, offset: offset, access_token: access_token)
40
-
41
- return [] if res['data'].nil?
42
-
43
- utxos += res['data']
44
-
45
- if res['data'].length < 100
46
- utxos
47
- else
48
- all_multisigs(utxos: utxos, offset: utxos[-1]['created_at'], access_token: access_token)
49
- end
50
- end
52
+ alias multisigs outputs
51
53
 
52
54
  def create_output(receivers:, index:, access_token: nil)
53
55
  path = '/outputs'
@@ -64,7 +66,7 @@ module MixinBot
64
66
  # create a request for multi sign
65
67
  # for now, raw(RAW-TRANSACTION-HEX) can only be generated by Mixin SDK of Golang or Javascript
66
68
  def create_sign_multisig_request(raw, access_token: nil)
67
- path = '/multisigs'
69
+ path = '/multisigs/requests'
68
70
  payload = {
69
71
  action: 'sign',
70
72
  raw: raw
@@ -77,7 +79,7 @@ module MixinBot
77
79
  # transfer from the multisig address
78
80
  # create a request for unlock a multi-sign
79
81
  def create_unlock_multisig_request(raw, access_token: nil)
80
- path = '/multisigs'
82
+ path = '/multisigs/requests'
81
83
  payload = {
82
84
  action: 'unlock',
83
85
  raw: raw
@@ -88,7 +90,7 @@ module MixinBot
88
90
  end
89
91
 
90
92
  def sign_multisig_request(request_id, pin)
91
- path = format('/multisigs/%<request_id>s/sign', request_id: request_id)
93
+ path = format('/multisigs/requests/%<request_id>s/sign', request_id: request_id)
92
94
  payload = {
93
95
  pin: encrypt_pin(pin)
94
96
  }
@@ -98,7 +100,7 @@ module MixinBot
98
100
  end
99
101
 
100
102
  def unlock_multisig_request(request_id, pin)
101
- path = format('/multisigs/%<request_id>s/unlock', request_id: request_id)
103
+ path = format('/multisigs/requests/%<request_id>s/unlock', request_id: request_id)
102
104
  payload = {
103
105
  pin: encrypt_pin(pin)
104
106
  }
@@ -108,7 +110,7 @@ module MixinBot
108
110
  end
109
111
 
110
112
  def cancel_multisig_request(request_id, pin)
111
- path = format('/multisigs/%<request_id>s/cancel', request_id: request_id)
113
+ path = format('/multisigs/requests/%<request_id>s/cancel', request_id: request_id)
112
114
  payload = {
113
115
  pin: encrypt_pin(pin)
114
116
  }
@@ -165,76 +167,42 @@ module MixinBot
165
167
  "fffe#{s}"
166
168
  end
167
169
 
168
- # filter utxo by members, asset_id and threshold
169
- def filter_utxos(**kwargs)
170
- utxos = all_multisigs(access_token: kwargs[:access_token])
171
-
172
- unless kwargs[:members].nil?
173
- utxos = utxos.filter(
174
- &lambda { |utxo|
175
- utxo['members'].sort == kwargs[:members].sort
176
- }
177
- )
178
- end
179
-
180
- unless kwargs[:asset_id].nil?
181
- utxos = utxos.filter(
182
- &lambda { |utxo|
183
- utxo['asset_id'] == kwargs[:asset_id]
184
- }
185
- )
186
- end
187
-
188
- unless kwargs[:threshold].nil?
189
- utxos = utxos.filter(
190
- &lambda { |utxo|
191
- utxo['threshold'] == kwargs[:threshold]
192
- }
193
- )
194
- end
195
-
196
- unless kwargs[:state].nil?
197
- utxos = utxos.filter(
198
- &lambda { |utxo|
199
- utxo['state'] == kwargs[:state]
200
- }
201
- )
202
- end
203
-
204
- utxos
205
- end
206
-
207
170
  # kwargs:
208
171
  # {
209
172
  # senders: [ uuid ],
210
173
  # receivers: [ uuid ],
211
174
  # threshold: integer,
212
175
  # asset_id: uuid,
213
- # asset_mixin_id: string,
214
176
  # amount: string / float,
215
177
  # memo: string,
216
178
  # }
217
- def build_raw_transaction(kwargs)
179
+ RAW_TRANSACTION_ARGUMENTS = %i[senders receivers amount threshold asset_id].freeze
180
+ def build_raw_transaction(**kwargs)
181
+ raise ArgumentError, "#{RAW_TRANSACTION_ARGUMENTS.join(', ')} are needed for build raw transaction" unless RAW_TRANSACTION_ARGUMENTS.all? { |param| kwargs.keys.include? param }
182
+
218
183
  senders = kwargs[:senders]
219
184
  receivers = kwargs[:receivers]
220
- asset_id = kwargs[:asset_id]
221
- asset_mixin_id = kwargs[:asset_mixin_id]
222
185
  amount = kwargs[:amount]
223
- memo = kwargs[:memo]
224
186
  threshold = kwargs[:threshold]
225
- access_token = kwargs[:access_token]
187
+ asset_id = kwargs[:asset_id]
226
188
  utxos = kwargs[:utxos]
189
+ memo = kwargs[:memo]
190
+ access_token = kwargs[:access_token]
227
191
 
228
192
  raise 'access_token required!' if access_token.nil? && !senders.include?(client_id)
229
193
 
230
- # default to use all unspent utxo
231
- utxos ||= filter_utxos(
194
+ # default to use all(first 100) unspent utxo
195
+ utxos ||= multisigs(
232
196
  members: senders,
233
- asset_id: asset_id,
234
197
  threshold: threshold,
235
198
  state: 'unspent',
236
199
  access_token: access_token
200
+ ).filter(
201
+ &lambda { |utxo|
202
+ utxo['asset_id'] == kwargs[:asset_id]
203
+ }
237
204
  )
205
+
238
206
  amount = amount.to_f.round(8)
239
207
  input_amount = utxos.map(
240
208
  &lambda { |utxo|
@@ -281,7 +249,7 @@ module MixinBot
281
249
  extra = Digest.hexencode memo.to_s.slice(0, 140)
282
250
  tx = {
283
251
  version: 1,
284
- asset: asset_mixin_id,
252
+ asset: SHA3::Digest::SHA256.hexdigest(asset_id),
285
253
  inputs: inputs,
286
254
  outputs: outputs,
287
255
  extra: extra
@@ -3,7 +3,7 @@
3
3
  module MixinBot
4
4
  class API
5
5
  module Snapshot
6
- def network_snapshots(options = {})
6
+ def network_snapshots(**options)
7
7
  path = format(
8
8
  '/network/snapshots?limit=%<limit>s&offset=%<offset>s&asset=%<asset>s&order=%<order>s',
9
9
  limit: options[:limit],
@@ -18,12 +18,13 @@ module MixinBot
18
18
  end
19
19
  alias read_network_snapshots network_snapshots
20
20
 
21
- def snapshots(options = {})
21
+ def snapshots(**options)
22
22
  path = format(
23
- '/snapshots?limit=%<limit>s&offset=%<offset>s&asset=%<asset>s',
23
+ '/snapshots?limit=%<limit>s&offset=%<offset>s&asset=%<asset>s&opponent=%<opponent>s',
24
24
  limit: options[:limit],
25
25
  offset: options[:offset],
26
- asset: options[:asset]
26
+ asset: options[:asset],
27
+ opponent: options[:opponent]
27
28
  )
28
29
 
29
30
  access_token = options[:access_token] || access_token('GET', path)
@@ -32,7 +33,7 @@ module MixinBot
32
33
  end
33
34
  alias read_snapshots snapshots
34
35
 
35
- def network_snapshot(snapshot_id, options = {})
36
+ def network_snapshot(snapshot_id, **options)
36
37
  path = format('/network/snapshots/%<snapshot_id>s', snapshot_id: snapshot_id)
37
38
 
38
39
  access_token = options[:access_token] || access_token('GET', path)
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MixinBot
4
+ class API
5
+ module Transaction
6
+ MULTISIG_TRANSACTION_ARGUMENTS = %i[asset_id receivers threshold amount].freeze
7
+ def create_multisig_transaction(pin, options = {})
8
+ raise ArgumentError, "#{MULTISIG_TRANSACTION_ARGUMENTS.join(', ')} are needed for create multisig transaction" unless MULTISIG_TRANSACTION_ARGUMENTS.all? { |param| options.keys.include? param }
9
+
10
+ asset_id = options[:asset_id]
11
+ receivers = options[:receivers]
12
+ threshold = options[:threshold]
13
+ amount = options[:amount].to_f
14
+ memo = options[:memo]
15
+ trace_id = options[:trace_id] || SecureRandom.uuid
16
+ encrypted_pin = options[:encrypted_pin] || encrypt_pin(pin)
17
+
18
+ path = '/transactions'
19
+ payload = {
20
+ asset_id: asset_id,
21
+ opponent_multisig: {
22
+ receivers: receivers,
23
+ threshold: threshold
24
+ },
25
+ pin: encrypted_pin,
26
+ amount: amount.to_s,
27
+ trace_id: trace_id,
28
+ memo: memo
29
+ }
30
+
31
+ access_token = options[:access_token]
32
+ access_token ||= access_token('POST', path, payload.to_json)
33
+ authorization = format('Bearer %<access_token>s', access_token: access_token)
34
+ client.post(path, headers: { 'Authorization': authorization }, json: payload)
35
+ end
36
+
37
+ MAINNET_TRANSACTION_ARGUMENTS = %i[asset_id opponent_key amount].freeze
38
+ def create_mainnet_transaction(pin, options = {})
39
+ raise ArgumentError, "#{MAINNET_TRANSACTION_ARGUMENTS.join(', ')} are needed for create main net transactions" unless MAINNET_TRANSACTION_ARGUMENTS.all? { |param| options.keys.include? param }
40
+
41
+ asset_id = options[:asset_id]
42
+ opponent_key = options[:opponent_key]
43
+ amount = options[:amount].to_f
44
+ memo = options[:memo]
45
+ trace_id = options[:trace_id] || SecureRandom.uuid
46
+ encrypted_pin = options[:encrypted_pin] || encrypt_pin(pin)
47
+
48
+ path = '/transactions'
49
+ payload = {
50
+ asset_id: asset_id,
51
+ opponent_key: opponent_key,
52
+ pin: encrypted_pin,
53
+ amount: amount.to_s,
54
+ trace_id: trace_id,
55
+ memo: memo
56
+ }
57
+
58
+ access_token = options[:access_token]
59
+ access_token ||= access_token('POST', path, payload.to_json)
60
+ authorization = format('Bearer %<access_token>s', access_token: access_token)
61
+ client.post(path, headers: { 'Authorization': authorization }, json: payload)
62
+ end
63
+ end
64
+ end
65
+ end
@@ -32,7 +32,7 @@ module MixinBot
32
32
  raise HttpError, e.message
33
33
  end
34
34
 
35
- raise RequestError.new(nil, response.to_s) unless response.status.success?
35
+ raise RequestError, response.to_s unless response.status.success?
36
36
 
37
37
  parse_response(response) do |parse_as, result|
38
38
  case parse_as
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MixinBot
4
- VERSION = '0.3.8'
4
+ VERSION = '0.4.0'
5
5
  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.3.8
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - an-lee
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-07 00:00:00.000000000 Z
11
+ date: 2021-06-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: awesome_print
@@ -122,6 +122,20 @@ dependencies:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
124
  version: '7.1'
125
+ - !ruby/object:Gem::Dependency
126
+ name: sha3
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '1.0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '1.0'
125
139
  - !ruby/object:Gem::Dependency
126
140
  name: thor
127
141
  requirement: !ruby/object:Gem::Requirement
@@ -244,6 +258,7 @@ files:
244
258
  - lib/mixin_bot/api/payment.rb
245
259
  - lib/mixin_bot/api/pin.rb
246
260
  - lib/mixin_bot/api/snapshot.rb
261
+ - lib/mixin_bot/api/transaction.rb
247
262
  - lib/mixin_bot/api/transfer.rb
248
263
  - lib/mixin_bot/api/user.rb
249
264
  - lib/mixin_bot/api/withdraw.rb
@@ -272,7 +287,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
272
287
  - !ruby/object:Gem::Version
273
288
  version: '0'
274
289
  requirements: []
275
- rubygems_version: 3.0.3
290
+ rubygems_version: 3.0.3.1
276
291
  signing_key:
277
292
  specification_version: 4
278
293
  summary: An API wrapper for Mixin Nexwork