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
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MixinBot
4
+ class Configuration
5
+ CONFIGURABLE_ATTRS = %i[
6
+ app_id
7
+ client_secret
8
+ session_id
9
+ session_private_key
10
+ server_public_key
11
+ spend_key
12
+ pin
13
+ api_host
14
+ blaze_host
15
+ session_private_key_curve25519
16
+ server_public_key_curve25519
17
+ debug
18
+ ].freeze
19
+ attr_accessor(*CONFIGURABLE_ATTRS)
20
+
21
+ def initialize(**kwargs)
22
+ @app_id = kwargs[:app_id] || kwargs[:client_id]
23
+ @client_secret = kwargs[:client_secret]
24
+ @session_id = kwargs[:session_id]
25
+ @api_host = kwargs[:api_host] || 'api.mixin.one'
26
+ @blaze_host = kwargs[:blaze_host] || 'blaze.mixin.one'
27
+ @debug = kwargs[:debug] || false
28
+
29
+ self.session_private_key = kwargs[:session_private_key] || kwargs[:private_key]
30
+ self.server_public_key = kwargs[:server_public_key] || kwargs[:pin_token]
31
+ self.spend_key = kwargs[:spend_key]
32
+ self.pin = kwargs[:pin] || spend_key
33
+ end
34
+
35
+ def valid?
36
+ %i[app_id session_id session_private_key server_public_key].all? do |attr|
37
+ send(attr).present?
38
+ end
39
+ end
40
+
41
+ def session_private_key=(key)
42
+ return if key.blank?
43
+
44
+ _private_key = decode_key key
45
+ @session_private_key =
46
+ if _private_key.size == 32
47
+ JOSE::JWA::Ed25519.keypair(_private_key).last
48
+ else
49
+ _private_key
50
+ end
51
+
52
+ @session_private_key_curve25519 = JOSE::JWA::Ed25519.sk_to_curve25519(@session_private_key) if @session_private_key.size == 64
53
+ end
54
+
55
+ def server_public_key=(key)
56
+ return if key.blank?
57
+
58
+ @server_public_key = decode_key key
59
+ # HEX encoded
60
+ @server_public_key_curve25519 =
61
+ if key.match?(/\A[\h]{32,}\z/i)
62
+ JOSE::JWA::Ed25519.pk_to_curve25519 @server_public_key
63
+ else
64
+ server_public_key
65
+ end
66
+ end
67
+
68
+ def spend_key=(key)
69
+ return if key.blank?
70
+
71
+ _private_key = decode_key key
72
+ @spend_key =
73
+ if _private_key.size == 32
74
+ JOSE::JWA::Ed25519.keypair(_private_key).last
75
+ else
76
+ _private_key
77
+ end
78
+ end
79
+
80
+ def pin=(key)
81
+ return if key.blank?
82
+
83
+ _private_key = decode_key key
84
+ @pin =
85
+ if _private_key.size == 32
86
+ JOSE::JWA::Ed25519.keypair(_private_key).last
87
+ else
88
+ _private_key
89
+ end
90
+ end
91
+
92
+ private
93
+
94
+ def decode_key(key)
95
+ MixinBot.utils.decode_key key
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,174 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MixinBot
4
+ class Nfo
5
+ NFT_MEMO_PREFIX = 'NFO'
6
+ NFT_MEMO_VERSION = 0x00
7
+ NFT_MEMO_DEFAULT_CHAIN = '43d61dcd-e413-450d-80b8-101d5e903357'
8
+ NFT_MEMO_DEFAULT_CLASS = '3c8c161a18ae2c8b14fda1216fff7da88c419b5d'
9
+ NULL_UUID = '00000000-0000-0000-0000-000000000000'
10
+
11
+ attr_reader :prefix, :version, :raw
12
+ attr_accessor :mask, :chain, :nm_class, :collection, :token, :extra, :memo, :hex
13
+
14
+ def initialize(**kwargs)
15
+ @prefix = NFT_MEMO_PREFIX
16
+ @version = NFT_MEMO_VERSION
17
+ @mask = kwargs[:mask] || 0
18
+ @chain = kwargs[:chain] || NFT_MEMO_DEFAULT_CHAIN
19
+ @nm_class = kwargs[:nm_class] || NFT_MEMO_DEFAULT_CLASS
20
+ @collection = kwargs[:collection] || NULL_UUID
21
+ @token = kwargs[:token].presence&.to_i
22
+ @extra = kwargs[:extra]
23
+ @memo = kwargs[:memo]
24
+ @hex = kwargs[:hex]
25
+ end
26
+
27
+ def mint_memo
28
+ raise MixinBot::InvalidNfoFormatError, 'token is required' if token.blank?
29
+ raise MixinBot::InvalidNfoFormatError, 'extra must be 256-bit string' if extra.blank? || extra.size != 64
30
+
31
+ @collection = NULL_UUID if collection.blank?
32
+ @chain = NFT_MEMO_DEFAULT_CHAIN
33
+ @nm_class = NFT_MEMO_DEFAULT_CLASS
34
+ mark 0
35
+ encode
36
+
37
+ memo
38
+ end
39
+
40
+ def unique_token_id
41
+ bytes = []
42
+ bytes += MixinBot::UUID.new(hex: chain).packed.bytes
43
+ bytes += [nm_class].pack('H*').bytes
44
+ bytes += MixinBot::UUID.new(hex: collection).packed.bytes
45
+ bytes += MixinBot.utils.encode_int token
46
+
47
+ md5 = Digest::MD5.new
48
+ md5.update bytes.pack('c*')
49
+ digest = [md5.hexdigest].pack('H*').bytes
50
+
51
+ digest[6] = (digest[6] & 0x0f) | 0x30
52
+ digest[8] = (digest[8] & 0x3f) | 0x80
53
+
54
+ hex = digest.pack('c*').unpack1('H*')
55
+
56
+ MixinBot::UUID.new(hex:).unpacked
57
+ end
58
+
59
+ def mark(*indexes)
60
+ indexes.map do |index|
61
+ raise ArgumentError, "invalid NFO memo index #{index}" if index >= 64 || index.negative?
62
+
63
+ @mask = mask ^ (1 << index)
64
+ end
65
+ end
66
+
67
+ def encode
68
+ bytes = []
69
+
70
+ bytes += prefix.bytes
71
+ bytes += [version]
72
+
73
+ if mask.zero?
74
+ bytes += [0]
75
+ else
76
+ bytes += [1]
77
+ bytes += MixinBot.utils.encode_uint_64 mask
78
+ bytes += MixinBot::UUID.new(hex: chain).packed.bytes
79
+
80
+ class_bytes = [nm_class].pack('H*').bytes
81
+ bytes += MixinBot.utils.encode_int class_bytes.size
82
+ bytes += class_bytes
83
+
84
+ collection_bytes = collection.split('-').pack('H* H* H* H* H*').bytes
85
+ bytes += MixinBot.utils.encode_int collection_bytes.size
86
+ bytes += collection_bytes
87
+
88
+ # token_bytes = memo[:token].split('-').pack('H* H* H* H* H*').bytes
89
+ token_bytes = MixinBot.utils.encode_int token
90
+ bytes += MixinBot.utils.encode_int token_bytes.size
91
+ bytes += token_bytes
92
+ end
93
+
94
+ extra_bytes = [extra].pack('H*').bytes
95
+ bytes += MixinBot.utils.encode_int extra_bytes.size
96
+ bytes += extra_bytes
97
+
98
+ @raw = bytes.pack('C*')
99
+ @hex = raw.unpack1('H*')
100
+ @memo = Base64.urlsafe_encode64 raw, padding: false
101
+
102
+ self
103
+ end
104
+
105
+ def decode
106
+ @raw =
107
+ if memo.present?
108
+ Base64.urlsafe_decode64 memo
109
+ elsif hex.present?
110
+ [hex].pack('H*')
111
+ else
112
+ raise InvalidNfoFormatError, 'memo or hex is required'
113
+ end
114
+
115
+ @hex = raw.unpack1('H*') if hex.blank?
116
+ @memo = Base64.urlsafe_encode64 raw, padding: false if memo.blank?
117
+
118
+ decode_bytes
119
+ self
120
+ end
121
+
122
+ def decode_bytes
123
+ bytes = raw.bytes
124
+
125
+ _prefix = bytes.shift(3).pack('C*')
126
+ raise MixinBot::InvalidNfoFormatError, "NFO prefix #{_prefix}" if _prefix != prefix
127
+
128
+ _version = bytes.shift
129
+ raise MixinBot::InvalidNfoFormatError, "NFO version #{prefix}" if _version != version
130
+
131
+ hint = bytes.shift
132
+ if hint == 1
133
+ @mask = bytes.shift(8).reverse.pack('C*').unpack1('Q*')
134
+
135
+ @chain = MixinBot::UUID.new(hex: bytes.shift(16).pack('C*').unpack1('H*')).unpacked
136
+
137
+ class_length = bytes.shift
138
+ @nm_class = bytes.shift(class_length).pack('C*').unpack1('H*')
139
+
140
+ collection_length = bytes.shift
141
+ @collection = MixinBot::UUID.new(hex: bytes.shift(collection_length).pack('C*').unpack1('H*')).unpacked
142
+
143
+ token_length = bytes.shift
144
+ @token = MixinBot.utils.decode_int bytes.shift(token_length)
145
+ end
146
+
147
+ extra_length = bytes.shift
148
+ @extra = bytes.shift(extra_length).pack('C*').unpack1('H*')
149
+
150
+ self
151
+ end
152
+
153
+ def to_h
154
+ hash = {
155
+ prefix:,
156
+ version:,
157
+ mask:,
158
+ chain:,
159
+ class: nm_class,
160
+ collection:,
161
+ token:,
162
+ extra:,
163
+ memo:,
164
+ hex:
165
+ }
166
+
167
+ hash.each do |key, value|
168
+ hash.delete key if value.blank?
169
+ end
170
+
171
+ hash
172
+ end
173
+ end
174
+ end