trident_assistant 0.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.
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TridentAssistant
4
+ class API
5
+ # api for order
6
+ module Order
7
+ EXCHANGE_ASSET_ID = "965e5c6e-434c-3fa9-b780-c50f43cd955c"
8
+ MINIMUM_AMOUNT = 0.000_000_01
9
+
10
+ def orders(**kwargs)
11
+ client.get(
12
+ "api/orders",
13
+ headers: {
14
+ Authorization: "Bearer #{mixin_bot.access_token("GET", "/me")}"
15
+ },
16
+ params: {
17
+ collection_id: kwargs[:collection_id],
18
+ metahash: kwargs[:metahash],
19
+ state: kwargs[:state],
20
+ type: kwargs[:type]
21
+ }
22
+ )
23
+ end
24
+
25
+ def order(id)
26
+ client
27
+ .get(
28
+ "api/orders/#{id}",
29
+ headers: {
30
+ Authorization: "Bearer #{mixin_bot.access_token("GET", "/me")}"
31
+ }
32
+ )
33
+ end
34
+
35
+ def fill_order(order_id)
36
+ info = order order_id
37
+ raise "Order state: #{info["state"]}" if info["state"] != "open"
38
+
39
+ memo = TridentAssistant::Utils::Memo.new(type: "F", order_id: order_id, token_id: info["token_id"])
40
+
41
+ trace_id = SecureRandom.uuid
42
+ mixin_bot.create_multisig_transaction(
43
+ keystore[:pin],
44
+ {
45
+ asset_id: info["asset_id"],
46
+ trace_id: trace_id,
47
+ amount: info["price"],
48
+ memo: memo.encode,
49
+ receivers: TridentAssistant::Utils::TRIDENT_MTG[:members],
50
+ threshold: TridentAssistant::Utils::TRIDENT_MTG[:threshold]
51
+ }
52
+ )
53
+ end
54
+
55
+ def cancel_order(order_id)
56
+ info = order order_id
57
+ raise "Order maker: #{info["maker"]["id"]}" if info.dig("maker", "id") != mixin_bot.client_id
58
+ raise "Order state: #{info["state"]}" if info["state"] != "open"
59
+
60
+ memo = TridentAssistant::Utils::Memo.new(type: "C", order_id: order_id, token_id: info["token_id"])
61
+
62
+ trace_id = SecureRandom.uuid
63
+ mixin_bot.create_multisig_transaction(
64
+ keystore[:pin],
65
+ {
66
+ asset_id: info["asset_id"],
67
+ trace_id: trace_id,
68
+ amount: info["price"],
69
+ memo: memo.encode,
70
+ receivers: TridentAssistant::Utils::TRIDENT_MTG[:members],
71
+ threshold: TridentAssistant::Utils::TRIDENT_MTG[:threshold]
72
+ }
73
+ )
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./client"
4
+ require_relative "./api/collection"
5
+ require_relative "./api/collectible"
6
+ require_relative "./api/metadata"
7
+ require_relative "./api/order"
8
+
9
+ module TridentAssistant
10
+ # APIs of Trident server
11
+ class API
12
+ attr_reader :mixin_bot, :client, :keystore
13
+
14
+ def initialize(**args)
15
+ @client = Client.new endpoint: args[:endpoint]
16
+ return if args[:keystore].blank?
17
+
18
+ @keystore = TridentAssistant::Utils.parse_json args[:keystore]
19
+ @mixin_bot = TridentAssistant::Utils.mixin_bot_from_keystore args[:keystore]
20
+ end
21
+
22
+ include TridentAssistant::API::Collectible
23
+ include TridentAssistant::API::Collection
24
+ include TridentAssistant::API::Metadata
25
+ include TridentAssistant::API::Order
26
+ end
27
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../api"
4
+
5
+ module TridentAssistant
6
+ module CLI
7
+ # Base class of CLI
8
+ class Base < Thor
9
+ # https://github.com/Shopify/cli-ui
10
+ UI = ::CLI::UI
11
+
12
+ attr_reader :keystore, :bot, :client, :api
13
+
14
+ def initialize(*args)
15
+ super
16
+
17
+ endpoint =
18
+ if options[:endpoint].present?
19
+ options[:endpoint]
20
+ else
21
+ {
22
+ prod: "https://thetrident.one",
23
+ test: "https://trident-test.onrender.com",
24
+ dev: "http://localhost:3000"
25
+ }[options[:environment].to_sym]
26
+ end
27
+ @api =
28
+ begin
29
+ TridentAssistant::API.new(
30
+ endpoint: endpoint,
31
+ keystore: options[:keystore]
32
+ )
33
+ rescue JSON::ParserError
34
+ log UI.fmt("{{x}} falied to parse keystore.json: #{options[:keystore]}")
35
+ rescue StandardError => e
36
+ log UI.fmt "{{x}} Failed to initialize Mixin bot, maybe your keystore is incorrect. #{e.inspect}"
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def log(obj)
43
+ if options[:pretty]
44
+ if obj.is_a? String
45
+ puts obj
46
+ else
47
+ ap obj
48
+ end
49
+ else
50
+ puts obj.inspect
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./base"
4
+
5
+ module TridentAssistant
6
+ module CLI
7
+ # CLI to query Trident and Mixin APIs
8
+ class Collectible < Base
9
+ desc "index", "query collectibles in wallet"
10
+ option :state, type: :string, aliases: "s", required: false, default: :unspent,
11
+ desc: "keystore or keystore.json file of Mixin bot"
12
+ option :keystore, type: :string, aliases: "k", required: true, desc: "keystore or keystore.json file of Mixin bot"
13
+ def index
14
+ r = api.mixin_bot.collectibles state: options[:state]
15
+
16
+ log r["data"]
17
+ end
18
+
19
+ desc "show COLLECTION TOKEN", "query collectible"
20
+ option :keystore, type: :string, aliases: "k", required: true, desc: "keystore or keystore.json file of Mixin bot"
21
+ def show(collection, token)
22
+ token_id = MixinBot::Utils::Nfo.new(collection: collection, token: token).unique_token_id
23
+ r = api.mixin_bot.collectible token_id
24
+
25
+ log r["data"]
26
+ end
27
+
28
+ desc "deposit COLLECTION TOKEN", "deposit NFT"
29
+ option :keystore, type: :string, aliases: "k", required: true, desc: "keystore or keystore.json file of Mixin bot"
30
+ def deposit(collection, token)
31
+ log api.deposit collection, token
32
+ log UI.fmt("{{v}} successfully transfer NFT")
33
+ rescue StandardError => e
34
+ log UI.fmt("{{x}} failed: #{e.inspect} #{e.backtrace.join("\n")}")
35
+ end
36
+
37
+ desc "airdrop COLLECTION, TOKEN", "airdrop NFT"
38
+ option :receiver, type: :string, aliases: "r", required: false, desc: "receiver ID of airdrop"
39
+ option :start, type: :string, aliases: "s", required: false, desc: "start time of airdrop"
40
+ option :keystore, type: :string, aliases: "k", required: true, desc: "keystore or keystore.json file of Mixin bot"
41
+ def airdrop(collection, token)
42
+ log api.airdrop collection, token, receiver_id: options[:receiver], start_at: options[:start]
43
+ log UI.fmt("{{v}} successfully transfer NFT")
44
+ rescue StandardError => e
45
+ log UI.fmt("{{x}} failed: #{e.inspect} #{e.backtrace.join("\n")}")
46
+ end
47
+
48
+ desc "withdraw COLLECTION TOKEN", "withdraw NFT"
49
+ option :keystore, type: :string, aliases: "k", required: true, desc: "keystore or keystore.json file of Mixin bot"
50
+ def withdraw(collection, token)
51
+ log api.withdraw collection, token
52
+ log UI.fmt("{{v}} successfully invoked withdraw")
53
+ rescue StandardError => e
54
+ log UI.fmt("{{x}} failed: #{e.inspect} #{e.backtrace.join("\n")}")
55
+ end
56
+
57
+ desc "bulkairdrop DIR", "Airdrop NFT in bulk"
58
+ option :keystore, type: :string, aliases: "k", required: true, desc: "keystore or keystore.json file of Mixin bot"
59
+ def bulkairdrop(dir)
60
+ raise "#{dir} is not a directory" unless Dir.exist?(dir)
61
+
62
+ Dir.glob("#{dir}/*.json").each do |file|
63
+ log UI.fmt("{{v}} found #{file}")
64
+ data = TridentAssistant::Utils.parse_json file
65
+ metadata = TridentAssistant::Utils.parse_metadata data
66
+ log UI.fmt("{{v}} metadata parsed")
67
+ metadata.validate!
68
+ log UI.fmt("{{v}} metadata validated")
69
+
70
+ if data.dig("_airdrop", "hash").present?
71
+ log UI.fmt("{{v}} NFT already transferred")
72
+ next
73
+ end
74
+
75
+ receiver_id = data.dig("_airdrop", "receiver_id")
76
+ start_at = data.dig("_airdrop", "start_at")
77
+ log UI.fmt("{{v}} airdrop receiver_id: #{receiver_id}")
78
+ log UI.fmt("{{v}} airdrop start_at: #{start_at}")
79
+
80
+ r = api.airdrop metadata.collection["id"], metadata.token["id"], receiver_id: receiver_id, start_at: start_at
81
+ log r["data"]
82
+ data["_airdrop"] ||= {}
83
+ data["_airdrop"]["hash"] = r["data"]["hash"]
84
+ log UI.fmt("{{v}} successfully transfer NFT ##{metadata.token["id"]} #{metadata.collection["id"]}")
85
+ rescue TridentAssistant::Utils::Metadata::InvalidFormatError, JSON::ParserError, Client::RequestError,
86
+ MixinBot::Error, RuntimeError => e
87
+ log UI.fmt("{{x}} #{file} failed to airdrop: #{e.inspect}")
88
+ next
89
+ ensure
90
+ File.write file, data.to_json
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ def _airdrop
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./base"
4
+
5
+ module TridentAssistant
6
+ module CLI
7
+ # CLI tool of collection
8
+ class Collection < Base
9
+ class InvalidError < TridentAssistant::Error; end
10
+
11
+ option :keystore, type: :string, aliases: "k", required: true, desc: "keystore or keystore.json file of Mixin bot"
12
+ desc "index", "query all collections"
13
+ def index
14
+ log api.collections
15
+ end
16
+
17
+ option :name, type: :string, aliases: "n", desc: "collection Name"
18
+ option :description, type: :string, aliases: "d", desc: "collection Description"
19
+ option :icon, type: :string, aliases: "i", desc: "collection Icon"
20
+ option :url, type: :string, aliases: "u", desc: "collection External URL"
21
+ option :split, type: :string, aliases: "s", desc: "collection Split"
22
+ option :keystore, type: :string, aliases: "k", required: true, desc: "keystore or keystore.json file of Mixin bot"
23
+ desc "create", "create a new collection"
24
+ def create
25
+ name = options[:name] || UI.ask("Please input collection name")
26
+ raise InvalidError, "Name cannot be blank" if name.blank?
27
+
28
+ description = options[:description] || UI.ask("Please input collection description")
29
+ raise InvalidError, "Description cannot be blank" if description.blank?
30
+
31
+ icon = options[:icon] || UI.ask("Please input icon file or icon url")
32
+ icon_file = File.open(icon) if File.exist? icon
33
+
34
+ external_url = options[:url] || UI.ask("Please input collection external url, start with https:// or http://")
35
+ split = options[:split] || UI.ask("Please input collection split", default: "0.0")
36
+ payload = {
37
+ name: name,
38
+ description: description,
39
+ external_url: external_url,
40
+ split: split
41
+ }
42
+
43
+ if icon_file.present?
44
+ payload[:icon_base64] = Base64.strict_encode64(icon_file.read)
45
+ else
46
+ payload[:icon_url] = icon
47
+ end
48
+
49
+ log api.create_collection(**payload)
50
+ rescue InvalidError => e
51
+ log UI.fmt("{{x}} failed: #{e.inspect}")
52
+ ensure
53
+ icon_file&.close
54
+ end
55
+
56
+ desc "update ID", "update collection"
57
+ option :description, type: :string, aliases: "d", desc: "collection Description"
58
+ option :icon, type: :string, aliases: "i", desc: "collection Icon"
59
+ option :url, type: :string, aliases: "u", desc: "collection External URL"
60
+ option :keystore, type: :string, aliases: "k", required: true, desc: "keystore or keystore.json file of Mixin bot"
61
+ def update(id)
62
+ payload = {}
63
+ payload[:description] = options[:description] if options[:description].present?
64
+ payload[:external_url] = options[:url] if options[:url].present?
65
+ if options[:icon].present? && File.exist?(options[:icon])
66
+ icon = File.open options[:icon]
67
+ payload[:icon_base64] = Base64.strict_encode64(icon.read)
68
+ end
69
+ log api.update_collection(id, **payload)
70
+ ensure
71
+ icon&.close
72
+ end
73
+
74
+ option :keystore, type: :string, aliases: "k", required: true, desc: "keystore or keystore.json file of Mixin bot"
75
+ desc "show ID", "query a collection"
76
+ def show(id)
77
+ log api.collection id
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./base"
4
+
5
+ module TridentAssistant
6
+ module CLI
7
+ # CLI to generate metadata
8
+ class Metadata < Base
9
+ class InvalidError < TridentAssistant::Error; end
10
+
11
+ desc "new", "generate a new metadata"
12
+ option :keystore, type: :string, aliases: "k", required: true, desc: "keystore or keystore.json file of Mixin bot"
13
+ def new
14
+ creator_id = api.mixin_bot.client_id
15
+ creator_name = api.mixin_bot.me["full_name"]
16
+ creator_royalty = UI.ask("Please input creator royalty, 0.0 ~ 0.1", default: "0.0")
17
+ raise InvalidError, "Royalty must in 0.0 ~ 0.1" unless (0..0.1).include?(creator_royalty.to_f)
18
+
19
+ collection_id = UI.ask("Please input collection ID")
20
+ collection = api.collection collection_id
21
+ raise InvalidError, "Cannot find collection #{collection_id}" if collection.blank?
22
+
23
+ if collection["creator"]&.[]("id") != api.mixin_bot.client_id
24
+ raise InvalidError,
25
+ "Unauthorized to mint in #{collection_id}"
26
+ end
27
+
28
+ token_id = UI.ask("Please input token ID", default: "1")
29
+ token_name = UI.ask("Please input token name")
30
+ token_description = UI.ask("Please input token description")
31
+
32
+ token_icon_url = UI.ask("Please input token icon url or local file dir")
33
+ if File.file? token_icon_url
34
+ begin
35
+ file = File.open token_icon_url
36
+ res = api.mixin_bot.upload_attachment file
37
+ token_icon_url = res["view_url"]
38
+ ensure
39
+ file&.close
40
+ end
41
+ end
42
+
43
+ token_media_url = UI.ask("Please input token media url or local file dir")
44
+ if File.file? token_media_url
45
+ begin
46
+ file = File.open token_media_url
47
+ res = api.mixin_bot.upload_attachment file
48
+ token_media_url = res["view_url"]
49
+ token_media_hash = SHA3::Digest::SHA256.hexdigest file.read
50
+ ensure
51
+ file&.close
52
+ end
53
+ elsif token_media_url =~ URI::DEFAULT_PARSER.make_regexp
54
+ token_media_hash = TridentAssistant::Utils.hash_from_url(token_media_url)
55
+ end
56
+
57
+ metadata = TridentAssistant::Utils::Metadata.new(
58
+ creator: {
59
+ id: creator_id,
60
+ name: creator_name,
61
+ royalty: creator_royalty
62
+ },
63
+ collection: {
64
+ id: collection_id,
65
+ name: collection["name"],
66
+ description: collection["description"],
67
+ icon: {
68
+ url: collection["icon"]&.[]("url")
69
+ },
70
+ split: collection["split"].to_s
71
+ },
72
+ token: {
73
+ id: token_id,
74
+ name: token_name,
75
+ description: token_description,
76
+ icon: {
77
+ url: token_icon_url
78
+ },
79
+ media: {
80
+ url: token_media_url,
81
+ hash: token_media_hash.to_s
82
+ }
83
+ },
84
+ checksum: {
85
+ fields: ["creator.id", "creator.royalty", "collection.id", "collection.name",
86
+ "collection.split", "token.id", "token.name", "token.media.hash"],
87
+ algorithm: "sha256"
88
+ }
89
+ )
90
+
91
+ File.write "#{token_id}_#{collection_id}.json", metadata.json.to_json
92
+ log metadata.json
93
+ end
94
+
95
+ desc "show METAHASH", "query metadata via metahash"
96
+ option :keystore, type: :string, aliases: "k", required: true, desc: "keystore or keystore.json file of Mixin bot"
97
+ def show(metahash)
98
+ log api.metadata metahash
99
+ end
100
+
101
+ desc "upload", "upload metadata to Trident"
102
+ option :metadata, type: :string, aliases: "m", required: true, desc: "metadata or metadata.json file"
103
+ option :keystore, type: :string, aliases: "k", required: true, desc: "keystore or keystore.json file of Mixin bot"
104
+ def upload
105
+ metadata = TridentAssistant::Utils.parse_metadata options[:metadata]
106
+ log UI.fmt("{{v}} metadata parsed")
107
+
108
+ metadata.validate!
109
+ log UI.fmt("{{v}} metadata validated")
110
+
111
+ # upload metadata
112
+ api.upload_metadata metadata: metadata.json, metahash: metadata.metahash
113
+ log UI.fmt("{{v}} metadata uploaded: #{options[:endpoint]}/api/collectibles/#{metadata.metahash}")
114
+ rescue JSON::ParserError, Client::RequestError, InvalidError => e
115
+ log UI.fmt("{{x}} #{e.inspect}")
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./base"
4
+
5
+ module TridentAssistant
6
+ module CLI
7
+ # CLI to mint NFTs
8
+ class NFO < Base
9
+ desc "mint", "Mint NFT from NFO"
10
+ option :metadata, type: :string, aliases: "m", required: true, desc: "metadata or metadata.json file"
11
+ option :keystore, type: :string, aliases: "k", required: true, desc: "keystore or keystore.json file of Mixin bot"
12
+ def mint
13
+ _mint options[:metadata]
14
+ rescue JSON::ParserError, Client::RequestError, TridentAssistant::Utils::Metadata::InvalidFormatError,
15
+ MixinBot::Error, RuntimeError => e
16
+ log UI.fmt("{{x}} #{e.inspect}")
17
+ end
18
+
19
+ desc "bulkmint DIR", "Mint NFT in bulk"
20
+ option :keystore, type: :string, aliases: "k", required: true, desc: "keystore or keystore.json file of Mixin bot"
21
+ def bulkmint(dir)
22
+ raise "#{dir} is not a directory" unless Dir.exist?(dir)
23
+
24
+ Dir.glob("#{dir}/*.json").each do |file|
25
+ log UI.fmt("{{v}} found #{file}")
26
+ _mint file
27
+ rescue TridentAssistant::Utils::Metadata::InvalidFormatError, JSON::ParserError, Client::RequestError,
28
+ MixinBot::Error, RuntimeError => e
29
+ log UI.fmt("{{x}} #{file} failed: #{e.inspect}")
30
+ next
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def _mint(raw)
37
+ # parse metadata
38
+ data = TridentAssistant::Utils.parse_json raw
39
+ metadata = TridentAssistant::Utils.parse_metadata data
40
+ log UI.fmt("{{v}} metadata parsed")
41
+
42
+ # validate metadata
43
+ metadata.validate!
44
+ log UI.fmt("{{v}} metadata validated")
45
+
46
+ raise "Creator ID incompatible with keystore" if metadata.creator[:id] != api.mixin_bot.client_id
47
+
48
+ token_id = MixinBot::Utils::Nfo.new(collection: metadata.collection[:id],
49
+ token: metadata.token[:id]).unique_token_id
50
+ collectible =
51
+ begin
52
+ api.mixin_bot.collectible token_id
53
+ rescue MixinBot::NotFoundError
54
+ nil
55
+ end
56
+ if collectible.present?
57
+ log UI.fmt("{{v}} already minted: #{token_id}")
58
+ return
59
+ end
60
+
61
+ # upload metadata
62
+ if data.dig("_mint", "metahash").blank?
63
+ api.upload_metadata metadata: metadata.json, metahash: metadata.metahash
64
+ data["_mint"] ||= {}
65
+ data["_mint"]["metahash"] = metadata.metahash
66
+ end
67
+ log UI.fmt("{{v}} metadata uploaded: #{options[:endpoint]}/api/collectibles/#{metadata.metahash}")
68
+
69
+ # pay to NFO
70
+ trace_id = data.dig("_mint", "trace_id") || SecureRandom.uuid
71
+ memo = api.mixin_bot.nft_memo metadata.collection[:id], metadata.token[:id].to_i, metadata.metahash
72
+ payment =
73
+ api.mixin_bot.create_multisig_transaction(
74
+ api.keystore[:pin],
75
+ {
76
+ asset_id: TridentAssistant::Utils::MINT_ASSET_ID,
77
+ trace_id: trace_id,
78
+ amount: TridentAssistant::Utils::MINT_AMOUNT,
79
+ memo: memo,
80
+ receivers: TridentAssistant::Utils::NFO_MTG[:members],
81
+ threshold: TridentAssistant::Utils::NFO_MTG[:threshold]
82
+ }
83
+ )
84
+
85
+ data["_mint"]["trace_id"] = trace_id
86
+ if payment["errors"].blank?
87
+ log UI.fmt("{{v}} NFT mint payment paid: #{payment["data"]}")
88
+ data["_mint"]["token_id"] = token_id
89
+ end
90
+
91
+ log UI.fmt("{{v}} NFT successfully minted")
92
+ ensure
93
+ if File.file? raw
94
+ File.write raw, data.to_json
95
+ else
96
+ log data
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./base"
4
+
5
+ module TridentAssistant
6
+ module CLI
7
+ # CLI to transfer to Trident MTG
8
+ class Order < Base
9
+ EXCHANGE_ASSET_ID = "965e5c6e-434c-3fa9-b780-c50f43cd955c"
10
+ MINIMUM_AMOUNT = 0.000_000_01
11
+
12
+ desc "index", "list orders"
13
+ option :keystore, type: :string, aliases: "k", required: true, desc: "keystore or keystore.json file of Mixin bot"
14
+ option :collection, type: :string, aliases: "c", required: false, desc: "collection ID"
15
+ option :metahash, type: :string, aliases: "m", required: false, desc: "metahash"
16
+ option :type, type: :string, aliases: "t", required: false, desc: "ask | bid | auction"
17
+ option :state, type: :string, aliases: "s", required: false, desc: "open | completed"
18
+ def index
19
+ log api.orders(
20
+ collection_id: options[:collection],
21
+ metahash: options[:metahash],
22
+ state: options[:state],
23
+ type: options[:type]
24
+ )
25
+ end
26
+
27
+ desc "show ID", "query order"
28
+ option :keystore, type: :string, aliases: "k", required: true, desc: "keystore or keystore.json file of Mixin bot"
29
+ def show(id)
30
+ log api.order id
31
+ end
32
+
33
+ desc "sell", "sell NFT at fixed price"
34
+ def ask; end
35
+
36
+ desc "auction", "auction NFT"
37
+ def auction; end
38
+
39
+ desc "bid", "bid NFT"
40
+ def bid; end
41
+
42
+ desc "fill ID", "fill order"
43
+ option :keystore, type: :string, aliases: "k", required: true, desc: "keystore or keystore.json file of Mixin bot"
44
+ def fill(id)
45
+ log api.fill_order id
46
+ end
47
+
48
+ desc "cancel ID", "cancel order"
49
+ option :keystore, type: :string, aliases: "k", required: true, desc: "keystore or keystore.json file of Mixin bot"
50
+ def cancel(id)
51
+ log api.cancel_order id
52
+ end
53
+
54
+ desc "deposit TOKEN", "deposit NFT"
55
+ def deposit(token)
56
+ log api.deposit_nft token
57
+ end
58
+
59
+ desc "withdraw TOKEN", "withdraw NFT"
60
+ option :keystore, type: :string, aliases: "k", required: true, desc: "keystore or keystore.json file of Mixin bot"
61
+ def withdraw(token)
62
+ payment =
63
+ api.mixin_bot.create_multisig_transaction(
64
+ keystore[:pin],
65
+ asset_id: EXCHANGE_ASSET_ID,
66
+ amount: MINIMUM_AMOUNT,
67
+ receivers: TridentAssistant::Utils::TRIDENT_MTG[:members],
68
+ threshold: TridentAssistant::Utils::TRIDENT_MTG[:threshold],
69
+ memo: TridentAssistant::Utils::Memo.new(type: "W", token_id: token).encode,
70
+ trace_id: SecureRandom.uuid
71
+ )
72
+
73
+ log UI.fmt("{{v}} payment: #{payment}")
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./base"
4
+
5
+ module TridentAssistant
6
+ module CLI
7
+ # CLI for utils
8
+ class Utils < Base
9
+ desc "hash INPUT", "Hash a string or file using sha256"
10
+ option :file, type: :boolean, aliases: "f", default: false, desc: "Hash a file"
11
+ def hash(_input)
12
+ content =
13
+ if options[:file]
14
+ File.read options[:input]
15
+ else
16
+ options[:input].to_s
17
+ end
18
+
19
+ log SHA3::Digest::SHA256.hexdigest(content)
20
+ end
21
+ end
22
+ end
23
+ end