artq 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c0f00a597b6533cbdce4aa3286531d8533f9d3c0cf57b8147b7160d4b1d206d3
4
- data.tar.gz: e192cfb230111adb5dd69a1213a1446a9dc629fc853cacb8b10e5d80ac723e8e
3
+ metadata.gz: d77ed6ab0733f84e19ecd4e1329f8edeea8c083dc773b0dd756182996fc81355
4
+ data.tar.gz: dc91009707f3e4b70e5682a5c62cd310b8e20ffc47d730ecf3ce29fda65d4e17
5
5
  SHA512:
6
- metadata.gz: 6bf4be0a1990ce7c1d6d6e2c30b936cc0a145401b2992b32e4f2c83153881746fdaab77de434a38409bc17c6c0cbb93ee4673a589767197b314b11b935078426
7
- data.tar.gz: 65db2f2e2d112926111cb05398dd992a28d64f793a6f5d955b01a4cf03f1f25d9334d998b16088302479a07e6a3191712bc41d932c1b87d821f5d68b3e7b9974
6
+ metadata.gz: de74d73e5080ce66ca49444e857478b20b9f2705eb918375302a38b0a04f0bb56eab98d0b211ced60263fb4415699efa244770a3b0dc7c336bf9046258d3c9e1
7
+ data.tar.gz: 6703ec538be7d9215b7b62a0b23d75297386e5065b2232982043a59d87346021073fae6f3f966fde77dd678b5ce267e80b7113086f7619afab1439d9660abb3b
data/Manifest.txt CHANGED
@@ -5,4 +5,6 @@ Rakefile
5
5
  bin/artq
6
6
  lib/artq.rb
7
7
  lib/artq/contract.rb
8
+ lib/artq/layers.rb
9
+ lib/artq/tokens.rb
8
10
  lib/artq/version.rb
data/README.md CHANGED
@@ -339,8 +339,7 @@ such as
339
339
  and many more.
340
340
 
341
341
 
342
- Tip: See the [**Art Factory Sandbox**](https://github.com/pixelartexchange/artfactory.sandbox) for more art collections with "on-blockchain" layers.
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
- def self.rpc
54
- @rpc ||= JsonRpc.new( ENV['INFURA_URI'] )
55
- end
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
 
@@ -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
@@ -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
@@ -1,7 +1,7 @@
1
1
 
2
2
 
3
3
  module ArtQ
4
- VERSION = '0.2.0'
4
+ VERSION = '0.3.0'
5
5
  end # module ArtQ
6
6
 
7
7
 
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
- meta = []
80
+ recs = []
81
81
  tokenIds = (0..2)
82
82
  tokenIds.each do |tokenId|
83
83
  tokenURI = c.tokenURI( tokenId )
84
- meta << [tokenId, tokenURI]
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
- meta.each do |tokenId, tokenURI|
92
+ recs.each do |tokenId, tokenURI|
93
93
  puts " tokenId #{tokenId}:"
94
- if tokenURI.start_with?( 'data:application/json;base64')
95
- ## on-blockchain!!!
96
- ## decode base64
97
-
98
- str = tokenURI.sub( 'data:application/json;base64', '' )
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
- c = Contract.new( contract_address )
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.2.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-23 00:00:00.000000000 Z
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: