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
|
@@ -6,24 +6,27 @@ module MixinBot
|
|
|
6
6
|
NFT_ASSET_MIXIN_ID = '1700941284a95f31b25ec8c546008f208f88eee4419ccdcdbe6e3195e60128ca'
|
|
7
7
|
|
|
8
8
|
def legacy_collectible(id, access_token: nil)
|
|
9
|
+
warn_legacy_mixin_api!('LegacyCollectible#legacy_collectible')
|
|
9
10
|
path = "/collectibles/tokens/#{id}"
|
|
10
11
|
client.get path, access_token:
|
|
11
12
|
end
|
|
12
13
|
|
|
13
14
|
def legacy_collection(id, access_token: nil)
|
|
15
|
+
warn_legacy_mixin_api!('LegacyCollectible#legacy_collection')
|
|
14
16
|
path = "/collectibles/collections/#{id}"
|
|
15
17
|
client.get path, access_token:
|
|
16
18
|
end
|
|
17
19
|
|
|
18
20
|
def legacy_collectibles(**kwargs)
|
|
21
|
+
warn_legacy_mixin_api!('LegacyCollectible#legacy_collectibles')
|
|
19
22
|
limit = kwargs[:limit] || 100
|
|
20
23
|
offset = kwargs[:offset] || ''
|
|
21
24
|
state = kwargs[:state] || ''
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
threshold = kwargs[:threshold] ||
|
|
26
|
+
member_ids = kwargs[:members] || [config.app_id]
|
|
27
|
+
threshold = kwargs[:threshold] || member_ids.length
|
|
25
28
|
|
|
26
|
-
|
|
29
|
+
members_hash = MixinBot.utils.hash_members(member_ids)
|
|
27
30
|
|
|
28
31
|
access_token = kwargs[:access_token]
|
|
29
32
|
|
|
@@ -32,7 +35,7 @@ module MixinBot
|
|
|
32
35
|
limit:,
|
|
33
36
|
offset:,
|
|
34
37
|
state:,
|
|
35
|
-
members
|
|
38
|
+
members: members_hash,
|
|
36
39
|
threshold:
|
|
37
40
|
}
|
|
38
41
|
|
|
@@ -41,7 +44,11 @@ module MixinBot
|
|
|
41
44
|
|
|
42
45
|
COLLECTABLE_REQUEST_ACTIONS = %i[sign unlock].freeze
|
|
43
46
|
def create_collectible_request(action, raw, access_token: nil)
|
|
44
|
-
|
|
47
|
+
warn_legacy_mixin_api!('LegacyCollectible#create_collectible_request')
|
|
48
|
+
unless COLLECTABLE_REQUEST_ACTIONS.include? action.to_sym
|
|
49
|
+
raise ArgumentError,
|
|
50
|
+
"request action is limited in #{COLLECTABLE_REQUEST_ACTIONS.join(', ')}"
|
|
51
|
+
end
|
|
45
52
|
|
|
46
53
|
path = '/collectibles/requests'
|
|
47
54
|
payload = {
|
|
@@ -52,47 +59,33 @@ module MixinBot
|
|
|
52
59
|
end
|
|
53
60
|
|
|
54
61
|
def create_sign_collectible_request(raw, access_token: nil)
|
|
62
|
+
warn_legacy_mixin_api!('LegacyCollectible#create_sign_collectible_request')
|
|
55
63
|
create_collectible_request 'sign', raw, access_token:
|
|
56
64
|
end
|
|
57
65
|
|
|
58
66
|
def create_unlock_collectible_request(raw, access_token: nil)
|
|
67
|
+
warn_legacy_mixin_api!('LegacyCollectible#create_unlock_collectible_request')
|
|
59
68
|
create_collectible_request 'unlock', raw, access_token:
|
|
60
69
|
end
|
|
61
70
|
|
|
62
71
|
def sign_collectible_request(request_id, pin = nil)
|
|
72
|
+
warn_legacy_mixin_api!('LegacyCollectible#sign_collectible_request')
|
|
63
73
|
pin ||= config.pin
|
|
64
74
|
raise ArgumentError, 'pin is needed for sign collectible request' if pin.blank?
|
|
65
75
|
|
|
66
76
|
path = format('/collectibles/requests/%<request_id>s/sign', request_id:)
|
|
67
|
-
payload =
|
|
68
|
-
|
|
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
|
|
77
|
+
payload = tip_or_legacy_pin_payload(pin, 'TIP:COLLECTIBLE:REQUEST:SIGN:', request_id)
|
|
78
|
+
|
|
78
79
|
client.post path, **payload
|
|
79
80
|
end
|
|
80
81
|
|
|
81
82
|
def unlock_collectible_request(request_id, pin = nil)
|
|
83
|
+
warn_legacy_mixin_api!('LegacyCollectible#unlock_collectible_request')
|
|
82
84
|
pin ||= config.pin
|
|
83
85
|
raise ArgumentError, 'pin is needed for sign collectible request' if pin.blank?
|
|
84
86
|
|
|
85
87
|
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
|
|
88
|
+
payload = tip_or_legacy_pin_payload(pin, 'TIP:COLLECTIBLE:REQUEST:UNLOCK:', request_id)
|
|
96
89
|
|
|
97
90
|
client.post path, **payload
|
|
98
91
|
end
|
|
@@ -114,7 +107,13 @@ module MixinBot
|
|
|
114
107
|
# }
|
|
115
108
|
COLLECTIBLE_TRANSACTION_ARGUMENTS = %i[collectible nfo receivers receivers_threshold].freeze
|
|
116
109
|
def build_collectible_transaction(**kwargs)
|
|
117
|
-
|
|
110
|
+
warn_legacy_mixin_api!('LegacyCollectible#build_collectible_transaction')
|
|
111
|
+
unless COLLECTIBLE_TRANSACTION_ARGUMENTS.all? do |param|
|
|
112
|
+
kwargs.keys.include? param
|
|
113
|
+
end
|
|
114
|
+
raise ArgumentError,
|
|
115
|
+
"#{COLLECTIBLE_TRANSACTION_ARGUMENTS.join(', ')} are needed for build collectible transaction"
|
|
116
|
+
end
|
|
118
117
|
|
|
119
118
|
kwargs = kwargs.with_indifferent_access
|
|
120
119
|
collectible = kwargs['collectible']
|
|
@@ -5,7 +5,11 @@ module MixinBot
|
|
|
5
5
|
module LegacyMultisig
|
|
6
6
|
MULTISIG_REQUEST_ACTIONS = %i[sign unlock].freeze
|
|
7
7
|
def create_multisig_request(action, raw, access_token: nil)
|
|
8
|
-
|
|
8
|
+
warn_legacy_mixin_api!('LegacyMultisig#create_multisig_request')
|
|
9
|
+
unless MULTISIG_REQUEST_ACTIONS.include? action.to_sym
|
|
10
|
+
raise ArgumentError,
|
|
11
|
+
"request action is limited in #{MULTISIG_REQUEST_ACTIONS.join(', ')}"
|
|
12
|
+
end
|
|
9
13
|
|
|
10
14
|
path = '/multisigs/requests'
|
|
11
15
|
payload = {
|
|
@@ -17,46 +21,39 @@ module MixinBot
|
|
|
17
21
|
|
|
18
22
|
# transfer from the multisig address
|
|
19
23
|
def create_sign_multisig_request(raw, access_token: nil)
|
|
24
|
+
warn_legacy_mixin_api!('LegacyMultisig#create_sign_multisig_request')
|
|
20
25
|
create_multisig_request 'sign', raw, access_token:
|
|
21
26
|
end
|
|
22
27
|
|
|
23
28
|
# transfer from the multisig address
|
|
24
29
|
# create a request for unlock a multi-sign
|
|
25
30
|
def create_unlock_multisig_request(raw, access_token: nil)
|
|
31
|
+
warn_legacy_mixin_api!('LegacyMultisig#create_unlock_multisig_request')
|
|
26
32
|
create_multisig_request 'unlock', raw, access_token:
|
|
27
33
|
end
|
|
28
34
|
|
|
29
35
|
def sign_multisig_request(request_id, pin = nil)
|
|
36
|
+
warn_legacy_mixin_api!('LegacyMultisig#sign_multisig_request')
|
|
30
37
|
pin ||= config.pin
|
|
31
38
|
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
|
|
39
|
+
payload = tip_or_legacy_pin_payload(pin, 'TIP:MULTISIG:REQUEST:SIGN:', request_id)
|
|
42
40
|
|
|
43
41
|
client.post path, **payload
|
|
44
42
|
end
|
|
45
43
|
|
|
44
|
+
def cancel_multisig_request(request_id, access_token: nil)
|
|
45
|
+
warn_legacy_mixin_api!('LegacyMultisig#cancel_multisig_request')
|
|
46
|
+
path = format('/multisigs/requests/%<request_id>s/cancel', request_id:)
|
|
47
|
+
client.post path, access_token:
|
|
48
|
+
end
|
|
49
|
+
alias cancel_multisig cancel_multisig_request
|
|
50
|
+
|
|
46
51
|
def unlock_multisig_request(request_id, pin = nil)
|
|
52
|
+
warn_legacy_mixin_api!('LegacyMultisig#unlock_multisig_request')
|
|
47
53
|
pin ||= config.pin
|
|
48
54
|
|
|
49
55
|
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
|
|
56
|
+
payload = tip_or_legacy_pin_payload(pin, 'TIP:MULTISIG:REQUEST:UNLOCK:', request_id)
|
|
60
57
|
|
|
61
58
|
client.post path, **payload
|
|
62
59
|
end
|
|
@@ -64,6 +61,7 @@ module MixinBot
|
|
|
64
61
|
# pay to the multisig address
|
|
65
62
|
# used for create multisig payment code_id
|
|
66
63
|
def create_multisig_payment(**kwargs)
|
|
64
|
+
warn_legacy_mixin_api!('LegacyMultisig#create_multisig_payment')
|
|
67
65
|
path = '/payments'
|
|
68
66
|
payload = {
|
|
69
67
|
asset_id: kwargs[:asset_id],
|
|
@@ -79,6 +77,7 @@ module MixinBot
|
|
|
79
77
|
end
|
|
80
78
|
|
|
81
79
|
def verify_multisig(code_id, access_token: nil)
|
|
80
|
+
warn_legacy_mixin_api!('LegacyMultisig#verify_multisig')
|
|
82
81
|
path = format('/codes/%<code_id>s', code_id:)
|
|
83
82
|
client.get path, access_token:
|
|
84
83
|
end
|
|
@@ -3,21 +3,26 @@
|
|
|
3
3
|
module MixinBot
|
|
4
4
|
class API
|
|
5
5
|
module LegacyOutput
|
|
6
|
+
def read_multisigs(**)
|
|
7
|
+
warn_legacy_mixin_api!('LegacyOutput#read_multisigs')
|
|
8
|
+
legacy_outputs(**)
|
|
9
|
+
end
|
|
10
|
+
|
|
6
11
|
def legacy_outputs(**kwargs)
|
|
12
|
+
warn_legacy_mixin_api!('LegacyOutput#legacy_outputs')
|
|
7
13
|
limit = kwargs[:limit] || 100
|
|
8
14
|
offset = kwargs[:offset] || ''
|
|
9
15
|
state = kwargs[:state] || ''
|
|
10
|
-
|
|
16
|
+
members_hash = MixinBot.utils.hash_members(kwargs[:members] || [])
|
|
11
17
|
threshold = kwargs[:threshold] || ''
|
|
12
18
|
access_token = kwargs[:access_token]
|
|
13
|
-
members = SHA3::Digest::SHA256.hexdigest(members&.sort&.join)
|
|
14
19
|
|
|
15
20
|
path = '/multisigs/outputs'
|
|
16
21
|
params = {
|
|
17
22
|
limit:,
|
|
18
23
|
offset:,
|
|
19
24
|
state:,
|
|
20
|
-
members
|
|
25
|
+
members: members_hash,
|
|
21
26
|
threshold:
|
|
22
27
|
}.compact_blank
|
|
23
28
|
|
|
@@ -27,6 +32,7 @@ module MixinBot
|
|
|
27
32
|
alias multisig_outputs legacy_outputs
|
|
28
33
|
|
|
29
34
|
def create_output(receivers:, index:, hint: nil, access_token: nil)
|
|
35
|
+
warn_legacy_mixin_api!('LegacyOutput#create_output')
|
|
30
36
|
path = '/outputs'
|
|
31
37
|
payload = {
|
|
32
38
|
receivers:,
|
|
@@ -37,6 +43,7 @@ module MixinBot
|
|
|
37
43
|
end
|
|
38
44
|
|
|
39
45
|
def build_output(receivers:, index:, amount:, threshold:, hint: nil)
|
|
46
|
+
warn_legacy_mixin_api!('LegacyOutput#build_output')
|
|
40
47
|
_output = create_output(receivers:, index:, hint:)
|
|
41
48
|
{
|
|
42
49
|
amount: format('%.8f', amount.to_d.to_r),
|
|
@@ -4,6 +4,7 @@ module MixinBot
|
|
|
4
4
|
class API
|
|
5
5
|
module LegacyPayment
|
|
6
6
|
def pay_url(**kwargs)
|
|
7
|
+
warn_legacy_mixin_api!('LegacyPayment#pay_url')
|
|
7
8
|
format(
|
|
8
9
|
'https://mixin.one/pay?recipient=%<recipient_id>s&asset=%<asset>s&amount=%<amount>s&trace=%<trace>s&memo=%<memo>s',
|
|
9
10
|
recipient_id: kwargs[:recipient_id],
|
|
@@ -16,6 +17,7 @@ module MixinBot
|
|
|
16
17
|
|
|
17
18
|
# https://developers.mixin.one/api/alpha-mixin-network/verify-payment/
|
|
18
19
|
def verify_payment(**kwargs)
|
|
20
|
+
warn_legacy_mixin_api!('LegacyPayment#verify_payment')
|
|
19
21
|
path = '/payments'
|
|
20
22
|
payload = {
|
|
21
23
|
asset_id: kwargs[:asset_id],
|
|
@@ -4,6 +4,7 @@ module MixinBot
|
|
|
4
4
|
class API
|
|
5
5
|
module LegacySnapshot
|
|
6
6
|
def network_snapshots(**kwargs)
|
|
7
|
+
warn_legacy_mixin_api!('LegacySnapshot#network_snapshots')
|
|
7
8
|
path = '/network/snapshots'
|
|
8
9
|
params = {
|
|
9
10
|
limit: kwargs[:limit],
|
|
@@ -16,6 +17,7 @@ module MixinBot
|
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
def snapshots(**kwargs)
|
|
20
|
+
warn_legacy_mixin_api!('LegacySnapshot#snapshots')
|
|
19
21
|
path = '/snapshots'
|
|
20
22
|
|
|
21
23
|
params = {
|
|
@@ -30,10 +32,24 @@ module MixinBot
|
|
|
30
32
|
end
|
|
31
33
|
|
|
32
34
|
def network_snapshot(snapshot_id, **kwargs)
|
|
35
|
+
warn_legacy_mixin_api!('LegacySnapshot#network_snapshot')
|
|
33
36
|
path = format('/network/snapshots/%<snapshot_id>s', snapshot_id:)
|
|
34
37
|
|
|
35
38
|
client.get path, access_token: kwargs[:access_token]
|
|
36
39
|
end
|
|
40
|
+
|
|
41
|
+
def snapshot(snapshot_id, access_token: nil)
|
|
42
|
+
warn_legacy_mixin_api!('LegacySnapshot#snapshot')
|
|
43
|
+
path = format('/snapshots/%<snapshot_id>s', snapshot_id:)
|
|
44
|
+
client.get path, access_token:
|
|
45
|
+
end
|
|
46
|
+
alias snapshot_by_id snapshot
|
|
47
|
+
|
|
48
|
+
def snapshot_by_trace_id(trace_id, access_token: nil)
|
|
49
|
+
warn_legacy_mixin_api!('LegacySnapshot#snapshot_by_trace_id')
|
|
50
|
+
path = format('/snapshots/trace/%<trace_id>s', trace_id:)
|
|
51
|
+
client.get path, access_token:
|
|
52
|
+
end
|
|
37
53
|
end
|
|
38
54
|
end
|
|
39
55
|
end
|
|
@@ -18,7 +18,13 @@ module MixinBot
|
|
|
18
18
|
# }
|
|
19
19
|
RAW_TRANSACTION_ARGUMENTS = %i[utxos senders senders_threshold receivers receivers_threshold amount].freeze
|
|
20
20
|
def build_raw_transaction(**kwargs)
|
|
21
|
-
|
|
21
|
+
warn_legacy_mixin_api!('LegacyTransaction#build_raw_transaction')
|
|
22
|
+
unless RAW_TRANSACTION_ARGUMENTS.all? do |param|
|
|
23
|
+
kwargs.keys.include? param
|
|
24
|
+
end
|
|
25
|
+
raise ArgumentError,
|
|
26
|
+
"#{RAW_TRANSACTION_ARGUMENTS.join(', ')} are needed for build raw transaction"
|
|
27
|
+
end
|
|
22
28
|
|
|
23
29
|
senders = kwargs[:senders]
|
|
24
30
|
senders_threshold = kwargs[:senders_threshold]
|
|
@@ -96,7 +102,13 @@ module MixinBot
|
|
|
96
102
|
# use safe transaction protocol instead
|
|
97
103
|
MULTISIG_TRANSACTION_ARGUMENTS = %i[asset_id receivers threshold amount].freeze
|
|
98
104
|
def create_multisig_transaction(pin, **options)
|
|
99
|
-
|
|
105
|
+
warn_legacy_mixin_api!('LegacyTransaction#create_multisig_transaction')
|
|
106
|
+
unless MULTISIG_TRANSACTION_ARGUMENTS.all? do |param|
|
|
107
|
+
options.keys.include? param
|
|
108
|
+
end
|
|
109
|
+
raise ArgumentError,
|
|
110
|
+
"#{MULTISIG_TRANSACTION_ARGUMENTS.join(', ')} are needed for create multisig transaction"
|
|
111
|
+
end
|
|
100
112
|
|
|
101
113
|
asset_id = options[:asset_id]
|
|
102
114
|
receivers = options[:receivers].sort
|
|
@@ -117,11 +129,9 @@ module MixinBot
|
|
|
117
129
|
memo:
|
|
118
130
|
}
|
|
119
131
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
payload[:pin] = encrypt_pin(pin)
|
|
124
|
-
end
|
|
132
|
+
payload.update(
|
|
133
|
+
tip_or_legacy_pin_payload(pin, 'TIP:TRANSACTION:CREATE:', asset_id, receivers.join, threshold, amount, trace_id, memo)
|
|
134
|
+
)
|
|
125
135
|
|
|
126
136
|
client.post path, **payload
|
|
127
137
|
end
|
|
@@ -129,7 +139,13 @@ module MixinBot
|
|
|
129
139
|
# use safe transaction protocol instead
|
|
130
140
|
MAINNET_TRANSACTION_ARGUMENTS = %i[asset_id opponent_id amount].freeze
|
|
131
141
|
def create_mainnet_transaction(pin, **options)
|
|
132
|
-
|
|
142
|
+
warn_legacy_mixin_api!('LegacyTransaction#create_mainnet_transaction')
|
|
143
|
+
unless MAINNET_TRANSACTION_ARGUMENTS.all? do |param|
|
|
144
|
+
options.keys.include? param
|
|
145
|
+
end
|
|
146
|
+
raise ArgumentError,
|
|
147
|
+
"#{MAINNET_TRANSACTION_ARGUMENTS.join(', ')} are needed for create main net transactions"
|
|
148
|
+
end
|
|
133
149
|
|
|
134
150
|
asset_id = options[:asset_id]
|
|
135
151
|
opponent_id = options[:opponent_id]
|
|
@@ -146,17 +162,16 @@ module MixinBot
|
|
|
146
162
|
memo:
|
|
147
163
|
}
|
|
148
164
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
payload[:pin] = encrypt_pin(pin)
|
|
153
|
-
end
|
|
165
|
+
payload.update(
|
|
166
|
+
tip_or_legacy_pin_payload(pin, 'TIP:TRANSACTION:CREATE:', asset_id, opponent_id, amount, trace_id, memo)
|
|
167
|
+
)
|
|
154
168
|
|
|
155
169
|
client.post path, **payload
|
|
156
170
|
end
|
|
157
171
|
|
|
158
172
|
# use safe transaction protocol instead
|
|
159
173
|
def transactions(**options)
|
|
174
|
+
warn_legacy_mixin_api!('LegacyTransaction#transactions')
|
|
160
175
|
path = '/external/transactions'
|
|
161
176
|
params = {
|
|
162
177
|
limit: options[:limit],
|
|
@@ -5,7 +5,10 @@ module MixinBot
|
|
|
5
5
|
module LegacyTransfer
|
|
6
6
|
TRANSFER_ARGUMENTS = %i[asset_id opponent_id amount].freeze
|
|
7
7
|
def create_legacy_transfer(pin, **kwargs)
|
|
8
|
-
|
|
8
|
+
warn_legacy_mixin_api!('LegacyTransfer#create_legacy_transfer')
|
|
9
|
+
raise ArgumentError, "#{TRANSFER_ARGUMENTS.join(', ')} are needed for create transfer" unless TRANSFER_ARGUMENTS.all? do |param|
|
|
10
|
+
kwargs.keys.include? param
|
|
11
|
+
end
|
|
9
12
|
|
|
10
13
|
asset_id = kwargs[:asset_id]
|
|
11
14
|
opponent_id = kwargs[:opponent_id]
|
|
@@ -21,22 +24,22 @@ module MixinBot
|
|
|
21
24
|
memo:
|
|
22
25
|
}
|
|
23
26
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
else
|
|
28
|
-
encrypted_pin = encrypt_pin pin
|
|
29
|
-
payload[:pin] = encrypted_pin
|
|
30
|
-
end
|
|
27
|
+
payload.update(
|
|
28
|
+
tip_or_legacy_pin_payload(pin, 'TIP:TRANSFER:CREATE:', asset_id, opponent_id, amount, trace_id, memo)
|
|
29
|
+
)
|
|
31
30
|
|
|
32
31
|
path = '/transfers'
|
|
33
32
|
client.post path, **payload
|
|
34
33
|
end
|
|
35
34
|
|
|
36
35
|
def legacy_transfer(trace_id, access_token: nil)
|
|
36
|
+
warn_legacy_mixin_api!('LegacyTransfer#legacy_transfer')
|
|
37
37
|
path = format('/transfers/trace/%<trace_id>s', trace_id:)
|
|
38
38
|
client.get path, access_token:
|
|
39
39
|
end
|
|
40
|
+
|
|
41
|
+
alias transfer legacy_transfer
|
|
42
|
+
alias create_transfer create_legacy_transfer
|
|
40
43
|
end
|
|
41
44
|
end
|
|
42
45
|
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MixinBot
|
|
4
|
+
class API
|
|
5
|
+
module LegacyUser
|
|
6
|
+
# Upgrades a legacy RSA keystore user to Ed25519 session keys.
|
|
7
|
+
#
|
|
8
|
+
# @param keystore [Hash] :pin, :session_id, :pin_token (base64), :private_key (PEM)
|
|
9
|
+
def upgrade_legacy_user(keystore)
|
|
10
|
+
kl = keystore.with_indifferent_access
|
|
11
|
+
priv = OpenSSL::PKey::RSA.new(kl[:private_key])
|
|
12
|
+
token = Base64.decode64(kl[:pin_token])
|
|
13
|
+
key_bytes = priv.decrypt(
|
|
14
|
+
token,
|
|
15
|
+
rsa_padding_mode: 'oaep',
|
|
16
|
+
rsa_oaep_md: 'sha256',
|
|
17
|
+
rsa_mgf1_md: 'sha1',
|
|
18
|
+
oaep_label: kl[:session_id]
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
pin_byte = kl[:pin].to_s.b
|
|
22
|
+
pin_byte += [Time.now.to_i].pack('Q<')
|
|
23
|
+
pin_byte += [0].pack('Q<')
|
|
24
|
+
padding = 16 - (pin_byte.length % 16)
|
|
25
|
+
pin_byte += ([padding].pack('C') * padding)
|
|
26
|
+
|
|
27
|
+
cipher = OpenSSL::Cipher.new('AES-256-CBC')
|
|
28
|
+
cipher.encrypt
|
|
29
|
+
iv = cipher.random_iv
|
|
30
|
+
cipher.key = key_bytes
|
|
31
|
+
ciphertext = iv + cipher.update(pin_byte) + cipher.final
|
|
32
|
+
|
|
33
|
+
pub_bytes = priv.public_key.to_der
|
|
34
|
+
seed = Digest::SHA512.digest(priv.to_der)[0, 32]
|
|
35
|
+
pub_ed25519 = RbNaCl::Signatures::Ed25519::SigningKey.new(seed).verify_key.to_bytes
|
|
36
|
+
|
|
37
|
+
payload = {
|
|
38
|
+
session_secret_legacy: Base64.urlsafe_encode64(pub_bytes, padding: false),
|
|
39
|
+
session_secret: Base64.urlsafe_encode64(pub_ed25519, padding: false),
|
|
40
|
+
session_id: kl[:session_id],
|
|
41
|
+
pin: Base64.urlsafe_encode64(ciphertext, padding: false)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
result = client.post '/legacy/users', **payload, access_token: ''
|
|
45
|
+
data = result['data'] || result.data
|
|
46
|
+
result['data'] = data.merge('session_private_key' => seed.unpack1('H*')) if data.is_a?(Hash)
|
|
47
|
+
result
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
data/lib/mixin_bot/api/me.rb
CHANGED
|
@@ -2,14 +2,60 @@
|
|
|
2
2
|
|
|
3
3
|
module MixinBot
|
|
4
4
|
class API
|
|
5
|
+
##
|
|
6
|
+
# API methods for bot profile management.
|
|
7
|
+
#
|
|
8
|
+
# Provides methods to:
|
|
9
|
+
# - Retrieve bot profile information
|
|
10
|
+
# - Update bot profile (name, avatar)
|
|
11
|
+
# - List friends
|
|
12
|
+
# - Access Safe API profile
|
|
13
|
+
#
|
|
5
14
|
module Me
|
|
15
|
+
##
|
|
16
|
+
# Retrieves the bot's profile information.
|
|
17
|
+
#
|
|
18
|
+
# Returns detailed information about the bot including:
|
|
19
|
+
# - user_id
|
|
20
|
+
# - full_name
|
|
21
|
+
# - avatar_url
|
|
22
|
+
# - identity_number
|
|
23
|
+
# - phone
|
|
24
|
+
# - biography
|
|
25
|
+
# - created_at
|
|
26
|
+
#
|
|
27
|
+
# @param access_token [String, nil] optional access token for authentication
|
|
28
|
+
# @return [Hash] the bot profile
|
|
29
|
+
#
|
|
30
|
+
# @example
|
|
31
|
+
# profile = api.me
|
|
32
|
+
# puts profile['full_name']
|
|
33
|
+
# puts profile['identity_number']
|
|
34
|
+
#
|
|
35
|
+
# @see https://developers.mixin.one/docs/api/users/me
|
|
36
|
+
#
|
|
6
37
|
def me(access_token: nil)
|
|
7
38
|
path = '/me'
|
|
8
39
|
client.get path, access_token:
|
|
9
40
|
end
|
|
10
41
|
|
|
11
|
-
|
|
12
|
-
#
|
|
42
|
+
##
|
|
43
|
+
# Updates the bot's profile.
|
|
44
|
+
#
|
|
45
|
+
# Allows updating:
|
|
46
|
+
# - full_name: the bot's display name
|
|
47
|
+
# - avatar_base64: Base64-encoded avatar image (PNG, JPEG, or GIF, size > 1024 bytes)
|
|
48
|
+
#
|
|
49
|
+
# @param kwargs [Hash] update options
|
|
50
|
+
# @option kwargs [String] :full_name the new name for the bot
|
|
51
|
+
# @option kwargs [String] :avatar_base64 Base64-encoded image data
|
|
52
|
+
# @option kwargs [String] :access_token optional access token
|
|
53
|
+
# @return [Hash] the updated profile
|
|
54
|
+
#
|
|
55
|
+
# @example
|
|
56
|
+
# api.update_me(full_name: 'My Awesome Bot')
|
|
57
|
+
# api.update_me(avatar_base64: Base64.strict_encode64(File.read('avatar.png')))
|
|
58
|
+
#
|
|
13
59
|
def update_me(**kwargs)
|
|
14
60
|
path = '/me'
|
|
15
61
|
payload = {
|
|
@@ -20,16 +66,66 @@ module MixinBot
|
|
|
20
66
|
client.post path, **payload
|
|
21
67
|
end
|
|
22
68
|
|
|
23
|
-
|
|
69
|
+
##
|
|
70
|
+
# Retrieves the bot's friend list.
|
|
71
|
+
#
|
|
72
|
+
# Returns an array of users who have interacted with the bot
|
|
73
|
+
# and are in the bot's contact list.
|
|
74
|
+
#
|
|
75
|
+
# @param access_token [String, nil] optional access token
|
|
76
|
+
# @return [Array<Hash>] array of user profiles
|
|
77
|
+
#
|
|
78
|
+
# @example
|
|
79
|
+
# friends = api.friends
|
|
80
|
+
# friends.each do |friend|
|
|
81
|
+
# puts "#{friend['full_name']} (#{friend['user_id']})"
|
|
82
|
+
# end
|
|
83
|
+
#
|
|
84
|
+
# @see https://developers.mixin.one/docs/api/users/friends
|
|
85
|
+
#
|
|
24
86
|
def friends(access_token: nil)
|
|
25
87
|
path = '/friends'
|
|
26
88
|
client.get path, access_token:
|
|
27
89
|
end
|
|
28
90
|
|
|
91
|
+
##
|
|
92
|
+
# Retrieves the bot's Safe API profile.
|
|
93
|
+
#
|
|
94
|
+
# Returns Safe API-specific profile information including:
|
|
95
|
+
# - Safe wallet address
|
|
96
|
+
# - TIP signing key
|
|
97
|
+
# - Safe network status
|
|
98
|
+
#
|
|
99
|
+
# @param access_token [String, nil] optional access token
|
|
100
|
+
# @return [Hash] the Safe API profile
|
|
101
|
+
#
|
|
102
|
+
# @example
|
|
103
|
+
# safe_profile = api.safe_me
|
|
104
|
+
# puts safe_profile['user_id']
|
|
105
|
+
#
|
|
29
106
|
def safe_me(access_token: nil)
|
|
30
107
|
path = '/safe/me'
|
|
31
108
|
client.get path, access_token:
|
|
32
109
|
end
|
|
110
|
+
alias request_user_me safe_me
|
|
111
|
+
|
|
112
|
+
def update_preferences(message_source: nil, conversation_source: nil, currency: nil, threshold: nil,
|
|
113
|
+
access_token: nil)
|
|
114
|
+
path = '/me/preferences'
|
|
115
|
+
payload = {
|
|
116
|
+
receive_message_source: message_source,
|
|
117
|
+
accept_conversation_source: conversation_source,
|
|
118
|
+
fiat_currency: currency,
|
|
119
|
+
transfer_notification_threshold: threshold
|
|
120
|
+
}.compact
|
|
121
|
+
client.post path, **payload, access_token:
|
|
122
|
+
end
|
|
123
|
+
alias update_preference update_preferences
|
|
124
|
+
|
|
125
|
+
def relationship(user_id:, action:, access_token: nil)
|
|
126
|
+
path = '/relationships'
|
|
127
|
+
client.post path, user_id:, action:, access_token:
|
|
128
|
+
end
|
|
33
129
|
end
|
|
34
130
|
end
|
|
35
131
|
end
|