trident_assistant 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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