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 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: