artq 0.2.0 → 0.3.1
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/Manifest.txt +2 -0
- data/README.md +37 -2
- data/lib/artq/contract.rb +6 -6
- data/lib/artq/layers.rb +100 -0
- data/lib/artq/tokens.rb +107 -0
- data/lib/artq/version.rb +1 -1
- data/lib/artq.rb +53 -108
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 173bf8cbe3bc0e9db811e19b585bd04bc23956b28b1b4cb055c1697288472d7e
|
4
|
+
data.tar.gz: 90c2751c1028ca2986e12825f83d8afbb9055ce864228f5a611a9110481f1439
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 339f0a49d30a544ce53892ec7f3c1a04a3ddb47d279ddc938d995ae8efc7c25356db9920a44f06d61a5d3482efe9975a965ba791ca6c6603cc427a6353914ab1
|
7
|
+
data.tar.gz: db571cdfcd4e29e3486199ac775a5494e1f4a42256d7d7449c8adbc3fdf7d4259356863dd3f440b60b94bcc30cf104c365c727965226eefe6a9e1ee849aa23da
|
data/Manifest.txt
CHANGED
data/README.md
CHANGED
@@ -184,7 +184,7 @@ and inline svg images in the base64 format get "cut" from the metadata and "past
|
|
184
184
|
```
|
185
185
|
|
186
186
|
|
187
|
-
####
|
187
|
+
#### Case No. 3 - "On-Blockchain" Layers (Incl. Metadata & Images)
|
188
188
|
|
189
189
|
|
190
190
|
Note: Some "on-blockchain" pixel art collections
|
@@ -339,7 +339,42 @@ such as
|
|
339
339
|
and many more.
|
340
340
|
|
341
341
|
|
342
|
-
Tip:
|
342
|
+
Tip: For more art collections with "on-blockchain" layers see the [**Art Factory Sandbox »**](https://github.com/pixelartexchange/artfactory.sandbox)
|
343
|
+
|
344
|
+
|
345
|
+
|
346
|
+
|
347
|
+
|
348
|
+
### Using the ArtQ Machinery in Your Own Scripts
|
349
|
+
|
350
|
+
|
351
|
+
Yes, you can. Let's try the (crypto) marcs:
|
352
|
+
|
353
|
+
|
354
|
+
``` ruby
|
355
|
+
require 'artq'
|
356
|
+
|
357
|
+
marcs_eth = "0xe9b91d537c3aa5a3fa87275fbd2e4feaaed69bd0"
|
358
|
+
|
359
|
+
marcs = ArtQ::Contract.new( marcs_eth )
|
360
|
+
|
361
|
+
n = 0
|
362
|
+
m = 0
|
363
|
+
res = marcs.traitData( n, m ) ## note: return binary blob (for n,m-index)
|
364
|
+
pp res
|
365
|
+
#=> ["\x89PNG..."]
|
366
|
+
|
367
|
+
res = marcs.traitDetails( n, m ) ## note: returns tuple (name, mimetype, hide?)
|
368
|
+
pp res
|
369
|
+
#=> ["Zombie", "image/png", false]
|
370
|
+
|
371
|
+
|
372
|
+
|
373
|
+
## or with convenience download_layers helper
|
374
|
+
ArtQ.download_layers( marcs_eth, outdir: './marcs' )
|
375
|
+
```
|
376
|
+
|
377
|
+
|
343
378
|
|
344
379
|
|
345
380
|
|
data/lib/artq/contract.rb
CHANGED
@@ -50,13 +50,12 @@ class Contract
|
|
50
50
|
|
51
51
|
|
52
52
|
|
53
|
-
|
54
|
-
|
55
|
-
|
53
|
+
## note: forward to Ethlite.config.rpc
|
54
|
+
## keep config here as a convenience shortcut - why? why not?
|
55
|
+
def self.rpc() Ethlite.config.rpc; end
|
56
|
+
def self.rpc=(value) Ethlite.config.rpc = value; end
|
57
|
+
|
56
58
|
|
57
|
-
def self.rpc=(value)
|
58
|
-
@rpc = value.is_a?( String ) ? JsonRpc.new( value ) : value
|
59
|
-
end
|
60
59
|
|
61
60
|
|
62
61
|
def initialize( contract_address )
|
@@ -66,6 +65,7 @@ class Contract
|
|
66
65
|
######
|
67
66
|
# private helper to call method
|
68
67
|
def _do_call( eth, args )
|
68
|
+
puts " calling method >#{eth.name}< with args >#{args}<... "
|
69
69
|
eth.do_call( self.class.rpc, @contract_address, args )
|
70
70
|
end
|
71
71
|
|
data/lib/artq/layers.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
|
2
|
+
module ArtQ
|
3
|
+
|
4
|
+
|
5
|
+
## use alternate Encoding::BINARY - why? why not?
|
6
|
+
## or just use .b e.g. "GIF87".b or such !!!
|
7
|
+
JPGSIG = "\xFF\xD8\xFF".force_encoding( Encoding::ASCII_8BIT )
|
8
|
+
PNGSIG = "\x89PNG".force_encoding( Encoding::ASCII_8BIT )
|
9
|
+
GIF87SIG = "GIF87".force_encoding( Encoding::ASCII_8BIT )
|
10
|
+
GIF89SIG = "GIF89".force_encoding( Encoding::ASCII_8BIT )
|
11
|
+
|
12
|
+
|
13
|
+
|
14
|
+
def self.download_layers( contract_address,
|
15
|
+
outdir: "./#{contract_address}" )
|
16
|
+
|
17
|
+
puts "==> download layers for art collection contract @ >#{contract_address}<:"
|
18
|
+
|
19
|
+
c = Contract.new( contract_address )
|
20
|
+
|
21
|
+
## get some metadata - why? why not?
|
22
|
+
name = c.name
|
23
|
+
symbol = c.symbol
|
24
|
+
totalSupply = c.totalSupply
|
25
|
+
|
26
|
+
traitDetails = []
|
27
|
+
n=0
|
28
|
+
loop do
|
29
|
+
m=0
|
30
|
+
loop do
|
31
|
+
rec = c.traitDetails( n, m )
|
32
|
+
break if rec == ['','',false] ## note: assume end-of-list if all values are zeros / defaults.
|
33
|
+
|
34
|
+
traitDetails << [[n,m], rec ]
|
35
|
+
m += 1
|
36
|
+
sleep( 0.5 )
|
37
|
+
end
|
38
|
+
break if m==0
|
39
|
+
n += 1
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
## todo/check: include or drop hide (of any use?) - why? why not?
|
44
|
+
headers = ['index', 'name', 'type', 'hide']
|
45
|
+
recs = []
|
46
|
+
traitDetails.each do |t|
|
47
|
+
recs << [ t[0].join('/'),
|
48
|
+
t[1][0],
|
49
|
+
t[1][1],
|
50
|
+
t[1][2].to_s]
|
51
|
+
end
|
52
|
+
|
53
|
+
buf = String.new('')
|
54
|
+
buf << headers.join( ', ' )
|
55
|
+
buf << "\n"
|
56
|
+
recs.each do |rec|
|
57
|
+
buf << rec.join( ', ' )
|
58
|
+
buf << "\n"
|
59
|
+
end
|
60
|
+
|
61
|
+
write_text( "#{outdir}/cache/layers.csv", buf )
|
62
|
+
|
63
|
+
#####
|
64
|
+
# try to download all images
|
65
|
+
traitDetails.each_with_index do |t,i|
|
66
|
+
puts " [#{i+1}/#{traitDetails.size}] downloading #{t[1][1]} >#{t[1][0]}<..."
|
67
|
+
|
68
|
+
n,m = t[0]
|
69
|
+
data = c.traitData( n, m )
|
70
|
+
|
71
|
+
basename = "#{n}_#{m}"
|
72
|
+
if data.start_with?( PNGSIG )
|
73
|
+
puts "BINGO!! it's a png blob - #{data.size} byte(s)"
|
74
|
+
write_blob( "#{outdir}/cache/#{basename}.png", data )
|
75
|
+
elsif data.start_with?( GIF87SIG ) || data.start_with?( GIF89SIG )
|
76
|
+
puts "BINGO!! it's a gif blob - #{data.size} byte(s)"
|
77
|
+
write_blob( "#{outdir}/cache/#{basename}.gif", data )
|
78
|
+
elsif data.start_with?( JPGSIG )
|
79
|
+
puts "BINGO!! it's a jpg blob - #{data.size} byte(s)"
|
80
|
+
write_blob( "#{outdir}/cache/#{basename}.jpg", data )
|
81
|
+
elsif data.index( /<svg[^>]*?>/i ) ## add more markers to find - why? why not?
|
82
|
+
puts "BINGO!! it's a svg (text) blob - #{data.size} byte(s)"
|
83
|
+
## todo/check - save text as binary blob 1:1 - why? why not?
|
84
|
+
write_blob( "#{outdir}/cache/#{basename}.svg", data )
|
85
|
+
else
|
86
|
+
puts "!! ERROR - unknown image format; sorry"
|
87
|
+
exit 1
|
88
|
+
end
|
89
|
+
sleep( 0.5 )
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
puts " name: >#{name}<"
|
94
|
+
puts " symbol: >#{symbol}<"
|
95
|
+
puts " totalSupply: >#{totalSupply}<"
|
96
|
+
puts
|
97
|
+
puts " traitDetails:"
|
98
|
+
pp traitDetails
|
99
|
+
end
|
100
|
+
end # module ArtQ
|
data/lib/artq/tokens.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
|
2
|
+
module ArtQ
|
3
|
+
|
4
|
+
|
5
|
+
|
6
|
+
class Meta ## check: change/rename to MetaToken or such - why? why not?
|
7
|
+
def self.parse( tokenURI )
|
8
|
+
if tokenURI.start_with?( 'data:application/json;base64' )
|
9
|
+
|
10
|
+
str = tokenURI.sub( 'data:application/json;base64', '' )
|
11
|
+
str = Base64.decode64( str )
|
12
|
+
data = JSON.parse( str )
|
13
|
+
|
14
|
+
## check for image_data - and replace if base64 encoded
|
15
|
+
image_data = data['image_data']
|
16
|
+
svg_image_data = data['svg_image_data']
|
17
|
+
|
18
|
+
if svg_image_data && svg_image_data.start_with?( 'data:image/svg+xml;base64' )
|
19
|
+
data['svg_image_data'] = '...'
|
20
|
+
data['image_data'] = '...' if image_data
|
21
|
+
## note: prefer svg_image_data if present over image_data - why? why not?
|
22
|
+
str = svg_image_data.sub( 'data:image/svg+xml;base64', '' )
|
23
|
+
image_data = Base64.decode64( str )
|
24
|
+
elsif image_data && image_data.start_with?( 'data:image/svg+xml;base64' )
|
25
|
+
data['image_data'] = '...'
|
26
|
+
str = image_data.sub( 'data:image/svg+xml;base64', '' )
|
27
|
+
image_data = Base64.decode64( str )
|
28
|
+
else
|
29
|
+
## assume no inline image_data ??
|
30
|
+
end
|
31
|
+
|
32
|
+
new( data, image_data )
|
33
|
+
else
|
34
|
+
new ## use new({},nil) - why? why not?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize( data={},
|
39
|
+
image_data=nil )
|
40
|
+
@data = data
|
41
|
+
@image_data = image_data
|
42
|
+
end
|
43
|
+
|
44
|
+
def data() @data; end
|
45
|
+
def image_data() @image_data; end
|
46
|
+
end # class Meta
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
## download on-blockchain token metadata and (inline) images
|
51
|
+
def self.download_tokens( contract_address,
|
52
|
+
outdir: "./#{contract_address}",
|
53
|
+
ids: (0..99) )
|
54
|
+
|
55
|
+
puts "==> download token (on-blockchain) metadata and (inline) images for art collection contract @ >#{contract_address}<:"
|
56
|
+
|
57
|
+
c = Contract.new( contract_address )
|
58
|
+
|
59
|
+
## get some metadata - why? why not?
|
60
|
+
name = c.name
|
61
|
+
symbol = c.symbol
|
62
|
+
totalSupply = c.totalSupply
|
63
|
+
|
64
|
+
|
65
|
+
warns = [] ## collect all warnings
|
66
|
+
|
67
|
+
tokenIds = ids
|
68
|
+
tokenIds.each do |tokenId|
|
69
|
+
tokenURI = c.tokenURI( tokenId )
|
70
|
+
sleep( 0.5 )
|
71
|
+
|
72
|
+
|
73
|
+
puts " tokenId #{tokenId}:"
|
74
|
+
meta = Meta.parse( tokenURI )
|
75
|
+
if meta.data.empty?
|
76
|
+
## todo/check: raise TypeError or such or return nil - why? why not?
|
77
|
+
warns << "token no. #{tokenId} metadata not 'on-blockchain'? expected json base64-encoded; got:"
|
78
|
+
puts "!! WARN - " + warns[-1]
|
79
|
+
pp tokenURI
|
80
|
+
else
|
81
|
+
path = "#{outdir}/token/#{tokenId}.json"
|
82
|
+
write_json( path, meta.data )
|
83
|
+
|
84
|
+
if meta.image_data
|
85
|
+
## assume svg for now - always - why? why not?
|
86
|
+
path = "#{outdir}/token-i/#{tokenId}.svg"
|
87
|
+
write_text( path, meta.image_data )
|
88
|
+
else
|
89
|
+
warns << "token no. #{tokenId} (inline) image data not found in 'on-blockchain' metadata; got:"
|
90
|
+
puts "!! WARN - " + warns[-1]
|
91
|
+
pp meta.data
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
if warns.size > 0
|
97
|
+
puts "!!! #{warns.size} WARNING(S):"
|
98
|
+
pp warns
|
99
|
+
end
|
100
|
+
|
101
|
+
puts
|
102
|
+
puts " name: >#{name}<"
|
103
|
+
puts " symbol: >#{symbol}<"
|
104
|
+
puts " totalSupply: >#{totalSupply}<"
|
105
|
+
end
|
106
|
+
|
107
|
+
end # module ArtQ
|
data/lib/artq/version.rb
CHANGED
data/lib/artq.rb
CHANGED
@@ -5,16 +5,25 @@ require 'optparse'
|
|
5
5
|
## our own code
|
6
6
|
require_relative 'artq/version' # let version go first
|
7
7
|
require_relative 'artq/contract'
|
8
|
+
require_relative 'artq/layers'
|
9
|
+
require_relative 'artq/tokens'
|
8
10
|
|
9
11
|
|
10
12
|
|
11
13
|
module ArtQ
|
12
14
|
|
15
|
+
class Tool
|
13
16
|
|
14
17
|
|
18
|
+
def self.is_addr?( str )
|
19
|
+
## e.g.
|
20
|
+
## must for now start with 0x (or 0X)
|
21
|
+
## and than 40 hexdigits (20 bytes)
|
22
|
+
## e.g. 0xe21ebcd28d37a67757b9bc7b290f4c4928a430b1
|
23
|
+
str.match( /\A0x[0-9a-f]{40}\z/i )
|
24
|
+
end
|
15
25
|
|
16
26
|
|
17
|
-
class Tool
|
18
27
|
|
19
28
|
def self.main( args=ARGV )
|
20
29
|
puts "==> welcome to artq tool with args:"
|
@@ -49,14 +58,38 @@ class Tool
|
|
49
58
|
exit
|
50
59
|
end
|
51
60
|
|
52
|
-
|
61
|
+
|
62
|
+
## todo/check - use collection_name/slug or such?
|
63
|
+
contract_address = nil
|
64
|
+
outdir = nil
|
65
|
+
|
66
|
+
if is_addr?( args[0] ) ## do nothing; it's an address
|
67
|
+
contract_address = args[0]
|
68
|
+
outdir = "./tmp/#{contract_address}"
|
69
|
+
else ## try reading collection.yml config
|
70
|
+
config_path = "./#{args[0]}/collection.yml"
|
71
|
+
if File.exist?( config_path )
|
72
|
+
config = read_yaml( config_path )
|
73
|
+
contract_address = config['token']['contract']
|
74
|
+
outdir = "./#{args[0]}"
|
75
|
+
else
|
76
|
+
puts "!! ERROR - no config found for collection >#{contract_address}<; sorry"
|
77
|
+
exit 1
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
|
53
82
|
command = args[1] || 'info'
|
54
83
|
|
55
84
|
|
56
85
|
if ['i','inf','info'].include?( command )
|
57
86
|
do_info( contract_address )
|
58
87
|
elsif ['l', 'layer', 'layers'].include?( command )
|
59
|
-
|
88
|
+
## note: outdir - save into cache for now
|
89
|
+
do_layers( contract_address, outdir: outdir )
|
90
|
+
elsif ['t', 'token', 'tokens'].include?( command )
|
91
|
+
## note: outdir - saves into token (metadata) & token-i (images)
|
92
|
+
do_tokens( contract_address, outdir: outdir )
|
60
93
|
else
|
61
94
|
puts "!! ERROR - unknown command >#{command}<, sorry"
|
62
95
|
end
|
@@ -77,11 +110,11 @@ class Tool
|
|
77
110
|
symbol = c.symbol
|
78
111
|
totalSupply = c.totalSupply
|
79
112
|
|
80
|
-
|
113
|
+
recs = []
|
81
114
|
tokenIds = (0..2)
|
82
115
|
tokenIds.each do |tokenId|
|
83
116
|
tokenURI = c.tokenURI( tokenId )
|
84
|
-
|
117
|
+
recs << [tokenId, tokenURI]
|
85
118
|
end
|
86
119
|
|
87
120
|
puts " name: >#{name}<"
|
@@ -89,122 +122,34 @@ class Tool
|
|
89
122
|
puts " totalSupply: >#{totalSupply}<"
|
90
123
|
puts
|
91
124
|
puts " tokenURIs #{tokenIds}:"
|
92
|
-
|
125
|
+
recs.each do |tokenId, tokenURI|
|
93
126
|
puts " tokenId #{tokenId}:"
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
str = Base64.decode64( str )
|
100
|
-
data = JSON.parse( str )
|
101
|
-
|
102
|
-
|
103
|
-
## check for image_data - and replace if base64 encoded
|
104
|
-
image_data = data['image_data']
|
105
|
-
|
106
|
-
if image_data.start_with?( 'data:image/svg+xml;base64' )
|
107
|
-
data['image_data'] = '...'
|
108
|
-
str = image_data.sub( 'data:image/svg+xml;base64', '' )
|
109
|
-
image_data = Base64.decode64( str )
|
110
|
-
end
|
111
|
-
|
112
|
-
pp data
|
127
|
+
meta = Meta.parse( tokenURI )
|
128
|
+
if meta.data.empty? ## assume "off-blockchain" if no "on-blockchain" data found
|
129
|
+
puts " #{tokenURI}"
|
130
|
+
else ## assume "on-blockchain" data
|
131
|
+
pp meta.data
|
113
132
|
puts
|
114
133
|
puts " image_data:"
|
115
|
-
puts image_data
|
116
|
-
else
|
117
|
-
puts " #{tokenURI}"
|
134
|
+
puts meta.image_data
|
118
135
|
end
|
119
136
|
end
|
120
137
|
end
|
121
138
|
|
122
139
|
|
123
140
|
|
124
|
-
|
125
|
-
PNGSIG = "\x89PNG".force_encoding( Encoding::ASCII_8BIT )
|
126
|
-
GIF87SIG = "GIF87".force_encoding( Encoding::ASCII_8BIT )
|
127
|
-
GIF89SIG = "GIF89".force_encoding( Encoding::ASCII_8BIT )
|
128
|
-
|
129
|
-
|
130
|
-
def self.do_layers( contract_address )
|
141
|
+
def self.do_layers( contract_address, outdir: )
|
131
142
|
puts "==> query layers for art collection contract @ >#{contract_address}<:"
|
132
143
|
|
133
|
-
|
134
|
-
|
135
|
-
name = c.name
|
136
|
-
symbol = c.symbol
|
137
|
-
totalSupply = c.totalSupply
|
138
|
-
|
139
|
-
|
140
|
-
traitDetails = []
|
141
|
-
n=0
|
142
|
-
loop do
|
143
|
-
m=0
|
144
|
-
loop do
|
145
|
-
rec = c.traitDetails( n, m )
|
146
|
-
break if rec == ['','',false]
|
147
|
-
|
148
|
-
traitDetails << [[n,m], rec ]
|
149
|
-
m += 1
|
150
|
-
sleep( 0.5 )
|
151
|
-
end
|
152
|
-
break if m==0
|
153
|
-
n += 1
|
154
|
-
end
|
155
|
-
|
156
|
-
headers = ['index', 'name', 'type', 'hide']
|
157
|
-
recs = []
|
158
|
-
traitDetails.each do |t|
|
159
|
-
recs << [ t[0].join('/'),
|
160
|
-
t[1][0],
|
161
|
-
t[1][1],
|
162
|
-
t[1][2].to_s]
|
163
|
-
end
|
164
|
-
|
165
|
-
buf = String.new('')
|
166
|
-
buf << headers.join( ', ' )
|
167
|
-
buf << "\n"
|
168
|
-
recs.each do |rec|
|
169
|
-
buf << rec.join( ', ' )
|
170
|
-
buf << "\n"
|
171
|
-
end
|
172
|
-
|
173
|
-
outdir = "./tmp/#{contract_address}"
|
174
|
-
write_text( "#{outdir}/layers.csv", buf )
|
175
|
-
|
176
|
-
#####
|
177
|
-
# try to download all images
|
178
|
-
traitDetails.each do |t|
|
179
|
-
n,m = t[0]
|
180
|
-
data = c.traitData( n, m )
|
181
|
-
|
182
|
-
basename = "#{n}_#{m}"
|
183
|
-
if data.start_with?( PNGSIG )
|
184
|
-
puts "BINGO!! it's a png blob"
|
185
|
-
write_blob( "#{outdir}/#{basename}.png", data )
|
186
|
-
elsif data.start_with?( GIF87SIG ) || data.start_with?( GIF89SIG )
|
187
|
-
puts "BINGO!! it's a gif blob"
|
188
|
-
write_blob( "#{outdir}/#{basename}.gif", data )
|
189
|
-
elsif data.start_with?( JPGSIG )
|
190
|
-
puts "BINGO!! it's a jpg blob"
|
191
|
-
write_blob( "#{outdir}/#{basename}.jpg", data )
|
192
|
-
else
|
193
|
-
puts "!! ERROR - unknown image format; sorry"
|
194
|
-
exit 1
|
195
|
-
end
|
196
|
-
sleep( 0.5 )
|
197
|
-
end
|
198
|
-
|
199
|
-
|
200
|
-
puts " name: >#{name}<"
|
201
|
-
puts " symbol: >#{symbol}<"
|
202
|
-
puts " totalSupply: >#{totalSupply}<"
|
203
|
-
puts
|
204
|
-
puts " traitDetails:"
|
205
|
-
pp traitDetails
|
144
|
+
ArtQ.download_layers( contract_address,
|
145
|
+
outdir: outdir )
|
206
146
|
end
|
207
147
|
|
148
|
+
def self.do_tokens( contract_address, outdir: )
|
149
|
+
puts "==> query inline 'on-blockchain' token metadata & images for art collection contract @ >#{contract_address}<:"
|
150
|
+
ArtQ.download_tokens( contract_address,
|
151
|
+
outdir: outdir )
|
152
|
+
end
|
208
153
|
|
209
154
|
end # class Tool
|
210
155
|
end # module ArtQ
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: artq
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gerald Bauer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-12-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ethlite
|
@@ -76,6 +76,8 @@ files:
|
|
76
76
|
- bin/artq
|
77
77
|
- lib/artq.rb
|
78
78
|
- lib/artq/contract.rb
|
79
|
+
- lib/artq/layers.rb
|
80
|
+
- lib/artq/tokens.rb
|
79
81
|
- lib/artq/version.rb
|
80
82
|
homepage: https://github.com/pixelartexchange/artbase
|
81
83
|
licenses:
|