mixin_bot 0.12.1 → 1.1.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 (57) 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 +34 -56
  7. data/lib/mixin_bot/api/blaze.rb +4 -3
  8. data/lib/mixin_bot/api/conversation.rb +29 -49
  9. data/lib/mixin_bot/api/encrypted_message.rb +19 -19
  10. data/lib/mixin_bot/api/inscription.rb +71 -0
  11. data/lib/mixin_bot/api/legacy_collectible.rb +140 -0
  12. data/lib/mixin_bot/api/legacy_multisig.rb +87 -0
  13. data/lib/mixin_bot/api/legacy_output.rb +50 -0
  14. data/lib/mixin_bot/api/legacy_payment.rb +31 -0
  15. data/lib/mixin_bot/api/legacy_snapshot.rb +39 -0
  16. data/lib/mixin_bot/api/legacy_transaction.rb +173 -0
  17. data/lib/mixin_bot/api/legacy_transfer.rb +42 -0
  18. data/lib/mixin_bot/api/me.rb +13 -17
  19. data/lib/mixin_bot/api/message.rb +13 -10
  20. data/lib/mixin_bot/api/multisig.rb +17 -222
  21. data/lib/mixin_bot/api/output.rb +48 -0
  22. data/lib/mixin_bot/api/payment.rb +9 -20
  23. data/lib/mixin_bot/api/pin.rb +57 -65
  24. data/lib/mixin_bot/api/rpc.rb +12 -14
  25. data/lib/mixin_bot/api/snapshot.rb +15 -29
  26. data/lib/mixin_bot/api/tip.rb +43 -0
  27. data/lib/mixin_bot/api/transaction.rb +295 -60
  28. data/lib/mixin_bot/api/transfer.rb +69 -31
  29. data/lib/mixin_bot/api/user.rb +88 -53
  30. data/lib/mixin_bot/api/withdraw.rb +52 -53
  31. data/lib/mixin_bot/api.rb +81 -46
  32. data/lib/mixin_bot/cli/api.rb +149 -5
  33. data/lib/mixin_bot/cli/utils.rb +14 -4
  34. data/lib/mixin_bot/cli.rb +13 -10
  35. data/lib/mixin_bot/client.rb +74 -127
  36. data/lib/mixin_bot/configuration.rb +98 -0
  37. data/lib/mixin_bot/nfo.rb +174 -0
  38. data/lib/mixin_bot/transaction.rb +524 -0
  39. data/lib/mixin_bot/utils/address.rb +121 -0
  40. data/lib/mixin_bot/utils/crypto.rb +218 -0
  41. data/lib/mixin_bot/utils/decoder.rb +56 -0
  42. data/lib/mixin_bot/utils/encoder.rb +63 -0
  43. data/lib/mixin_bot/utils.rb +8 -109
  44. data/lib/mixin_bot/uuid.rb +41 -0
  45. data/lib/mixin_bot/version.rb +1 -1
  46. data/lib/mixin_bot.rb +39 -14
  47. data/lib/mvm/bridge.rb +2 -19
  48. data/lib/mvm/client.rb +11 -33
  49. data/lib/mvm/nft.rb +4 -4
  50. data/lib/mvm/registry.rb +9 -9
  51. data/lib/mvm/scan.rb +3 -5
  52. data/lib/mvm.rb +5 -6
  53. metadata +77 -103
  54. data/lib/mixin_bot/api/collectible.rb +0 -138
  55. data/lib/mixin_bot/utils/nfo.rb +0 -176
  56. data/lib/mixin_bot/utils/transaction.rb +0 -478
  57. data/lib/mixin_bot/utils/uuid.rb +0 -43
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MixinBot
4
+ class API
5
+ module Inscription
6
+ def collection(hash)
7
+ path = "/safe/inscriptions/collections/#{hash}"
8
+
9
+ client.get path
10
+ end
11
+
12
+ def collectible(hash)
13
+ path = "/safe/inscriptions/items/#{hash}"
14
+
15
+ client.get path
16
+ end
17
+
18
+ def collectibles(members: [], access_token: nil)
19
+ unspent_outputs = safe_outputs(state: :unspent, members:, access_token:)['data']
20
+ unspent_outputs.select { |output| output['inscription_hash'].present? }
21
+ end
22
+
23
+ def create_collectible_transfer(utxo, **kwargs)
24
+ # verify collectible
25
+ utxo = utxo.with_indifferent_access
26
+ raise MixinBot::ArgumentError, 'not a valid collectible' unless utxo['inscription_hash'].present?
27
+
28
+ # verify members
29
+ members = [kwargs[:members]].flatten.compact
30
+ raise ArgumentError, 'members required' if members.blank?
31
+
32
+ threshold = kwargs[:threshold] || members.length
33
+ request_id = kwargs[:request_id] || kwargs[:trace_id] || SecureRandom.uuid
34
+
35
+ memo = kwargs[:memo] || ''
36
+
37
+ # build transaction
38
+ tx = build_safe_transaction(
39
+ utxos: [utxo],
40
+ receivers: [{
41
+ members:,
42
+ threshold:,
43
+ amount: utxo['amount']
44
+ }],
45
+ extra: memo
46
+ )
47
+
48
+ # encode transaction
49
+ raw = MixinBot.utils.encode_raw_transaction tx
50
+
51
+ # verify transaction
52
+ request = create_safe_transaction_request(request_id, raw)['data']
53
+
54
+ # sign transaction
55
+ spend_key = MixinBot.utils.decode_key(kwargs[:spend_key]) || config.spend_key
56
+ signed_raw = MixinBot.api.sign_safe_transaction(
57
+ raw:,
58
+ utxos: [utxo],
59
+ request: request[0],
60
+ spend_key:
61
+ )
62
+
63
+ # submit transaction
64
+ send_safe_transaction(
65
+ request_id,
66
+ signed_raw
67
+ )
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MixinBot
4
+ class API
5
+ module LegacyCollectible
6
+ NFT_ASSET_MIXIN_ID = '1700941284a95f31b25ec8c546008f208f88eee4419ccdcdbe6e3195e60128ca'
7
+
8
+ def legacy_collectible(id, access_token: nil)
9
+ path = "/collectibles/tokens/#{id}"
10
+ client.get path, access_token:
11
+ end
12
+
13
+ def legacy_collection(id, access_token: nil)
14
+ path = "/collectibles/collections/#{id}"
15
+ client.get path, access_token:
16
+ end
17
+
18
+ def legacy_collectibles(**kwargs)
19
+ limit = kwargs[:limit] || 100
20
+ offset = kwargs[:offset] || ''
21
+ state = kwargs[:state] || ''
22
+
23
+ members = kwargs[:members] || [config.app_id]
24
+ threshold = kwargs[:threshold] || members.length
25
+
26
+ members = SHA3::Digest::SHA256.hexdigest(members&.sort&.join)
27
+
28
+ access_token = kwargs[:access_token]
29
+
30
+ path = '/collectibles/outputs'
31
+ params = {
32
+ limit:,
33
+ offset:,
34
+ state:,
35
+ members:,
36
+ threshold:
37
+ }
38
+
39
+ client.get path, **params, access_token:
40
+ end
41
+
42
+ COLLECTABLE_REQUEST_ACTIONS = %i[sign unlock].freeze
43
+ def create_collectible_request(action, raw, access_token: nil)
44
+ raise ArgumentError, "request action is limited in #{COLLECTABLE_REQUEST_ACTIONS.join(', ')}" unless COLLECTABLE_REQUEST_ACTIONS.include? action.to_sym
45
+
46
+ path = '/collectibles/requests'
47
+ payload = {
48
+ action:,
49
+ raw:
50
+ }
51
+ client.post path, **payload, access_token:
52
+ end
53
+
54
+ def create_sign_collectible_request(raw, access_token: nil)
55
+ create_collectible_request 'sign', raw, access_token:
56
+ end
57
+
58
+ def create_unlock_collectible_request(raw, access_token: nil)
59
+ create_collectible_request 'unlock', raw, access_token:
60
+ end
61
+
62
+ def sign_collectible_request(request_id, pin = nil)
63
+ pin ||= config.pin
64
+ raise ArgumentError, 'pin is needed for sign collectible request' if pin.blank?
65
+
66
+ path = format('/collectibles/requests/%<request_id>s/sign', request_id:)
67
+ payload =
68
+ if pin.length > 6
69
+ pin_base64 = encrypt_tip_pin(pin, 'TIP:COLLECTIBLE:REQUEST:SIGN:', request_id)
70
+ {
71
+ pin_base64:
72
+ }
73
+ else
74
+ {
75
+ pin: encrypt_pin(pin)
76
+ }
77
+ end
78
+ client.post path, **payload
79
+ end
80
+
81
+ def unlock_collectible_request(request_id, pin = nil)
82
+ pin ||= config.pin
83
+ raise ArgumentError, 'pin is needed for sign collectible request' if pin.blank?
84
+
85
+ path = format('/collectibles/requests/%<request_id>s/unlock', request_id:)
86
+ payload =
87
+ if pin.length > 6
88
+ {
89
+ pin_base64: encrypt_tip_pin(pin, 'TIP:COLLECTIBLE:REQUEST:UNLOCK:', request_id)
90
+ }
91
+ else
92
+ {
93
+ pin: encrypt_pin(pin)
94
+ }
95
+ end
96
+
97
+ client.post path, **payload
98
+ end
99
+ end
100
+
101
+ # collectible = {
102
+ # type: 'non_fungible_output',
103
+ # user_id: '',
104
+ # output_id: '',
105
+ # token_id: '',
106
+ # transaction_hash: '',
107
+ # output_index: '',
108
+ # amount: 1,
109
+ # senders: [],
110
+ # sender_threshold: 1,
111
+ # receivers: [],
112
+ # receivers_threshold: 1,
113
+ # state: 'unspent'
114
+ # }
115
+ COLLECTIBLE_TRANSACTION_ARGUMENTS = %i[collectible nfo receivers receivers_threshold].freeze
116
+ def build_collectible_transaction(**kwargs)
117
+ raise ArgumentError, "#{COLLECTIBLE_TRANSACTION_ARGUMENTS.join(', ')} are needed for build collectible transaction" unless COLLECTIBLE_TRANSACTION_ARGUMENTS.all? { |param| kwargs.keys.include? param }
118
+
119
+ kwargs = kwargs.with_indifferent_access
120
+ collectible = kwargs['collectible']
121
+ raise 'collectible is spent' if collectible['state'] == 'spent'
122
+
123
+ build_raw_transaction(
124
+ utxos: [collectible],
125
+ senders: collectible['receivers'],
126
+ senders_threshold: collectible['receivers_threshold'],
127
+ receivers: kwargs['receivers'],
128
+ receivers_threshold: kwargs['receivers_threshold'],
129
+ extra: [kwargs['nfo']].pack('H*'),
130
+ amount: 1,
131
+ asset_mixin_id: NFT_ASSET_MIXIN_ID,
132
+ hint: kwargs['hint']
133
+ )
134
+ end
135
+
136
+ def nft_memo(collection, token_id, meta)
137
+ MixinBot.utils.nft_memo collection, token_id, meta
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MixinBot
4
+ class API
5
+ module LegacyMultisig
6
+ MULTISIG_REQUEST_ACTIONS = %i[sign unlock].freeze
7
+ def create_multisig_request(action, raw, access_token: nil)
8
+ raise ArgumentError, "request action is limited in #{MULTISIG_REQUEST_ACTIONS.join(', ')}" unless MULTISIG_REQUEST_ACTIONS.include? action.to_sym
9
+
10
+ path = '/multisigs/requests'
11
+ payload = {
12
+ action:,
13
+ raw:
14
+ }
15
+ client.post path, **payload, access_token:
16
+ end
17
+
18
+ # transfer from the multisig address
19
+ def create_sign_multisig_request(raw, access_token: nil)
20
+ create_multisig_request 'sign', raw, access_token:
21
+ end
22
+
23
+ # transfer from the multisig address
24
+ # create a request for unlock a multi-sign
25
+ def create_unlock_multisig_request(raw, access_token: nil)
26
+ create_multisig_request 'unlock', raw, access_token:
27
+ end
28
+
29
+ def sign_multisig_request(request_id, pin = nil)
30
+ pin ||= config.pin
31
+ path = format('/multisigs/requests/%<request_id>s/sign', request_id:)
32
+ payload =
33
+ if pin.length > 6
34
+ {
35
+ pin_base64: encrypt_tip_pin(pin, 'TIP:MULTISIG:REQUEST:SIGN:', request_id)
36
+ }
37
+ else
38
+ {
39
+ pin: encrypt_pin(pin)
40
+ }
41
+ end
42
+
43
+ client.post path, **payload
44
+ end
45
+
46
+ def unlock_multisig_request(request_id, pin = nil)
47
+ pin ||= config.pin
48
+
49
+ path = format('/multisigs/requests/%<request_id>s/unlock', request_id:)
50
+ payload =
51
+ if pin.length > 6
52
+ {
53
+ pin_base64: encrypt_tip_pin(pin, 'TIP:MULTISIG:REQUEST:UNLOCK:', request_id)
54
+ }
55
+ else
56
+ {
57
+ pin: encrypt_pin(pin)
58
+ }
59
+ end
60
+
61
+ client.post path, **payload
62
+ end
63
+
64
+ # pay to the multisig address
65
+ # used for create multisig payment code_id
66
+ def create_multisig_payment(**kwargs)
67
+ path = '/payments'
68
+ payload = {
69
+ asset_id: kwargs[:asset_id],
70
+ amount: format('%.8f', kwargs[:amount].to_d),
71
+ trace_id: kwargs[:trace_id] || SecureRandom.uuid,
72
+ memo: kwargs[:memo],
73
+ opponent_multisig: {
74
+ receivers: kwargs[:receivers],
75
+ threshold: kwargs[:threshold]
76
+ }
77
+ }
78
+ client.post path, **payload, access_token: kwargs[:access_token]
79
+ end
80
+
81
+ def verify_multisig(code_id, access_token: nil)
82
+ path = format('/codes/%<code_id>s', code_id:)
83
+ client.get path, access_token:
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MixinBot
4
+ class API
5
+ module LegacyOutput
6
+ def legacy_outputs(**kwargs)
7
+ limit = kwargs[:limit] || 100
8
+ offset = kwargs[:offset] || ''
9
+ state = kwargs[:state] || ''
10
+ members = kwargs[:members] || []
11
+ threshold = kwargs[:threshold] || ''
12
+ access_token = kwargs[:access_token]
13
+ members = SHA3::Digest::SHA256.hexdigest(members&.sort&.join)
14
+
15
+ path = '/multisigs/outputs'
16
+ params = {
17
+ limit:,
18
+ offset:,
19
+ state:,
20
+ members:,
21
+ threshold:
22
+ }.compact_blank
23
+
24
+ client.get path, **params, access_token:
25
+ end
26
+ alias multisigs legacy_outputs
27
+ alias multisig_outputs legacy_outputs
28
+
29
+ def create_output(receivers:, index:, hint: nil, access_token: nil)
30
+ path = '/outputs'
31
+ payload = {
32
+ receivers:,
33
+ index:,
34
+ hint:
35
+ }
36
+ client.post path, **payload, access_token:
37
+ end
38
+
39
+ def build_output(receivers:, index:, amount:, threshold:, hint: nil)
40
+ _output = create_output(receivers:, index:, hint:)
41
+ {
42
+ amount: format('%.8f', amount.to_d.to_r),
43
+ script: build_threshold_script(threshold),
44
+ mask: _output['mask'],
45
+ keys: _output['keys']
46
+ }
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MixinBot
4
+ class API
5
+ module LegacyPayment
6
+ def pay_url(**kwargs)
7
+ format(
8
+ 'https://mixin.one/pay?recipient=%<recipient_id>s&asset=%<asset>s&amount=%<amount>s&trace=%<trace>s&memo=%<memo>s',
9
+ recipient_id: kwargs[:recipient_id],
10
+ asset: kwargs[:asset_id],
11
+ amount: kwargs[:amount].to_s,
12
+ trace: kwargs[:trace],
13
+ memo: kwargs[:memo]
14
+ )
15
+ end
16
+
17
+ # https://developers.mixin.one/api/alpha-mixin-network/verify-payment/
18
+ def verify_payment(**kwargs)
19
+ path = '/payments'
20
+ payload = {
21
+ asset_id: kwargs[:asset_id],
22
+ opponent_id: kwargs[:opponent_id],
23
+ amount: kwargs[:amount].to_s,
24
+ trace_id: kwargs[:trace]
25
+ }
26
+
27
+ client.post path, **payload
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MixinBot
4
+ class API
5
+ module LegacySnapshot
6
+ def network_snapshots(**kwargs)
7
+ path = '/network/snapshots'
8
+ params = {
9
+ limit: kwargs[:limit],
10
+ offset: kwargs[:offset],
11
+ asset: kwargs[:asset],
12
+ order: kwargs[:order]
13
+ }
14
+
15
+ client.get path, **params, access_token: kwargs[:access_token]
16
+ end
17
+
18
+ def snapshots(**kwargs)
19
+ path = '/snapshots'
20
+
21
+ params = {
22
+ limit: kwargs[:limit],
23
+ offset: kwargs[:offset],
24
+ asset: kwargs[:asset],
25
+ opponent: kwargs[:opponent],
26
+ order: kwargs[:order]
27
+ }
28
+
29
+ client.get path, **params, access_token: kwargs[:access_token]
30
+ end
31
+
32
+ def network_snapshot(snapshot_id, **kwargs)
33
+ path = format('/network/snapshots/%<snapshot_id>s', snapshot_id:)
34
+
35
+ client.get path, access_token: kwargs[:access_token]
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,173 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MixinBot
4
+ class API
5
+ module LegacyTransaction
6
+ LEGACY_TX_VERSION = 0x04
7
+
8
+ # use safe transaction protocol instead
9
+ # kwargs:
10
+ # {
11
+ # senders: [ uuid ],
12
+ # senders_threshold: integer,
13
+ # receivers: [ uuid ],
14
+ # receivers_threshold: integer,
15
+ # asset_id: uuid,
16
+ # amount: string / float,
17
+ # memo: string,
18
+ # }
19
+ RAW_TRANSACTION_ARGUMENTS = %i[utxos senders senders_threshold receivers receivers_threshold amount].freeze
20
+ def build_raw_transaction(**kwargs)
21
+ raise ArgumentError, "#{RAW_TRANSACTION_ARGUMENTS.join(', ')} are needed for build raw transaction" unless RAW_TRANSACTION_ARGUMENTS.all? { |param| kwargs.keys.include? param }
22
+
23
+ senders = kwargs[:senders]
24
+ senders_threshold = kwargs[:senders_threshold]
25
+ receivers = kwargs[:receivers]
26
+ receivers_threshold = kwargs[:receivers_threshold]
27
+ amount = kwargs[:amount]
28
+ asset_id = kwargs[:asset_id]
29
+ asset_mixin_id = kwargs[:asset_mixin_id]
30
+ utxos = kwargs[:utxos]
31
+ extra = kwargs[:extra]
32
+ access_token = kwargs[:access_token]
33
+ outputs = kwargs[:outputs] || []
34
+ hint = kwargs[:hint]
35
+ version = kwargs[:version] || LEGACY_TX_VERSION
36
+
37
+ raise 'access_token required!' if access_token.nil? && !senders.include?(config.app_id)
38
+
39
+ amount = amount.to_d.round(8)
40
+ input_amount = utxos.map(
41
+ &lambda { |utxo|
42
+ utxo['amount'].to_d
43
+ }
44
+ ).sum
45
+
46
+ if input_amount < amount
47
+ raise format(
48
+ 'not enough amount! %<input_amount>s < %<amount>s',
49
+ input_amount:,
50
+ amount:
51
+ )
52
+ end
53
+
54
+ inputs = utxos.map(
55
+ &lambda { |utx|
56
+ {
57
+ 'hash' => utx['transaction_hash'],
58
+ 'index' => utx['output_index']
59
+ }
60
+ }
61
+ )
62
+
63
+ if outputs.empty?
64
+ receivers_threshold = 1 if receivers.size == 1
65
+ output0 = build_output(
66
+ receivers:,
67
+ index: 0,
68
+ amount:,
69
+ threshold: receivers_threshold,
70
+ hint:
71
+ )
72
+ outputs.push output0
73
+
74
+ if input_amount > amount
75
+ output1 = build_output(
76
+ receivers: senders,
77
+ index: 1,
78
+ amount: input_amount - amount,
79
+ threshold: senders_threshold,
80
+ hint:
81
+ )
82
+ outputs.push output1
83
+ end
84
+ end
85
+
86
+ asset = asset_mixin_id || SHA3::Digest::SHA256.hexdigest(asset_id)
87
+ {
88
+ version:,
89
+ asset:,
90
+ inputs:,
91
+ outputs:,
92
+ extra:
93
+ }
94
+ end
95
+
96
+ # use safe transaction protocol instead
97
+ MULTISIG_TRANSACTION_ARGUMENTS = %i[asset_id receivers threshold amount].freeze
98
+ def create_multisig_transaction(pin, **options)
99
+ raise ArgumentError, "#{MULTISIG_TRANSACTION_ARGUMENTS.join(', ')} are needed for create multisig transaction" unless MULTISIG_TRANSACTION_ARGUMENTS.all? { |param| options.keys.include? param }
100
+
101
+ asset_id = options[:asset_id]
102
+ receivers = options[:receivers].sort
103
+ threshold = options[:threshold]
104
+ amount = format('%.8f', options[:amount].to_d.to_r)
105
+ memo = options[:memo]
106
+ trace_id = options[:trace_id] || SecureRandom.uuid
107
+
108
+ path = '/transactions'
109
+ payload = {
110
+ asset_id:,
111
+ opponent_multisig: {
112
+ receivers:,
113
+ threshold:
114
+ },
115
+ amount:,
116
+ trace_id:,
117
+ memo:
118
+ }
119
+
120
+ if pin.length > 6
121
+ payload[:pin_base64] = encrypt_tip_pin(pin, 'TIP:TRANSACTION:CREATE:', asset_id, receivers.join, threshold, amount, trace_id, memo)
122
+ else
123
+ payload[:pin] = encrypt_pin(pin)
124
+ end
125
+
126
+ client.post path, **payload
127
+ end
128
+
129
+ # use safe transaction protocol instead
130
+ MAINNET_TRANSACTION_ARGUMENTS = %i[asset_id opponent_id amount].freeze
131
+ def create_mainnet_transaction(pin, **options)
132
+ raise ArgumentError, "#{MAINNET_TRANSACTION_ARGUMENTS.join(', ')} are needed for create main net transactions" unless MAINNET_TRANSACTION_ARGUMENTS.all? { |param| options.keys.include? param }
133
+
134
+ asset_id = options[:asset_id]
135
+ opponent_id = options[:opponent_id]
136
+ amount = format('%.8f', options[:amount].to_d)
137
+ memo = options[:memo]
138
+ trace_id = options[:trace_id] || SecureRandom.uuid
139
+
140
+ path = '/transactions'
141
+ payload = {
142
+ asset_id:,
143
+ opponent_id:,
144
+ amount:,
145
+ trace_id:,
146
+ memo:
147
+ }
148
+
149
+ if pin.length > 6
150
+ payload[:pin_base64] = encrypt_tip_pin(pin, 'TIP:TRANSACTION:CREATE:', asset_id, opponent_id, amount, trace_id, memo)
151
+ else
152
+ payload[:pin] = encrypt_pin(pin)
153
+ end
154
+
155
+ client.post path, **payload
156
+ end
157
+
158
+ # use safe transaction protocol instead
159
+ def transactions(**options)
160
+ path = '/external/transactions'
161
+ params = {
162
+ limit: options[:limit],
163
+ offset: options[:offset],
164
+ asset: options[:asset],
165
+ destination: options[:destination],
166
+ tag: options[:tag]
167
+ }
168
+
169
+ client.get path, **params
170
+ end
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MixinBot
4
+ class API
5
+ module LegacyTransfer
6
+ TRANSFER_ARGUMENTS = %i[asset_id opponent_id amount].freeze
7
+ def create_transfer(pin, **kwargs)
8
+ raise ArgumentError, "#{TRANSFER_ARGUMENTS.join(', ')} are needed for create transfer" unless TRANSFER_ARGUMENTS.all? { |param| kwargs.keys.include? param }
9
+
10
+ asset_id = kwargs[:asset_id]
11
+ opponent_id = kwargs[:opponent_id]
12
+ amount = format('%.8f', kwargs[:amount].to_d.to_r).gsub(/\.?0+$/, '')
13
+ trace_id = kwargs[:trace_id] || SecureRandom.uuid
14
+ memo = kwargs[:memo] || ''
15
+
16
+ payload = {
17
+ asset_id:,
18
+ opponent_id:,
19
+ amount:,
20
+ trace_id:,
21
+ memo:
22
+ }
23
+
24
+ if pin.length > 6
25
+ pin_base64 = encrypt_tip_pin pin, 'TIP:TRANSFER:CREATE:', asset_id, opponent_id, amount, trace_id, memo
26
+ payload[:pin_base64] = pin_base64
27
+ else
28
+ encrypted_pin = encrypt_pin pin
29
+ payload[:pin] = encrypted_pin
30
+ end
31
+
32
+ path = '/transfers'
33
+ client.post path, **payload
34
+ end
35
+
36
+ def transfer(trace_id, access_token: nil)
37
+ path = format('/transfers/trace/%<trace_id>s', trace_id:)
38
+ client.get path, access_token:
39
+ end
40
+ end
41
+ end
42
+ end
@@ -3,37 +3,33 @@
3
3
  module MixinBot
4
4
  class API
5
5
  module Me
6
- # https://developers.mixin.one/api/beta-mixin-message/read-profile/
7
6
  def me(access_token: nil)
8
7
  path = '/me'
9
- access_token ||= access_token('GET', path, '')
10
- authorization = format('Bearer %<access_token>s', access_token: access_token)
11
- client.get(path, headers: { 'Authorization': authorization })
8
+ client.get path, access_token:
12
9
  end
13
- alias read_me me
14
10
 
15
- # https://developers.mixin.one/api/beta-mixin-message/update-profile/
16
11
  # avatar_base64:
17
- # String: Base64 of image, supports format png, jpeg and gif, base64 image size > 1024.
18
- def update_me(full_name:, avatar_base64: nil, access_token: nil)
12
+ # String: Base64 of image, supports format png, jpeg and gif, base64 image size > 1024.
13
+ def update_me(**kwargs)
19
14
  path = '/me'
20
15
  payload = {
21
- full_name: full_name,
22
- avatar_base64: avatar_base64
16
+ full_name: kwargs[:full_name],
17
+ avatar_base64: kwargs[:avatar_base64],
18
+ access_token: kwargs[:access_token]
23
19
  }
24
- access_token ||= access_token('POST', path, payload.to_json)
25
- authorization = format('Bearer %<access_token>s', access_token: access_token)
26
- client.post(path, headers: { 'Authorization': authorization }, json: payload)
20
+ client.post path, **payload
27
21
  end
28
22
 
29
23
  # https://developers.mixin.one/api/beta-mixin-message/friends/
30
24
  def friends(access_token: nil)
31
25
  path = '/friends'
32
- access_token ||= access_token('GET', path, '')
33
- authorization = format('Bearer %<access_token>s', access_token: access_token)
34
- client.get(path, headers: { 'Authorization': authorization })
26
+ client.get path, access_token:
27
+ end
28
+
29
+ def safe_me(access_token: nil)
30
+ path = '/safe/me'
31
+ client.get path, access_token:
35
32
  end
36
- alias read_friends friends
37
33
  end
38
34
  end
39
35
  end