mixin_bot 1.4.0 → 2.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/AGENTS.md +75 -0
- data/API_COVERAGE.md +143 -0
- data/CHANGELOG.md +112 -0
- data/README.md +375 -0
- data/docs/agent/cli.md +149 -0
- data/docs/agent/cookbook.md +152 -0
- data/examples/blaze.rb +43 -0
- data/examples/config.yml.example +21 -0
- data/lib/mixin_bot/address.rb +43 -3
- data/lib/mixin_bot/api/app.rb +7 -0
- data/lib/mixin_bot/api/asset.rb +114 -3
- data/lib/mixin_bot/api/auth.rb +19 -10
- data/lib/mixin_bot/api/blaze.rb +81 -0
- data/lib/mixin_bot/api/chain.rb +94 -0
- data/lib/mixin_bot/api/code.rb +16 -0
- data/lib/mixin_bot/api/computer_api.rb +60 -0
- data/lib/mixin_bot/api/conversation.rb +7 -1
- data/lib/mixin_bot/api/deposit.rb +12 -0
- data/lib/mixin_bot/api/encrypted_message.rb +1 -1
- data/lib/mixin_bot/api/fiat.rb +12 -0
- data/lib/mixin_bot/api/inscription.rb +2 -2
- data/lib/mixin_bot/api/legacy_collectible.rb +26 -27
- data/lib/mixin_bot/api/legacy_multisig.rb +20 -21
- data/lib/mixin_bot/api/legacy_output.rb +10 -3
- data/lib/mixin_bot/api/legacy_payment.rb +2 -0
- data/lib/mixin_bot/api/legacy_snapshot.rb +16 -0
- data/lib/mixin_bot/api/legacy_transaction.rb +28 -13
- data/lib/mixin_bot/api/legacy_transfer.rb +11 -8
- data/lib/mixin_bot/api/legacy_user.rb +51 -0
- data/lib/mixin_bot/api/me.rb +99 -3
- data/lib/mixin_bot/api/message.rb +18 -27
- data/lib/mixin_bot/api/multisig.rb +19 -0
- data/lib/mixin_bot/api/network.rb +17 -0
- data/lib/mixin_bot/api/network_asset.rb +27 -0
- data/lib/mixin_bot/api/output.rb +1 -1
- data/lib/mixin_bot/api/pin.rb +16 -3
- data/lib/mixin_bot/api/pin_payload.rb +26 -0
- data/lib/mixin_bot/api/session.rb +14 -0
- data/lib/mixin_bot/api/snapshot.rb +6 -0
- data/lib/mixin_bot/api/tip.rb +74 -1
- data/lib/mixin_bot/api/transaction.rb +106 -17
- data/lib/mixin_bot/api/transfer.rb +141 -14
- data/lib/mixin_bot/api/turn.rb +12 -0
- data/lib/mixin_bot/api/user.rb +148 -45
- data/lib/mixin_bot/api/withdraw.rb +24 -23
- data/lib/mixin_bot/api.rb +248 -3
- data/lib/mixin_bot/bot_auth.rb +71 -0
- data/lib/mixin_bot/cli/api.rb +224 -143
- data/lib/mixin_bot/cli/base.rb +77 -0
- data/lib/mixin_bot/cli/call.rb +71 -0
- data/lib/mixin_bot/cli/errors.rb +56 -0
- data/lib/mixin_bot/cli/node.rb +9 -2
- data/lib/mixin_bot/cli/output.rb +196 -0
- data/lib/mixin_bot/cli/schema.rb +274 -0
- data/lib/mixin_bot/cli/schema_command.rb +21 -0
- data/lib/mixin_bot/cli/utils.rb +114 -18
- data/lib/mixin_bot/cli.rb +124 -48
- data/lib/mixin_bot/client/error_mapper.rb +40 -0
- data/lib/mixin_bot/client.rb +94 -64
- data/lib/mixin_bot/computer.rb +132 -0
- data/lib/mixin_bot/configuration.rb +108 -1
- data/lib/mixin_bot/errors.rb +102 -0
- data/lib/mixin_bot/models/address.rb +11 -0
- data/lib/mixin_bot/models/api_envelope.rb +67 -0
- data/lib/mixin_bot/models/asset.rb +11 -0
- data/lib/mixin_bot/models/ghost_keys.rb +14 -0
- data/lib/mixin_bot/models/output.rb +11 -0
- data/lib/mixin_bot/models/safe_multisig_request.rb +11 -0
- data/lib/mixin_bot/models/sequencer_transaction_request.rb +11 -0
- data/lib/mixin_bot/models/user.rb +11 -0
- data/lib/mixin_bot/models.rb +10 -0
- data/lib/mixin_bot/monitor.rb +77 -0
- data/lib/mixin_bot/transaction/buffer.rb +34 -0
- data/lib/mixin_bot/transaction/decoder.rb +227 -0
- data/lib/mixin_bot/transaction/encoder.rb +255 -0
- data/lib/mixin_bot/transaction.rb +6 -475
- data/lib/mixin_bot/url_scheme.rb +63 -0
- data/lib/mixin_bot/utils/address.rb +17 -80
- data/lib/mixin_bot/utils/crypto.rb +173 -1
- data/lib/mixin_bot/utils/decoder.rb +1 -1
- data/lib/mixin_bot/utils/encoder.rb +13 -0
- data/lib/mixin_bot/utils.rb +45 -0
- data/lib/mixin_bot/uuid.rb +78 -1
- data/lib/mixin_bot/version.rb +11 -1
- data/lib/mixin_bot.rb +172 -18
- data/lib/mvm/bridge.rb +46 -0
- data/lib/mvm/client.rb +60 -0
- data/lib/mvm/nft.rb +4 -2
- data/lib/mvm/registry.rb +2 -1
- data/lib/mvm.rb +93 -0
- data/lib/tasks/api_coverage.rake +20 -0
- data/llms.txt +29 -0
- metadata +77 -9
|
@@ -2,7 +2,65 @@
|
|
|
2
2
|
|
|
3
3
|
module MixinBot
|
|
4
4
|
module Utils
|
|
5
|
+
##
|
|
6
|
+
# Cryptographic utility methods for Mixin Network operations.
|
|
7
|
+
#
|
|
8
|
+
# This module provides essential cryptographic functions including:
|
|
9
|
+
# - JWT token generation for API authentication
|
|
10
|
+
# - Ed25519 and RSA key generation
|
|
11
|
+
# - PIN encryption/decryption
|
|
12
|
+
# - Transaction signing
|
|
13
|
+
# - UUID generation and derivation
|
|
14
|
+
# - Ghost key derivation for Safe API
|
|
15
|
+
#
|
|
16
|
+
# == Key Types
|
|
17
|
+
#
|
|
18
|
+
# Mixin Network uses several types of cryptographic keys:
|
|
19
|
+
#
|
|
20
|
+
# [Session Key] Ed25519 or RSA key for API authentication
|
|
21
|
+
# [Spend Key] Ed25519 key for signing transactions (Safe API)
|
|
22
|
+
# [View Key] Ed25519 key for viewing transactions
|
|
23
|
+
# [PIN Key] For legacy PIN operations
|
|
24
|
+
#
|
|
25
|
+
# == Signature Algorithm
|
|
26
|
+
#
|
|
27
|
+
# Safe API uses Ed25519 with Blake3 hashing for transaction signing.
|
|
28
|
+
# This provides quantum-resistant security with compact signatures.
|
|
29
|
+
#
|
|
5
30
|
module Crypto
|
|
31
|
+
##
|
|
32
|
+
# Generates a JWT access token for API authentication.
|
|
33
|
+
#
|
|
34
|
+
# Creates a signed JWT token containing request details and credentials.
|
|
35
|
+
# The token is used in the Authorization header for API requests.
|
|
36
|
+
#
|
|
37
|
+
# Token payload includes:
|
|
38
|
+
# - uid: user/bot ID
|
|
39
|
+
# - sid: session ID
|
|
40
|
+
# - iat: issued at timestamp
|
|
41
|
+
# - exp: expiration timestamp
|
|
42
|
+
# - jti: unique token ID
|
|
43
|
+
# - sig: SHA-256 hash of request (method + uri + body)
|
|
44
|
+
# - scp: scope (usually 'FULL')
|
|
45
|
+
#
|
|
46
|
+
# @param method [String] HTTP method (GET, POST, etc.)
|
|
47
|
+
# @param uri [String] request URI path
|
|
48
|
+
# @param body [String] request body (empty for GET)
|
|
49
|
+
# @param kwargs [Hash] additional options
|
|
50
|
+
# @option kwargs [Integer] :exp_in (600) token lifetime in seconds
|
|
51
|
+
# @option kwargs [String] :scp ('FULL') token scope
|
|
52
|
+
# @option kwargs [String] :app_id bot application ID
|
|
53
|
+
# @option kwargs [String] :session_id session ID
|
|
54
|
+
# @option kwargs [String] :private_key session private key
|
|
55
|
+
#
|
|
56
|
+
# @return [String] JWT token
|
|
57
|
+
#
|
|
58
|
+
# @raise [ConfigurationNotValidError] if private_key is missing
|
|
59
|
+
#
|
|
60
|
+
# @example
|
|
61
|
+
# token = MixinBot.utils.access_token('GET', '/me', '')
|
|
62
|
+
# # Use in request: Authorization: Bearer #{token}
|
|
63
|
+
#
|
|
6
64
|
def access_token(method, uri, body = '', **kwargs)
|
|
7
65
|
sig = Digest::SHA256.hexdigest(method + uri + body.to_s)
|
|
8
66
|
iat = Time.now.utc.to_i
|
|
@@ -37,6 +95,19 @@ module MixinBot
|
|
|
37
95
|
JOSE::JWT.sign(jwk, jws, jwt).compact
|
|
38
96
|
end
|
|
39
97
|
|
|
98
|
+
##
|
|
99
|
+
# Generates a new Ed25519 keypair.
|
|
100
|
+
#
|
|
101
|
+
# Ed25519 is the recommended key type for Mixin Network.
|
|
102
|
+
# It provides strong security with compact keys and signatures.
|
|
103
|
+
#
|
|
104
|
+
# @return [Hash] hash containing :private_key and :public_key (Base64-encoded)
|
|
105
|
+
#
|
|
106
|
+
# @example
|
|
107
|
+
# keys = MixinBot.utils.generate_ed25519_key
|
|
108
|
+
# puts "Private: #{keys[:private_key]}"
|
|
109
|
+
# puts "Public: #{keys[:public_key]}"
|
|
110
|
+
#
|
|
40
111
|
def generate_ed25519_key
|
|
41
112
|
ed25519_key = JOSE::JWA::Ed25519.keypair
|
|
42
113
|
{
|
|
@@ -45,6 +116,18 @@ module MixinBot
|
|
|
45
116
|
}
|
|
46
117
|
end
|
|
47
118
|
|
|
119
|
+
##
|
|
120
|
+
# Generates a new RSA keypair.
|
|
121
|
+
#
|
|
122
|
+
# RSA keys are supported for backward compatibility but
|
|
123
|
+
# Ed25519 is recommended for new applications.
|
|
124
|
+
#
|
|
125
|
+
# @return [Hash] hash containing :private_key and :public_key (PEM format)
|
|
126
|
+
#
|
|
127
|
+
# @example
|
|
128
|
+
# keys = MixinBot.utils.generate_rsa_key
|
|
129
|
+
# puts keys[:private_key]
|
|
130
|
+
#
|
|
48
131
|
def generate_rsa_key
|
|
49
132
|
rsa_key = OpenSSL::PKey::RSA.new 1024
|
|
50
133
|
{
|
|
@@ -53,10 +136,26 @@ module MixinBot
|
|
|
53
136
|
}
|
|
54
137
|
end
|
|
55
138
|
|
|
139
|
+
##
|
|
140
|
+
# Derives a public key from a private key.
|
|
141
|
+
#
|
|
142
|
+
# Used internally for cryptographic operations.
|
|
143
|
+
#
|
|
144
|
+
# @param key [String] the private key (64 bytes)
|
|
145
|
+
# @return [String] the derived public key
|
|
146
|
+
#
|
|
56
147
|
def shared_public_key(key)
|
|
57
148
|
(JOSE::JWA::Edwards25519Point.stdbase * scalar_from_bytes(key[...64]).x.to_i).encode
|
|
58
149
|
end
|
|
59
150
|
|
|
151
|
+
##
|
|
152
|
+
# Converts raw bytes to a scalar for Ed25519 operations.
|
|
153
|
+
#
|
|
154
|
+
# Used internally for cryptographic calculations.
|
|
155
|
+
#
|
|
156
|
+
# @param raw [String] raw bytes
|
|
157
|
+
# @return [JOSE::JWA::FieldElement] the scalar value
|
|
158
|
+
#
|
|
60
159
|
def scalar_from_bytes(raw)
|
|
61
160
|
JOSE::JWA::FieldElement.new(
|
|
62
161
|
# https://github.com/potatosalad/ruby-jose/blob/e1be589b889f1e59ac233a5d19a3fa13f1e4b8a0/lib/jose/jwa/x25519.rb#L122C14-L122C48
|
|
@@ -100,6 +199,29 @@ module MixinBot
|
|
|
100
199
|
MixinBot::UUID.new(raw: cipher).unpacked
|
|
101
200
|
end
|
|
102
201
|
|
|
202
|
+
##
|
|
203
|
+
# Generates a unique UUID from multiple UUIDs.
|
|
204
|
+
#
|
|
205
|
+
# Creates a deterministic UUID by combining multiple UUIDs.
|
|
206
|
+
# The result is always the same for the same set of input UUIDs,
|
|
207
|
+
# regardless of order (after sorting).
|
|
208
|
+
#
|
|
209
|
+
# This is used for:
|
|
210
|
+
# - Creating conversation IDs
|
|
211
|
+
# - Generating multisig addresses
|
|
212
|
+
# - Creating deterministic identifiers
|
|
213
|
+
#
|
|
214
|
+
# @param uuids [Array<String>] array of UUIDs to combine
|
|
215
|
+
# @return [String] the unique combined UUID
|
|
216
|
+
#
|
|
217
|
+
# @example
|
|
218
|
+
# uuid = MixinBot.utils.unique_uuid(
|
|
219
|
+
# 'user1-uuid',
|
|
220
|
+
# 'user2-uuid',
|
|
221
|
+
# 'user3-uuid'
|
|
222
|
+
# )
|
|
223
|
+
# puts uuid # Always the same for these inputs
|
|
224
|
+
#
|
|
103
225
|
def unique_uuid(*uuids)
|
|
104
226
|
uuids = uuids.flatten.compact
|
|
105
227
|
uuids.sort
|
|
@@ -111,6 +233,28 @@ module MixinBot
|
|
|
111
233
|
r
|
|
112
234
|
end
|
|
113
235
|
|
|
236
|
+
##
|
|
237
|
+
# Generates a group conversation ID.
|
|
238
|
+
#
|
|
239
|
+
# Creates a deterministic conversation ID for a group based on:
|
|
240
|
+
# - Owner ID
|
|
241
|
+
# - Group name
|
|
242
|
+
# - Participant IDs
|
|
243
|
+
# - Random ID (optional, for creating different groups with same members)
|
|
244
|
+
#
|
|
245
|
+
# @param user_ids [Array<String>] array of participant user IDs
|
|
246
|
+
# @param name [String] the group name
|
|
247
|
+
# @param owner_id [String] the group owner's user ID
|
|
248
|
+
# @param random_id [String, nil] optional random ID for uniqueness
|
|
249
|
+
# @return [String] the conversation UUID
|
|
250
|
+
#
|
|
251
|
+
# @example
|
|
252
|
+
# conv_id = MixinBot.utils.generate_group_conversation_id(
|
|
253
|
+
# user_ids: ['user1', 'user2', 'user3'],
|
|
254
|
+
# name: 'My Group',
|
|
255
|
+
# owner_id: 'owner-id'
|
|
256
|
+
# )
|
|
257
|
+
#
|
|
114
258
|
def generate_group_conversation_id(user_ids:, name:, owner_id:, random_id: nil)
|
|
115
259
|
random_id ||= SecureRandom.uuid
|
|
116
260
|
|
|
@@ -129,6 +273,33 @@ module MixinBot
|
|
|
129
273
|
gid
|
|
130
274
|
end
|
|
131
275
|
|
|
276
|
+
def generate_user_checksum(sessions)
|
|
277
|
+
list = Array(sessions).map do |s|
|
|
278
|
+
s.is_a?(Hash) ? s['session_id'] || s[:session_id] : s.session_id
|
|
279
|
+
end.compact.sort
|
|
280
|
+
return '' if list.empty?
|
|
281
|
+
|
|
282
|
+
Digest::MD5.hexdigest(list.join)
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def chunked(source, size)
|
|
286
|
+
source.each_slice(size).to_a
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def make_unique_string_slice(strings)
|
|
290
|
+
strings.uniq
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def unique_object_id(*args)
|
|
294
|
+
md5 = Digest::MD5.new
|
|
295
|
+
args.flatten.compact.each { |s| md5 << s.to_s }
|
|
296
|
+
digest = md5.digest
|
|
297
|
+
digest = digest.dup
|
|
298
|
+
digest[6] = ((digest[6].ord & 0x0f) | 0x30).chr
|
|
299
|
+
digest[8] = ((digest[8].ord & 0x3f) | 0x80).chr
|
|
300
|
+
MixinBot::UUID.new(raw: digest).unpacked
|
|
301
|
+
end
|
|
302
|
+
|
|
132
303
|
def generate_trace_from_hash(hash, output_index = 0)
|
|
133
304
|
md5 = Digest::MD5.new
|
|
134
305
|
md5 << hash
|
|
@@ -153,7 +324,8 @@ module MixinBot
|
|
|
153
324
|
decode_cipher.update(cipher)
|
|
154
325
|
end
|
|
155
326
|
|
|
156
|
-
# use timestamp(timestamp) for iterator as default: must be bigger than the previous,
|
|
327
|
+
# use timestamp(timestamp) for iterator as default: must be bigger than the previous,
|
|
328
|
+
# the first time must be greater than 0. After a new session created, it will be reset to 0.
|
|
157
329
|
def encrypt_pin(pin, **kwargs)
|
|
158
330
|
pin = MixinBot.utils.decode_key pin
|
|
159
331
|
|
|
@@ -37,6 +37,19 @@ module MixinBot
|
|
|
37
37
|
[int].pack('Q*').bytes.reverse
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
+
##
|
|
41
|
+
# Big-endian varint-style encoding for deposit amounts (integer or decimal string).
|
|
42
|
+
#
|
|
43
|
+
def bytes_of(amount)
|
|
44
|
+
int =
|
|
45
|
+
case amount
|
|
46
|
+
when Integer then amount
|
|
47
|
+
when String then Integer(amount, 10)
|
|
48
|
+
else amount.to_i
|
|
49
|
+
end
|
|
50
|
+
encode_int(int)
|
|
51
|
+
end
|
|
52
|
+
|
|
40
53
|
def encode_int(int)
|
|
41
54
|
raise ArgumentError, 'not integer' unless int.is_a?(Integer)
|
|
42
55
|
|
data/lib/mixin_bot/utils.rb
CHANGED
|
@@ -6,6 +6,51 @@ require_relative 'utils/decoder'
|
|
|
6
6
|
require_relative 'utils/encoder'
|
|
7
7
|
|
|
8
8
|
module MixinBot
|
|
9
|
+
##
|
|
10
|
+
# Utility module providing various helper methods for Mixin Network operations.
|
|
11
|
+
#
|
|
12
|
+
# This module aggregates utility methods from several sub-modules:
|
|
13
|
+
#
|
|
14
|
+
# == Sub-modules
|
|
15
|
+
#
|
|
16
|
+
# [MixinBot::Utils::Address] Address-related utilities
|
|
17
|
+
# - Main address handling
|
|
18
|
+
# - Ghost key derivation
|
|
19
|
+
# - Address validation
|
|
20
|
+
#
|
|
21
|
+
# [MixinBot::Utils::Crypto] Cryptographic operations
|
|
22
|
+
# - JWT token generation
|
|
23
|
+
# - Key generation and management
|
|
24
|
+
# - Transaction signing
|
|
25
|
+
# - PIN encryption
|
|
26
|
+
# - UUID generation
|
|
27
|
+
#
|
|
28
|
+
# [MixinBot::Utils::Decoder] Data decoding utilities
|
|
29
|
+
# - Integer decoding
|
|
30
|
+
# - Transaction decoding
|
|
31
|
+
# - Key decoding
|
|
32
|
+
#
|
|
33
|
+
# [MixinBot::Utils::Encoder] Data encoding utilities
|
|
34
|
+
# - Integer encoding
|
|
35
|
+
# - Transaction encoding
|
|
36
|
+
# - Binary packing
|
|
37
|
+
#
|
|
38
|
+
# == Usage
|
|
39
|
+
#
|
|
40
|
+
# Access utilities through the MixinBot.utils shortcut:
|
|
41
|
+
#
|
|
42
|
+
# # Generate a unique UUID
|
|
43
|
+
# uuid = MixinBot.utils.unique_uuid(uuid1, uuid2)
|
|
44
|
+
#
|
|
45
|
+
# # Generate access token
|
|
46
|
+
# token = MixinBot.utils.access_token('GET', '/me', '')
|
|
47
|
+
#
|
|
48
|
+
# # Encode a transaction
|
|
49
|
+
# raw = MixinBot.utils.encode_raw_transaction(txn)
|
|
50
|
+
#
|
|
51
|
+
# # Decode a key
|
|
52
|
+
# key = MixinBot.utils.decode_key(encoded_key)
|
|
53
|
+
#
|
|
9
54
|
module Utils
|
|
10
55
|
extend MixinBot::Utils::Address
|
|
11
56
|
extend MixinBot::Utils::Crypto
|
data/lib/mixin_bot/uuid.rb
CHANGED
|
@@ -1,9 +1,66 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module MixinBot
|
|
4
|
+
##
|
|
5
|
+
# Utility class for handling Mixin Network UUID format conversions.
|
|
6
|
+
#
|
|
7
|
+
# Mixin Network uses UUIDs extensively for identifying:
|
|
8
|
+
# - Users and bots
|
|
9
|
+
# - Assets
|
|
10
|
+
# - Transactions and traces
|
|
11
|
+
# - Conversations
|
|
12
|
+
#
|
|
13
|
+
# == Format Conversions
|
|
14
|
+
#
|
|
15
|
+
# This class handles conversions between:
|
|
16
|
+
# - Standard UUID format: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" (36 chars)
|
|
17
|
+
# - Packed binary format: 16 bytes
|
|
18
|
+
# - Hex format without dashes: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" (32 chars)
|
|
19
|
+
#
|
|
20
|
+
# == Usage
|
|
21
|
+
#
|
|
22
|
+
# # From hex format to UUID
|
|
23
|
+
# uuid = MixinBot::UUID.new(hex: '965e5c6e434c3fa9b780c50f43cd955c')
|
|
24
|
+
# uuid.unpacked
|
|
25
|
+
# # => "965e5c6e-434c-3fa9-b780-c50f43cd955c"
|
|
26
|
+
#
|
|
27
|
+
# # From UUID to packed binary
|
|
28
|
+
# uuid = MixinBot::UUID.new(hex: '965e5c6e-434c-3fa9-b780-c50f43cd955c')
|
|
29
|
+
# uuid.packed
|
|
30
|
+
# # => "\x96^\\nC<?\xA9\xB7\x80\xC5\x0FC\xCD\x95\\"
|
|
31
|
+
#
|
|
32
|
+
# # From packed binary to UUID
|
|
33
|
+
# uuid = MixinBot::UUID.new(raw: binary_data)
|
|
34
|
+
# uuid.unpacked
|
|
35
|
+
# # => "965e5c6e-434c-3fa9-b780-c50f43cd955c"
|
|
36
|
+
#
|
|
4
37
|
class UUID
|
|
5
|
-
|
|
38
|
+
##
|
|
39
|
+
# @return [String] the UUID in hex format (with or without dashes)
|
|
40
|
+
attr_accessor :hex
|
|
6
41
|
|
|
42
|
+
##
|
|
43
|
+
# @return [String] the UUID in packed binary format (16 bytes)
|
|
44
|
+
attr_accessor :raw
|
|
45
|
+
|
|
46
|
+
##
|
|
47
|
+
# Initializes a new UUID instance.
|
|
48
|
+
#
|
|
49
|
+
# Provide either :hex or :raw parameter. The other format can be
|
|
50
|
+
# obtained via #packed or #unpacked methods.
|
|
51
|
+
#
|
|
52
|
+
# @param args [Hash] initialization options
|
|
53
|
+
# @option args [String] :hex the UUID in hex format (with or without dashes)
|
|
54
|
+
# @option args [String] :raw the UUID in packed binary format (16 bytes)
|
|
55
|
+
#
|
|
56
|
+
# @raise [MixinBot::InvalidUuidFormatError] if format is invalid
|
|
57
|
+
#
|
|
58
|
+
# @example From hex
|
|
59
|
+
# uuid = MixinBot::UUID.new(hex: '965e5c6e-434c-3fa9-b780-c50f43cd955c')
|
|
60
|
+
#
|
|
61
|
+
# @example From raw binary
|
|
62
|
+
# uuid = MixinBot::UUID.new(raw: binary_string)
|
|
63
|
+
#
|
|
7
64
|
def initialize(**args)
|
|
8
65
|
args = args.with_indifferent_access
|
|
9
66
|
|
|
@@ -14,6 +71,16 @@ module MixinBot
|
|
|
14
71
|
raise MixinBot::InvalidUuidFormatError if hex.present? && hex.gsub('-', '').size != 32
|
|
15
72
|
end
|
|
16
73
|
|
|
74
|
+
##
|
|
75
|
+
# Returns the UUID in packed binary format (16 bytes).
|
|
76
|
+
#
|
|
77
|
+
# @return [String] 16-byte binary string
|
|
78
|
+
#
|
|
79
|
+
# @example
|
|
80
|
+
# uuid = MixinBot::UUID.new(hex: '965e5c6e-434c-3fa9-b780-c50f43cd955c')
|
|
81
|
+
# uuid.packed
|
|
82
|
+
# # => 16-byte binary string
|
|
83
|
+
#
|
|
17
84
|
def packed
|
|
18
85
|
if raw.present?
|
|
19
86
|
raw
|
|
@@ -22,6 +89,16 @@ module MixinBot
|
|
|
22
89
|
end
|
|
23
90
|
end
|
|
24
91
|
|
|
92
|
+
##
|
|
93
|
+
# Returns the UUID in standard format with dashes.
|
|
94
|
+
#
|
|
95
|
+
# @return [String] UUID in format "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
|
96
|
+
#
|
|
97
|
+
# @example
|
|
98
|
+
# uuid = MixinBot::UUID.new(raw: binary_string)
|
|
99
|
+
# uuid.unpacked
|
|
100
|
+
# # => "965e5c6e-434c-3fa9-b780-c50f43cd955c"
|
|
101
|
+
#
|
|
25
102
|
def unpacked
|
|
26
103
|
_hex =
|
|
27
104
|
if hex.present?
|
data/lib/mixin_bot/version.rb
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module MixinBot
|
|
4
|
-
|
|
4
|
+
##
|
|
5
|
+
# Current version of the MixinBot gem.
|
|
6
|
+
#
|
|
7
|
+
# Follows Semantic Versioning (SemVer):
|
|
8
|
+
# - MAJOR version for incompatible API changes
|
|
9
|
+
# - MINOR version for backwards-compatible functionality additions
|
|
10
|
+
# - PATCH version for backwards-compatible bug fixes
|
|
11
|
+
#
|
|
12
|
+
# @see https://semver.org/
|
|
13
|
+
#
|
|
14
|
+
VERSION = '2.0.0'
|
|
5
15
|
end
|
data/lib/mixin_bot.rb
CHANGED
|
@@ -20,10 +20,16 @@ require 'openssl'
|
|
|
20
20
|
require 'rbnacl'
|
|
21
21
|
require 'sha3'
|
|
22
22
|
|
|
23
|
+
require_relative 'mixin_bot/errors'
|
|
23
24
|
require_relative 'mixin_bot/address'
|
|
25
|
+
require_relative 'mixin_bot/models'
|
|
24
26
|
require_relative 'mixin_bot/api'
|
|
27
|
+
require_relative 'mixin_bot/bot_auth'
|
|
25
28
|
require_relative 'mixin_bot/cli'
|
|
29
|
+
require_relative 'mixin_bot/computer'
|
|
26
30
|
require_relative 'mixin_bot/invoice'
|
|
31
|
+
require_relative 'mixin_bot/monitor'
|
|
32
|
+
require_relative 'mixin_bot/url_scheme'
|
|
27
33
|
require_relative 'mixin_bot/utils'
|
|
28
34
|
require_relative 'mixin_bot/nfo'
|
|
29
35
|
require_relative 'mixin_bot/uuid'
|
|
@@ -31,8 +37,134 @@ require_relative 'mixin_bot/transaction'
|
|
|
31
37
|
require_relative 'mixin_bot/version'
|
|
32
38
|
require_relative 'mvm'
|
|
33
39
|
|
|
40
|
+
##
|
|
41
|
+
# = MixinBot
|
|
42
|
+
#
|
|
43
|
+
# MixinBot is a Ruby SDK for interacting with the Mixin Network API.
|
|
44
|
+
#
|
|
45
|
+
# == Overview
|
|
46
|
+
#
|
|
47
|
+
# Mixin Network is a free and lightning fast peer-to-peer transactional network
|
|
48
|
+
# for digital assets. MixinBot provides a comprehensive Ruby interface to interact
|
|
49
|
+
# with the Mixin Network, including asset management, transfers, messaging, and more.
|
|
50
|
+
#
|
|
51
|
+
# == Installation
|
|
52
|
+
#
|
|
53
|
+
# Add this line to your application's Gemfile:
|
|
54
|
+
#
|
|
55
|
+
# gem 'mixin_bot'
|
|
56
|
+
#
|
|
57
|
+
# And then execute:
|
|
58
|
+
#
|
|
59
|
+
# $ bundle install
|
|
60
|
+
#
|
|
61
|
+
# Or install it yourself as:
|
|
62
|
+
#
|
|
63
|
+
# $ gem install mixin_bot
|
|
64
|
+
#
|
|
65
|
+
# == Quick Start
|
|
66
|
+
#
|
|
67
|
+
# === Configuration
|
|
68
|
+
#
|
|
69
|
+
# Configure your Mixin bot credentials:
|
|
70
|
+
#
|
|
71
|
+
# MixinBot.configure do
|
|
72
|
+
# self.app_id = '25696f85-b7b4-4509-8c3f-2684a8fc4a2a'
|
|
73
|
+
# self.client_secret = 'd9dc58107bacde671...'
|
|
74
|
+
# self.session_id = '25696f85-b7b4-4509-8c3f-2684a8fc4a2a'
|
|
75
|
+
# self.server_public_key = 'b0pjBUKI0Vp9K+NspaL....'
|
|
76
|
+
# self.session_private_key = '...'
|
|
77
|
+
# end
|
|
78
|
+
#
|
|
79
|
+
# === Basic Usage
|
|
80
|
+
#
|
|
81
|
+
# Get bot profile:
|
|
82
|
+
#
|
|
83
|
+
# MixinBot.api.me
|
|
84
|
+
# # => { "user_id" => "...", "full_name" => "...", ... }
|
|
85
|
+
#
|
|
86
|
+
# Get bot assets:
|
|
87
|
+
#
|
|
88
|
+
# MixinBot.api.assets
|
|
89
|
+
# # => [{ "asset_id" => "...", "symbol" => "BTC", ... }, ...]
|
|
90
|
+
#
|
|
91
|
+
# Transfer assets:
|
|
92
|
+
#
|
|
93
|
+
# MixinBot.api.create_transfer(
|
|
94
|
+
# '123456', # pin_code
|
|
95
|
+
# asset_id: '965e5c6e-434c-3fa9-b780-c50f43cd955c', # CNB
|
|
96
|
+
# opponent_id: '6ae1c7ae-1df1-498e-8f21-d48cb6d129b5', # receiver
|
|
97
|
+
# amount: 0.00000001,
|
|
98
|
+
# memo: 'test transfer',
|
|
99
|
+
# trace_id: SecureRandom.uuid
|
|
100
|
+
# )
|
|
101
|
+
#
|
|
102
|
+
# === Managing Multiple Bots
|
|
103
|
+
#
|
|
104
|
+
# You can manage multiple bots by creating separate API instances:
|
|
105
|
+
#
|
|
106
|
+
# bot1 = MixinBot::API.new(
|
|
107
|
+
# app_id: '...',
|
|
108
|
+
# session_id: '...',
|
|
109
|
+
# session_private_key: '...',
|
|
110
|
+
# server_public_key: '...'
|
|
111
|
+
# )
|
|
112
|
+
#
|
|
113
|
+
# bot2 = MixinBot::API.new(
|
|
114
|
+
# app_id: '...',
|
|
115
|
+
# session_id: '...',
|
|
116
|
+
# session_private_key: '...',
|
|
117
|
+
# server_public_key: '...'
|
|
118
|
+
# )
|
|
119
|
+
#
|
|
120
|
+
# bot1.me
|
|
121
|
+
# bot2.me
|
|
122
|
+
#
|
|
123
|
+
# == Main Components
|
|
124
|
+
#
|
|
125
|
+
# [MixinBot::API] Main API interface for interacting with Mixin Network
|
|
126
|
+
# [MixinBot::Configuration] Configuration management for bot credentials
|
|
127
|
+
# [MixinBot::Client] HTTP client for making API requests
|
|
128
|
+
# [MixinBot::Utils] Utility methods for cryptography and encoding
|
|
129
|
+
# [MixinBot::Transaction] Transaction encoding and decoding
|
|
130
|
+
# [MixinBot::MixAddress] Address handling for Mixin Network
|
|
131
|
+
# [MixinBot::Invoice] Invoice creation and parsing
|
|
132
|
+
# [MixinBot::Nfo] NFT memo handling
|
|
133
|
+
# [MixinBot::UUID] UUID utilities for Mixin Network
|
|
134
|
+
# [MVM] Mixin Virtual Machine integration
|
|
135
|
+
#
|
|
136
|
+
# == Error Handling
|
|
137
|
+
#
|
|
138
|
+
# MixinBot defines several custom error classes for different scenarios:
|
|
139
|
+
#
|
|
140
|
+
# begin
|
|
141
|
+
# MixinBot.api.create_transfer(...)
|
|
142
|
+
# rescue MixinBot::InsufficientBalanceError => e
|
|
143
|
+
# puts "Insufficient balance: #{e.message}"
|
|
144
|
+
# rescue MixinBot::UnauthorizedError => e
|
|
145
|
+
# puts "Unauthorized: #{e.message}"
|
|
146
|
+
# rescue MixinBot::ResponseError => e
|
|
147
|
+
# puts "API error: #{e.message}"
|
|
148
|
+
# end
|
|
149
|
+
#
|
|
150
|
+
# == Links
|
|
151
|
+
#
|
|
152
|
+
# - {Mixin Network Documentation}[https://developers.mixin.one/docs]
|
|
153
|
+
# - {GitHub Repository}[https://github.com/an-lee/mixin_bot]
|
|
154
|
+
#
|
|
34
155
|
module MixinBot
|
|
35
156
|
class << self
|
|
157
|
+
##
|
|
158
|
+
# Returns the default API instance using the global configuration.
|
|
159
|
+
#
|
|
160
|
+
# This is a singleton instance that will be created on first access.
|
|
161
|
+
# The API instance provides access to all Mixin Network API endpoints.
|
|
162
|
+
#
|
|
163
|
+
# MixinBot.api.me
|
|
164
|
+
# # => { "user_id" => "...", "full_name" => "..." }
|
|
165
|
+
#
|
|
166
|
+
# @return [MixinBot::API] the default API instance
|
|
167
|
+
#
|
|
36
168
|
def api
|
|
37
169
|
return @api if defined?(@api)
|
|
38
170
|
|
|
@@ -40,6 +172,14 @@ module MixinBot
|
|
|
40
172
|
@api
|
|
41
173
|
end
|
|
42
174
|
|
|
175
|
+
##
|
|
176
|
+
# Returns the global configuration instance.
|
|
177
|
+
#
|
|
178
|
+
# The configuration instance stores bot credentials and settings.
|
|
179
|
+
# It will be created on first access with default values.
|
|
180
|
+
#
|
|
181
|
+
# @return [MixinBot::Configuration] the configuration instance
|
|
182
|
+
#
|
|
43
183
|
def config
|
|
44
184
|
return @config if defined?(@config)
|
|
45
185
|
|
|
@@ -47,30 +187,44 @@ module MixinBot
|
|
|
47
187
|
@config
|
|
48
188
|
end
|
|
49
189
|
|
|
190
|
+
##
|
|
191
|
+
# Configures the global MixinBot settings.
|
|
192
|
+
#
|
|
193
|
+
# This method yields the configuration instance to a block where
|
|
194
|
+
# you can set your bot credentials and other settings.
|
|
195
|
+
#
|
|
196
|
+
# MixinBot.configure do
|
|
197
|
+
# self.app_id = '25696f85-b7b4-4509-8c3f-2684a8fc4a2a'
|
|
198
|
+
# self.session_id = '25696f85-b7b4-4509-8c3f-2684a8fc4a2a'
|
|
199
|
+
# self.session_private_key = '...'
|
|
200
|
+
# self.server_public_key = '...'
|
|
201
|
+
# end
|
|
202
|
+
#
|
|
203
|
+
# @yield [Configuration] the configuration instance
|
|
204
|
+
# @return [void]
|
|
205
|
+
#
|
|
50
206
|
def configure(&)
|
|
51
207
|
config.instance_exec(&)
|
|
52
208
|
end
|
|
53
209
|
|
|
210
|
+
##
|
|
211
|
+
# Returns the Utils module for accessing utility methods.
|
|
212
|
+
#
|
|
213
|
+
# MixinBot.utils.unique_uuid(uuid1, uuid2)
|
|
214
|
+
#
|
|
215
|
+
# @return [Module] the Utils module
|
|
216
|
+
#
|
|
54
217
|
def utils
|
|
55
218
|
MixinBot::Utils
|
|
56
219
|
end
|
|
57
|
-
end
|
|
58
220
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
class InsufficientBalanceError < Error; end
|
|
69
|
-
class InsufficientPoolError < Error; end
|
|
70
|
-
class PinError < Error; end
|
|
71
|
-
class InvalidNfoFormatError < Error; end
|
|
72
|
-
class InvalidUuidFormatError < Error; end
|
|
73
|
-
class InvalidTransactionFormatError < Error; end
|
|
74
|
-
class ConfigurationNotValidError < Error; end
|
|
75
|
-
class InvalidInvoiceFormatError < Error; end
|
|
221
|
+
##
|
|
222
|
+
# ActiveSupport deprecation helper for legacy API surfaces.
|
|
223
|
+
#
|
|
224
|
+
# @return [ActiveSupport::Deprecation]
|
|
225
|
+
#
|
|
226
|
+
def deprecator
|
|
227
|
+
@deprecator ||= ActiveSupport::Deprecation.new('2.0', 'MixinBot')
|
|
228
|
+
end
|
|
229
|
+
end
|
|
76
230
|
end
|