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.
- checksums.yaml +7 -0
- data/.rubocop.yml +132 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +106 -0
- data/LICENSE.txt +21 -0
- data/README.md +47 -0
- data/Rakefile +16 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/exe/ta +6 -0
- data/lib/trident_assistant/api/collectible.rb +81 -0
- data/lib/trident_assistant/api/collection.rb +61 -0
- data/lib/trident_assistant/api/metadata.rb +26 -0
- data/lib/trident_assistant/api/order.rb +77 -0
- data/lib/trident_assistant/api.rb +27 -0
- data/lib/trident_assistant/cli/base.rb +55 -0
- data/lib/trident_assistant/cli/collectible.rb +100 -0
- data/lib/trident_assistant/cli/collection.rb +81 -0
- data/lib/trident_assistant/cli/metadata.rb +119 -0
- data/lib/trident_assistant/cli/nfo.rb +101 -0
- data/lib/trident_assistant/cli/order.rb +77 -0
- data/lib/trident_assistant/cli/utils.rb +23 -0
- data/lib/trident_assistant/cli.rb +45 -0
- data/lib/trident_assistant/client.rb +116 -0
- data/lib/trident_assistant/utils/memo.rb +62 -0
- data/lib/trident_assistant/utils/metadata.rb +117 -0
- data/lib/trident_assistant/utils.rb +88 -0
- data/lib/trident_assistant/version.rb +5 -0
- data/lib/trident_assistant.rb +18 -0
- metadata +92 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "./cli/base"
|
4
|
+
require_relative "./cli/collectible"
|
5
|
+
require_relative "./cli/collection"
|
6
|
+
require_relative "./cli/metadata"
|
7
|
+
require_relative "./cli/nfo"
|
8
|
+
require_relative "./cli/order"
|
9
|
+
require_relative "./cli/utils"
|
10
|
+
|
11
|
+
module TridentAssistant
|
12
|
+
# CLI tool
|
13
|
+
module CLI
|
14
|
+
# Main commands of CLI
|
15
|
+
class Command < TridentAssistant::CLI::Base
|
16
|
+
class_option :pretty, type: :boolean, aliases: "-p", default: true, desc: "Print output in pretty"
|
17
|
+
class_option :environment, type: :string, aliases: "-e", default: "prod", desc: "prod | test | dev"
|
18
|
+
class_option :endpoint, type: :string, aliases: "-E", desc: "Specify an endpoint"
|
19
|
+
|
20
|
+
desc "version", "Display TridentAssistant version"
|
21
|
+
def version
|
22
|
+
log VERSION
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.exit_on_failure?
|
26
|
+
true
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "collectible", "commands for collectible"
|
30
|
+
subcommand "collectible", CLI::Collectible
|
31
|
+
|
32
|
+
desc "collection", "commands for collection"
|
33
|
+
subcommand "collection", CLI::Collection
|
34
|
+
|
35
|
+
desc "nfo", "commands for nfo"
|
36
|
+
subcommand "nfo", CLI::NFO
|
37
|
+
|
38
|
+
desc "metadata", "commands for metadata"
|
39
|
+
subcommand "metadata", CLI::Metadata
|
40
|
+
|
41
|
+
desc "order", "commands for order"
|
42
|
+
subcommand "order", CLI::Order
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TridentAssistant
|
4
|
+
# HTTP client to trident server
|
5
|
+
class Client
|
6
|
+
class HttpError < TridentAssistant::Error; end
|
7
|
+
class RequestError < TridentAssistant::Error; end
|
8
|
+
|
9
|
+
ENDPOINT = "https://thetrident.one"
|
10
|
+
|
11
|
+
attr_reader :endpoint
|
12
|
+
|
13
|
+
def initialize(**kwargs)
|
14
|
+
@endpoint = URI(kwargs[:endpoint] || ENDPOINT)
|
15
|
+
end
|
16
|
+
|
17
|
+
def get(path, **options)
|
18
|
+
request(:get, path, **options)
|
19
|
+
end
|
20
|
+
|
21
|
+
def post(path, **options)
|
22
|
+
request(:post, path, **options)
|
23
|
+
end
|
24
|
+
|
25
|
+
def put(path, **options)
|
26
|
+
request(:put, path, **options)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def request(verb, path, **options)
|
32
|
+
uri = uri_for path
|
33
|
+
|
34
|
+
options[:headers] ||= {}
|
35
|
+
options[:headers]["Content-Type"] ||= "application/json"
|
36
|
+
|
37
|
+
begin
|
38
|
+
response = HTTP.timeout(connect: 5, write: 10, read: 10).request(verb, uri, options)
|
39
|
+
rescue HTTP::Error => e
|
40
|
+
raise HttpError, e.message
|
41
|
+
end
|
42
|
+
|
43
|
+
raise RequestError, response.to_s unless response.status.success?
|
44
|
+
|
45
|
+
parse_response(response) do |parse_as, result|
|
46
|
+
case parse_as
|
47
|
+
when :json
|
48
|
+
break result if result["message"].blank?
|
49
|
+
|
50
|
+
raise result["message"]
|
51
|
+
else
|
52
|
+
result
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def uri_for(path)
|
58
|
+
uri_options = {
|
59
|
+
scheme: endpoint.scheme,
|
60
|
+
host: endpoint.host,
|
61
|
+
port: endpoint.port,
|
62
|
+
path: path
|
63
|
+
}
|
64
|
+
Addressable::URI.new(uri_options)
|
65
|
+
end
|
66
|
+
|
67
|
+
def parse_response(response)
|
68
|
+
content_type = response.headers[:content_type]
|
69
|
+
parse_as = {
|
70
|
+
%r{^application/json} => :json,
|
71
|
+
%r{^image/.*} => :file,
|
72
|
+
%r{^text/html} => :xml,
|
73
|
+
%r{^text/plain} => :plain
|
74
|
+
}.each_with_object([]) { |match, memo| memo << match[1] if content_type =~ match[0] }.first || :plain
|
75
|
+
|
76
|
+
if parse_as == :plain
|
77
|
+
result = JSON.parse(response&.body&.to_s)
|
78
|
+
result && yield(:json, result)
|
79
|
+
|
80
|
+
yield(:plain, response.body)
|
81
|
+
end
|
82
|
+
|
83
|
+
case parse_as
|
84
|
+
when :json
|
85
|
+
result = JSON.parse(response.body.to_s)
|
86
|
+
when :file
|
87
|
+
extension =
|
88
|
+
if response.headers[:content_type] =~ %r{^image/.*}
|
89
|
+
{
|
90
|
+
"image/gif": ".gif",
|
91
|
+
"image/jpeg": ".jpg",
|
92
|
+
"image/png": ".png"
|
93
|
+
}[response.headers["content-type"]]
|
94
|
+
else
|
95
|
+
""
|
96
|
+
end
|
97
|
+
|
98
|
+
begin
|
99
|
+
file = Tempfile.new(["mixin-file-", extension])
|
100
|
+
file.binmode
|
101
|
+
file.write(response.body)
|
102
|
+
ensure
|
103
|
+
file&.close
|
104
|
+
end
|
105
|
+
|
106
|
+
result = file
|
107
|
+
when :xml
|
108
|
+
result = Hash.from_xml(response.body.to_s)
|
109
|
+
else
|
110
|
+
result = response.body
|
111
|
+
end
|
112
|
+
|
113
|
+
yield(parse_as, result)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TridentAssistant
|
4
|
+
module Utils
|
5
|
+
# build metadata of NFT
|
6
|
+
class Memo
|
7
|
+
attr_accessor :type, :token_id, :asset_id, :order_id, :price, :reserve_price, :receiver_id, :start_at,
|
8
|
+
:expire_at, :encoded, :decoded
|
9
|
+
|
10
|
+
def initialize(**kwargs)
|
11
|
+
@type = kwargs[:type]
|
12
|
+
@token_id = kwargs[:token_id]
|
13
|
+
@asset_id = kwargs[:asset_id]
|
14
|
+
@order_id = kwargs[:order_id]
|
15
|
+
@price = kwargs[:price]
|
16
|
+
@reserve_price = kwargs[:reserve_price]
|
17
|
+
@receiver_id = kwargs[:receiver_id]
|
18
|
+
@start_at = kwargs[:start_at]
|
19
|
+
@expire_at = kwargs[:expire_at]
|
20
|
+
@encoded = kwargs[:encoded]
|
21
|
+
end
|
22
|
+
|
23
|
+
def json
|
24
|
+
{
|
25
|
+
T: type,
|
26
|
+
N: token_id,
|
27
|
+
A: asset_id,
|
28
|
+
O: order_id,
|
29
|
+
P: price,
|
30
|
+
R: reserve_price,
|
31
|
+
RC: receiver_id,
|
32
|
+
S: start_at,
|
33
|
+
E: expire_at
|
34
|
+
}.compact
|
35
|
+
end
|
36
|
+
|
37
|
+
def encode
|
38
|
+
hash = {
|
39
|
+
T: type,
|
40
|
+
N: token_id && MixinBot::Utils::UUID.new(hex: token_id).packed,
|
41
|
+
A: asset_id && MixinBot::Utils::UUID.new(hex: asset_id).packed,
|
42
|
+
O: order_id && MixinBot::Utils::UUID.new(hex: order_id).packed,
|
43
|
+
P: price && format("%.8f", price.to_f).gsub(/(0)+\z/, ""),
|
44
|
+
R: reserve_price && format("%.8f", reserve_price.to_f).gsub(/(0)+\z/, ""),
|
45
|
+
RC: receiver_id && MixinBot::Utils::UUID.new(hex: receiver_id).packed,
|
46
|
+
S: start_at && Time.parse(start_at).to_i,
|
47
|
+
E: expire_at && Time.parse(start_at).to_i
|
48
|
+
}.compact
|
49
|
+
|
50
|
+
@encoded =
|
51
|
+
Base64.urlsafe_encode64(
|
52
|
+
MessagePack.pack(hash),
|
53
|
+
padding: false
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
def decode
|
58
|
+
json
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TridentAssistant
|
4
|
+
module Utils
|
5
|
+
# build metadata of NFT
|
6
|
+
class Metadata
|
7
|
+
class InvalidFormatError < TridentAssistant::Error; end
|
8
|
+
|
9
|
+
attr_reader :creator, :collection, :token, :checksum
|
10
|
+
|
11
|
+
def initialize(**kwargs)
|
12
|
+
@creator = kwargs[:creator] || {}
|
13
|
+
@collection = kwargs[:collection] || {}
|
14
|
+
@token = kwargs[:token] || {}
|
15
|
+
@checksum = kwargs[:checksum] || {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def json
|
19
|
+
{
|
20
|
+
creator: creator,
|
21
|
+
collection: collection,
|
22
|
+
token: token,
|
23
|
+
checksum: checksum
|
24
|
+
}.compact.with_indifferent_access
|
25
|
+
end
|
26
|
+
|
27
|
+
def validate!
|
28
|
+
raise InvalidFormatError, creator unless creator_valid?
|
29
|
+
raise InvalidFormatError, collection unless collection_valid?
|
30
|
+
raise InvalidFormatError, token unless token_valid?
|
31
|
+
raise InvalidFormatError, checksum unless checksum_valid?
|
32
|
+
raise InvalidFormatError, "failed to validate hash" unless all_hash_valid?
|
33
|
+
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
37
|
+
def valid?
|
38
|
+
creator_valid? && collection_valid? && token_valid? && checksum_valid? && all_hash_valid?
|
39
|
+
end
|
40
|
+
|
41
|
+
def creator_valid?
|
42
|
+
return false unless creator.is_a?(Hash)
|
43
|
+
|
44
|
+
@creator = creator.with_indifferent_access
|
45
|
+
creator["id"].present? && creator["name"].present?
|
46
|
+
end
|
47
|
+
|
48
|
+
def collection_valid?
|
49
|
+
return false unless collection.is_a?(Hash)
|
50
|
+
|
51
|
+
@collection = collection.with_indifferent_access
|
52
|
+
return false unless collection["id"].match?(/\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/)
|
53
|
+
return false if collection["name"].blank?
|
54
|
+
|
55
|
+
true
|
56
|
+
end
|
57
|
+
|
58
|
+
def token_valid?
|
59
|
+
return false unless token.is_a?(Hash)
|
60
|
+
|
61
|
+
@token = token.with_indifferent_access
|
62
|
+
return false unless token["id"].match?(/\A(?!0)\d+\z/)
|
63
|
+
return false if token["id"].to_i > 8**64
|
64
|
+
return false if token["name"].blank?
|
65
|
+
|
66
|
+
true
|
67
|
+
end
|
68
|
+
|
69
|
+
def checksum_valid?
|
70
|
+
return false unless checksum.is_a?(Hash)
|
71
|
+
|
72
|
+
@checksum = checksum.with_indifferent_access
|
73
|
+
checksum["algorithm"] && checksum["fields"].is_a?(Array)
|
74
|
+
end
|
75
|
+
|
76
|
+
def all_hash_valid?
|
77
|
+
checksum["fields"].each do |field|
|
78
|
+
value = json
|
79
|
+
|
80
|
+
field.split(".").each do |key|
|
81
|
+
next unless value.is_a?(Hash)
|
82
|
+
|
83
|
+
return false if key == "hash" && value["hash"] != TridentAssistant::Utils.hash_from_url(value["url"])
|
84
|
+
|
85
|
+
value = value[key]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
true
|
90
|
+
end
|
91
|
+
|
92
|
+
def checksum_content
|
93
|
+
return unless checksum_valid?
|
94
|
+
|
95
|
+
checksum["fields"].map do |field|
|
96
|
+
value = json
|
97
|
+
field.split(".").each do |key|
|
98
|
+
value = (value[key] if value.is_a?(Hash))
|
99
|
+
end
|
100
|
+
value.to_s
|
101
|
+
end.join
|
102
|
+
end
|
103
|
+
|
104
|
+
def metahash
|
105
|
+
return unless valid?
|
106
|
+
|
107
|
+
alg = checksum["algorithm"]
|
108
|
+
case alg
|
109
|
+
when "sha256", "sha3-256"
|
110
|
+
SHA3::Digest::SHA256.hexdigest checksum_content
|
111
|
+
else
|
112
|
+
raise "algorithm #{alg} not supported!"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "./utils/memo"
|
4
|
+
require_relative "./utils/metadata"
|
5
|
+
|
6
|
+
module TridentAssistant
|
7
|
+
# Some useful methods
|
8
|
+
module Utils
|
9
|
+
MINT_ASSET_ID = "c94ac88f-4671-3976-b60a-09064f1811e8"
|
10
|
+
MINT_AMOUNT = 0.001
|
11
|
+
NFO_MTG = {
|
12
|
+
members: %w[
|
13
|
+
047061e6-496d-4c35-b06b-b0424a8a400d
|
14
|
+
4b188942-9fb0-4b99-b4be-e741a06d1ebf
|
15
|
+
50115496-7247-4e2c-857b-ec8680756bee
|
16
|
+
a51006d0-146b-4b32-a2ce-7defbf0d7735
|
17
|
+
acf65344-c778-41ee-bacb-eb546bacfb9f
|
18
|
+
cf4abd9c-2cfa-4b5a-b1bd-e2b61a83fabd
|
19
|
+
dd655520-c919-4349-822f-af92fabdbdf4
|
20
|
+
].sort,
|
21
|
+
threshold: 5
|
22
|
+
}.freeze
|
23
|
+
TRIDENT_MTG = {
|
24
|
+
members: %w[
|
25
|
+
6f5a84ce-d663-451e-b413-2d0c84b7629d
|
26
|
+
99216bb5-9787-49b6-b251-537362ce23eb
|
27
|
+
fcc1e3ec-97f1-41a7-89a3-20a0ec30a31f
|
28
|
+
1a361145-66a7-48cd-ab6c-db9a42da4074
|
29
|
+
9ba50819-159c-45e6-9bed-1eb2dded3360
|
30
|
+
].sort,
|
31
|
+
threshold: 3
|
32
|
+
}.freeze
|
33
|
+
|
34
|
+
class << self
|
35
|
+
def hash_from_url(url)
|
36
|
+
return if url.to_s.blank?
|
37
|
+
|
38
|
+
content =
|
39
|
+
begin
|
40
|
+
URI.parse(url).open(&:read)
|
41
|
+
rescue OpenURI::HTTPError
|
42
|
+
""
|
43
|
+
end
|
44
|
+
|
45
|
+
SHA3::Digest::SHA256.hexdigest content
|
46
|
+
end
|
47
|
+
|
48
|
+
def mixin_bot_from_keystore(keystore)
|
49
|
+
keystore = parse_json keystore if keystore.is_a?(String)
|
50
|
+
|
51
|
+
MixinBot::API.new(
|
52
|
+
client_id: keystore["client_id"],
|
53
|
+
session_id: keystore["session_id"],
|
54
|
+
pin_token: keystore["pin_token"],
|
55
|
+
private_key: keystore["private_key"]
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
def parse_metadata(input)
|
60
|
+
metadata =
|
61
|
+
case input
|
62
|
+
when String
|
63
|
+
TridentAssistant::Utils.parse_json input
|
64
|
+
when Hash
|
65
|
+
input
|
66
|
+
else
|
67
|
+
{}
|
68
|
+
end
|
69
|
+
TridentAssistant::Utils::Metadata.new(
|
70
|
+
creator: metadata["creator"],
|
71
|
+
collection: metadata["collection"],
|
72
|
+
token: metadata["token"],
|
73
|
+
checksum: metadata["checksum"]
|
74
|
+
)
|
75
|
+
end
|
76
|
+
|
77
|
+
def parse_json(input)
|
78
|
+
input =
|
79
|
+
if File.file? input
|
80
|
+
File.read input
|
81
|
+
else
|
82
|
+
input
|
83
|
+
end
|
84
|
+
JSON.parse(input).with_indifferent_access
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "mixin_bot"
|
4
|
+
require "open-uri"
|
5
|
+
|
6
|
+
# Trident SDK
|
7
|
+
module TridentAssistant
|
8
|
+
class Error < StandardError; end
|
9
|
+
|
10
|
+
def self.api
|
11
|
+
TridentAssistant::API.new
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
require_relative "trident_assistant/api"
|
16
|
+
require_relative "trident_assistant/cli"
|
17
|
+
require_relative "trident_assistant/utils"
|
18
|
+
require_relative "trident_assistant/version"
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: trident_assistant
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- an-lee
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-04-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: mixin_bot
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.8'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.8'
|
27
|
+
description: A simple program to use Trident NFT
|
28
|
+
email:
|
29
|
+
- an.lee.work@gmail.com
|
30
|
+
executables:
|
31
|
+
- ta
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- ".rubocop.yml"
|
36
|
+
- CHANGELOG.md
|
37
|
+
- CODE_OF_CONDUCT.md
|
38
|
+
- Gemfile
|
39
|
+
- Gemfile.lock
|
40
|
+
- LICENSE.txt
|
41
|
+
- README.md
|
42
|
+
- Rakefile
|
43
|
+
- bin/console
|
44
|
+
- bin/setup
|
45
|
+
- exe/ta
|
46
|
+
- lib/trident_assistant.rb
|
47
|
+
- lib/trident_assistant/api.rb
|
48
|
+
- lib/trident_assistant/api/collectible.rb
|
49
|
+
- lib/trident_assistant/api/collection.rb
|
50
|
+
- lib/trident_assistant/api/metadata.rb
|
51
|
+
- lib/trident_assistant/api/order.rb
|
52
|
+
- lib/trident_assistant/cli.rb
|
53
|
+
- lib/trident_assistant/cli/base.rb
|
54
|
+
- lib/trident_assistant/cli/collectible.rb
|
55
|
+
- lib/trident_assistant/cli/collection.rb
|
56
|
+
- lib/trident_assistant/cli/metadata.rb
|
57
|
+
- lib/trident_assistant/cli/nfo.rb
|
58
|
+
- lib/trident_assistant/cli/order.rb
|
59
|
+
- lib/trident_assistant/cli/utils.rb
|
60
|
+
- lib/trident_assistant/client.rb
|
61
|
+
- lib/trident_assistant/utils.rb
|
62
|
+
- lib/trident_assistant/utils/memo.rb
|
63
|
+
- lib/trident_assistant/utils/metadata.rb
|
64
|
+
- lib/trident_assistant/version.rb
|
65
|
+
homepage: https://github.com/TheTridentOne/trident_assistant
|
66
|
+
licenses:
|
67
|
+
- MIT
|
68
|
+
metadata:
|
69
|
+
homepage_uri: https://github.com/TheTridentOne/trident_assistant
|
70
|
+
source_code_uri: https://github.com/TheTridentOne/trident_assistant
|
71
|
+
changelog_uri: https://github.com/TheTridentOne/trident_assistant
|
72
|
+
rubygems_mfa_required: 'true'
|
73
|
+
post_install_message:
|
74
|
+
rdoc_options: []
|
75
|
+
require_paths:
|
76
|
+
- lib
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: 2.6.0
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
requirements: []
|
88
|
+
rubygems_version: 3.3.4
|
89
|
+
signing_key:
|
90
|
+
specification_version: 4
|
91
|
+
summary: A simple program to use Trident NFT
|
92
|
+
test_files: []
|