artq 0.2.0 → 0.3.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/Manifest.txt +2 -0
- data/README.md +1 -2
- data/lib/artq/contract.rb +6 -6
- data/lib/artq/layers.rb +96 -0
- data/lib/artq/tokens.rb +107 -0
- data/lib/artq/version.rb +1 -1
- data/lib/artq.rb +20 -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: d77ed6ab0733f84e19ecd4e1329f8edeea8c083dc773b0dd756182996fc81355
|
4
|
+
data.tar.gz: dc91009707f3e4b70e5682a5c62cd310b8e20ffc47d730ecf3ce29fda65d4e17
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: de74d73e5080ce66ca49444e857478b20b9f2705eb918375302a38b0a04f0bb56eab98d0b211ced60263fb4415699efa244770a3b0dc7c336bf9046258d3c9e1
|
7
|
+
data.tar.gz: 6703ec538be7d9215b7b62a0b23d75297386e5065b2232982043a59d87346021073fae6f3f966fde77dd678b5ce267e80b7113086f7619afab1439d9660abb3b
|
data/Manifest.txt
CHANGED
data/README.md
CHANGED
@@ -339,8 +339,7 @@ such as
|
|
339
339
|
and many more.
|
340
340
|
|
341
341
|
|
342
|
-
Tip:
|
343
|
-
|
342
|
+
Tip: For more art collections with "on-blockchain" layers see the [**Art Factory Sandbox »**](https://github.com/pixelartexchange/artfactory.sandbox)
|
344
343
|
|
345
344
|
|
346
345
|
|
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,96 @@
|
|
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}/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}/#{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}/#{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}/#{basename}.jpg", data )
|
81
|
+
else
|
82
|
+
puts "!! ERROR - unknown image format; sorry"
|
83
|
+
exit 1
|
84
|
+
end
|
85
|
+
sleep( 0.5 )
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
puts " name: >#{name}<"
|
90
|
+
puts " symbol: >#{symbol}<"
|
91
|
+
puts " totalSupply: >#{totalSupply}<"
|
92
|
+
puts
|
93
|
+
puts " traitDetails:"
|
94
|
+
pp traitDetails
|
95
|
+
end
|
96
|
+
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,15 +5,13 @@ 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
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
15
|
class Tool
|
18
16
|
|
19
17
|
def self.main( args=ARGV )
|
@@ -57,6 +55,8 @@ class Tool
|
|
57
55
|
do_info( contract_address )
|
58
56
|
elsif ['l', 'layer', 'layers'].include?( command )
|
59
57
|
do_layers( contract_address )
|
58
|
+
elsif ['t', 'token', 'tokens'].include?( command )
|
59
|
+
do_tokens( contract_address )
|
60
60
|
else
|
61
61
|
puts "!! ERROR - unknown command >#{command}<, sorry"
|
62
62
|
end
|
@@ -77,11 +77,11 @@ class Tool
|
|
77
77
|
symbol = c.symbol
|
78
78
|
totalSupply = c.totalSupply
|
79
79
|
|
80
|
-
|
80
|
+
recs = []
|
81
81
|
tokenIds = (0..2)
|
82
82
|
tokenIds.each do |tokenId|
|
83
83
|
tokenURI = c.tokenURI( tokenId )
|
84
|
-
|
84
|
+
recs << [tokenId, tokenURI]
|
85
85
|
end
|
86
86
|
|
87
87
|
puts " name: >#{name}<"
|
@@ -89,122 +89,34 @@ class Tool
|
|
89
89
|
puts " totalSupply: >#{totalSupply}<"
|
90
90
|
puts
|
91
91
|
puts " tokenURIs #{tokenIds}:"
|
92
|
-
|
92
|
+
recs.each do |tokenId, tokenURI|
|
93
93
|
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
|
94
|
+
meta = Meta.parse( tokenURI )
|
95
|
+
if meta.data.empty? ## assume "off-blockchain" if no "on-blockchain" data found
|
96
|
+
puts " #{tokenURI}"
|
97
|
+
else ## assume "on-blockchain" data
|
98
|
+
pp meta.data
|
113
99
|
puts
|
114
100
|
puts " image_data:"
|
115
|
-
puts image_data
|
116
|
-
else
|
117
|
-
puts " #{tokenURI}"
|
101
|
+
puts meta.image_data
|
118
102
|
end
|
119
103
|
end
|
120
104
|
end
|
121
105
|
|
122
106
|
|
123
107
|
|
124
|
-
JPGSIG = "\xFF\xD8\xFF".force_encoding( Encoding::ASCII_8BIT )
|
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
108
|
def self.do_layers( contract_address )
|
131
109
|
puts "==> query layers for art collection contract @ >#{contract_address}<:"
|
132
110
|
|
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
|
111
|
+
ArtQ.download_layers( contract_address,
|
112
|
+
outdir: "./tmp/#{contract_address}" )
|
206
113
|
end
|
207
114
|
|
115
|
+
def self.do_tokens( contract_address )
|
116
|
+
puts "==> query inline 'on-blockchain' token metadata & images for art collection contract @ >#{contract_address}<:"
|
117
|
+
ArtQ.download_tokens( contract_address,
|
118
|
+
outdir: "./tmp/#{contract_address}" )
|
119
|
+
end
|
208
120
|
|
209
121
|
end # class Tool
|
210
122
|
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.0
|
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-
|
11
|
+
date: 2022-11-24 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:
|