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
@@ -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
|