artq 0.1.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: c9779a590a436cc490adf3f14a21c2a663c349a56cc2d3546fa3d7ca490ed157
4
- data.tar.gz: 8c54bc4cdbc6e1626f13f5d445b39a0fa6d029f9330aefa030f37d7f8fa83558
3
+ metadata.gz: d77ed6ab0733f84e19ecd4e1329f8edeea8c083dc773b0dd756182996fc81355
4
+ data.tar.gz: dc91009707f3e4b70e5682a5c62cd310b8e20ffc47d730ecf3ce29fda65d4e17
5
5
  SHA512:
6
- metadata.gz: c75af5b52d7b37b752f9e7de03b5e3d7f84f8ead09cc3f537c1897c2468990a812b2de9b6fd2a44c1c54237bf6cc4120e63ea0b31b1c4bd907db58c56412ef82
7
- data.tar.gz: d37f4a40864b66dccf69b629f07572f10abb38036047bd35e04947499f7de767800f87f8fe852414b9201835782589a8e6d237a60748d36d22d558140b2f763c
6
+ metadata.gz: de74d73e5080ce66ca49444e857478b20b9f2705eb918375302a38b0a04f0bb56eab98d0b211ced60263fb4415699efa244770a3b0dc7c336bf9046258d3c9e1
7
+ data.tar.gz: 6703ec538be7d9215b7b62a0b23d75297386e5065b2232982043a59d87346021073fae6f3f966fde77dd678b5ce267e80b7113086f7619afab1439d9660abb3b
data/Manifest.txt CHANGED
@@ -4,4 +4,7 @@ README.md
4
4
  Rakefile
5
5
  bin/artq
6
6
  lib/artq.rb
7
+ lib/artq/contract.rb
8
+ lib/artq/layers.rb
9
+ lib/artq/tokens.rb
7
10
  lib/artq/version.rb
data/README.md CHANGED
@@ -31,13 +31,14 @@ set INFURA_URI=https://mainnet.infura.io/v3/<YOUR_KEY_HERE>
31
31
  To use the artq command line tool pass in the art collection contract address in the hex (string) format.
32
32
 
33
33
 
34
- #### "Off-Blockchain" Token Metadata - Case No. 1
34
+ #### Case No. 1 - "Off-Blockchain" Token Metadata
35
35
 
36
- Let's try Moonbirds - an ("off-blockchain") pixel art collection -
36
+ Let's try Moonbirds - an "off-blockchain" pixel art collection -
37
37
  with the token contract / service at [0x23581767a106ae21c074b2276d25e5c3e136a68b](https://etherscan.io/address/0x23581767a106ae21c074b2276d25e5c3e136a68b):
38
38
 
39
39
  ```
40
- $ artq 0x23581767a106ae21c074b2276d25e5c3e136a68b
40
+ $ artq 0x23581767a106ae21c074b2276d25e5c3e136a68b # or
41
+ $ artq 0x23581767a106ae21c074b2276d25e5c3e136a68b info
41
42
  ```
42
43
 
43
44
  resulting in:
@@ -48,11 +49,11 @@ symbol: >MOONBIRD<
48
49
  totalSupply: >10000<
49
50
 
50
51
  tokenURIs 0..2:
51
- tokenId: 0
52
+ tokenId 0:
52
53
  https://live---metadata-5covpqijaa-uc.a.run.app/metadata/0
53
- tokenId: 1
54
+ tokenId 1:
54
55
  https://live---metadata-5covpqijaa-uc.a.run.app/metadata/1
55
- tokenId: 2
56
+ tokenId 2:
56
57
  https://live---metadata-5covpqijaa-uc.a.run.app/metadata/2
57
58
  ```
58
59
 
@@ -83,15 +84,16 @@ For example if you request <https://live---metadata-5covpqijaa-uc.a.run.app/met
83
84
 
84
85
 
85
86
 
86
- #### "On-Blockchain" Token Metadata (With Inline Image) - Case No. 2
87
+ #### Case No. 2 - "On-Blockchain" Token Metadata (With Inline Image)
87
88
 
88
89
 
89
90
 
90
- Let's try The Saudis - an ("on-blockchain") pixel art collection -
91
+ Let's try The Saudis - an "on-blockchain" pixel art collection -
91
92
  with the token contract / service at [0xe21ebcd28d37a67757b9bc7b290f4c4928a430b1](https://etherscan.io/address/0xe21ebcd28d37a67757b9bc7b290f4c4928a430b1):
92
93
 
93
94
  ```
94
- $ artq 0xe21ebcd28d37a67757b9bc7b290f4c4928a430b1
95
+ $ artq 0xe21ebcd28d37a67757b9bc7b290f4c4928a430b1 # or
96
+ $ artq 0xe21ebcd28d37a67757b9bc7b290f4c4928a430b1 info
95
97
  ```
96
98
 
97
99
  resulting in:
@@ -182,6 +184,163 @@ and inline svg images in the base64 format get "cut" from the metadata and "past
182
184
  ```
183
185
 
184
186
 
187
+ #### Bonus - Case No. 3 - "On-Blockchain" Layers (Incl. Metadata & Images)
188
+
189
+
190
+ Note: Some "on-blockchain" pixel art collections
191
+ include all layers, that is, metadata and images
192
+ to compose / make-up "on-blockchain" token images "on-demand / on-the-fly" from "trait" building blocks from scratch.
193
+
194
+
195
+ If the contract uses / supports:
196
+
197
+ - `traitDetails(uint256 _layerIndex, uint256 _traitIndex) returns (string _name, string _mimetype, bool _hide)` and
198
+ - `traitData(uint256 _layerIndex, uint256 _traitIndex) returns string`
199
+
200
+ than you can "auto-magically" download all "on-blockchain" layers, that is, all metadata triplets by repeatedly calling `traitDetails` starting
201
+ with index `0/0`, `0/1`, ..., `1/0`, `1/1`, ... and so on e.g.
202
+
203
+ - `traitDetails( 0, 0 )` => `["Rainbow Puke", "image/png", false]`
204
+ - `traitDetails( 0, 1 )` => `["Bubble Gum", "image/png", false]`
205
+ - ...
206
+ - `traitDetails( 1, 0 )` => `["Gold Chain", "image/png", false]`
207
+ - `traitDetails( 2, 1 )` => `["Bowtie", "image/png", false]`
208
+ - ...
209
+
210
+
211
+ and all images (as binary blobs) by calling `traitData` e.g.
212
+
213
+ - `traitData( 0, 0 )` => `"\x89PNG..."`
214
+ - `traitData( 0, 1 )` => `"\x89PNG..."`
215
+ - ...
216
+
217
+ and so on.
218
+
219
+
220
+
221
+ Let's try Mad Camels - an "on-blockchain" pixel art collection -
222
+ with the token contract / service at [0xad8474ba5a7f6abc52708f171f57fefc5cdc8c1c](https://etherscan.io/address/0xad8474ba5a7f6abc52708f171f57fefc5cdc8c1c):
223
+
224
+
225
+ ```
226
+ $ artq 0xad8474ba5a7f6abc52708f171f57fefc5cdc8c1c layers
227
+ ```
228
+
229
+ resulting in a temp(orary) directory holding
230
+ all images:
231
+
232
+ ```
233
+ /0_0.png
234
+ 0_1.png
235
+ ...
236
+ 1_0.png
237
+ 1_1.png
238
+ ...
239
+ ```
240
+
241
+ ![](i/madcamels-32x32.png)
242
+
243
+
244
+ and a datafile with all metadata in the comma-separated values (csv) format, that is, `layers.csv` e.g:
245
+
246
+ ```
247
+ index, name, type, hide
248
+ 0/0, Rainbow Puke, image/png, false
249
+ 0/1, Bubble Gum, image/png, false
250
+ 0/2, Vape, image/png, false
251
+ 0/3, None, image/png, false
252
+ 0/4, Cigarette, image/png, false
253
+ 0/5, Pipe, image/png, false
254
+ 1/0, Gold Chain, image/png, false
255
+ 1/1, Bowtie, image/png, false
256
+ 1/2, Gold Necklace, image/png, false
257
+ 1/3, None, image/png, false
258
+ 2/0, Eye Patch, image/png, false
259
+ 2/1, Nerd Glasses, image/png, false
260
+ 2/2, Blue Beams, image/png, false
261
+ 2/3, Purple Eye Shadow, image/png, false
262
+ 2/4, Gold Glasses, image/png, false
263
+ 2/5, Holographic, image/png, false
264
+ 2/6, Clown Eyes Red, image/png, false
265
+ 2/7, Clown Eyes Green, image/png, false
266
+ 2/8, Eye Mask, image/png, false
267
+ 2/9, Laser Eye, image/png, false
268
+ 2/10, VR, image/png, false
269
+ 2/11, 3D Glasses, image/png, false
270
+ 2/12, None, image/png, false
271
+ 2/13, Yellow Glasses, image/png, false
272
+ 2/14, Cool Glasses, image/png, false
273
+ 2/15, Purple Glasses, image/png, false
274
+ 2/16, Green Glasses, image/png, false
275
+ 3/0, Diamond, image/png, false
276
+ 3/1, Silver, image/png, false
277
+ 3/2, Gold, image/png, false
278
+ 3/3, None, image/png, false
279
+ 4/0, Crown, image/png, false
280
+ 4/1, Wireless Earphone, image/png, false
281
+ 4/2, Flower, image/png, false
282
+ 4/3, Fez, image/png, false
283
+ 4/4, Fire, image/png, false
284
+ 4/5, Beanie, image/png, false
285
+ 4/6, Headphone, image/png, false
286
+ 4/7, White Shemagh, image/png, false
287
+ 4/8, Red And White Shemagh, image/png, false
288
+ 4/9, Angle Ring, image/png, false
289
+ 4/10, Blue Mohawk, image/png, false
290
+ 4/11, Sombrero, image/png, false
291
+ 4/12, Red Mohawk, image/png, false
292
+ 4/13, Blue Bandana, image/png, false
293
+ 4/14, Viking, image/png, false
294
+ 4/15, Pilot Helmet, image/png, false
295
+ 4/16, Top Hat, image/png, false
296
+ 4/17, Captain Hat, image/png, false
297
+ 4/18, Thief Hat, image/png, false
298
+ 4/19, Orange Cap, image/png, false
299
+ 4/20, Pirate Bandana, image/png, false
300
+ 4/21, Knitted Cap, image/png, false
301
+ 4/22, Purple Cap, image/png, false
302
+ 4/23, Black Cap, image/png, false
303
+ 4/24, Pirate Hat, image/png, false
304
+ 4/25, None, image/png, false
305
+ 4/26, Red Cap, image/png, false
306
+ 4/27, Cop Hat, image/png, false
307
+ 4/28, Cowboy Hat, image/png, false
308
+ 4/29, Fedora, image/png, false
309
+ 5/0, Mole, image/png, false
310
+ 5/1, Pimple, image/png, false
311
+ 5/2, None, image/png, false
312
+ 6/0, Gold, image/png, false
313
+ 6/1, Cyborg, image/png, false
314
+ 6/2, Skeleton, image/png, false
315
+ 6/3, Female, image/png, false
316
+ 6/4, Robot, image/png, false
317
+ 6/5, Zombie, image/png, false
318
+ 6/6, Alien, image/png, false
319
+ 6/7, Default, image/png, false
320
+ 7/0, Desert, image/png, false
321
+ 7/1, Cream, image/png, false
322
+ 7/2, Pink, image/png, false
323
+ 7/3, Purple, image/png, false
324
+ 7/4, Green, image/png, false
325
+ 7/5, Blue, image/png, false
326
+ ```
327
+
328
+
329
+ Try some more art collections with "on-blockchain" layers
330
+ such as
331
+ [Long Live Kevin](https://etherscan.io/address/0x8ae5523f76a5711fb6bdca1566df3f4707aec1c4),
332
+ [Aliens vs Punks](https://etherscan.io/address/0x2612c0375c47ee510a1663169288f2e9eb912947),
333
+ [Chi Chis](https://etherscan.io/address/0x2204a94f96d39df3b6bc0298cf068c8c82dc8d61),
334
+ [Chopper](https://etherscan.io/address/0x090c8034e6706994945049e0ede1bbdf21498e6e),
335
+ [Inverse Punks](https://etherscan.io/address/0xf3a1befc9643f94551c24a766afb87383ef64dd4),
336
+ [Marcs](https://etherscan.io/address/0xe9b91d537c3aa5a3fa87275fbd2e4feaaed69bd0),
337
+ [Phunk Ape Origins](https://etherscan.io/address/0x9b66d03fc1eee61a512341058e95f1a68dc3a913),
338
+ [Punkin Spicies](https://etherscan.io/address/0x34625ecaa75c0ea33733a05c584f4cf112c10b6b),
339
+ and many more.
340
+
341
+
342
+ Tip: For more art collections with "on-blockchain" layers see the [**Art Factory Sandbox »**](https://github.com/pixelartexchange/artfactory.sandbox)
343
+
185
344
 
186
345
 
187
346
  ## License
@@ -0,0 +1,73 @@
1
+
2
+ module ArtQ
3
+
4
+
5
+ class Contract
6
+
7
+ ## auto-add "well-known" methods for contract methods - why? why not?
8
+ METHODS = {
9
+ name: { inputs: [],
10
+ outputs: ['string'] },
11
+ symbol: { inputs: [],
12
+ outputs: ['string'] },
13
+ totalSupply: { inputs: [],
14
+ outputs: ['uint256'] },
15
+ tokenURI: { inputs: ['uint256'],
16
+ outputs: ['string'] },
17
+
18
+ traitData: { inputs: ['uint256', 'uint256'],
19
+ outputs: ['string'] },
20
+ traitDetails: { inputs: ['uint256', 'uint256'],
21
+ outputs: ['(string,string,bool)'] },
22
+ }
23
+
24
+
25
+ METHODS.each do |name, m|
26
+ eth = Ethlite::ContractMethod.new( name.to_s,
27
+ inputs: m[:inputs],
28
+ outputs: m[:outputs] )
29
+
30
+ arity = m[:inputs].size
31
+ if arity == 0
32
+ define_method( name ) do
33
+ args = []
34
+ _do_call( eth, args )
35
+ end
36
+ elsif arity == 1
37
+ define_method( name ) do |arg0|
38
+ args = [arg0]
39
+ _do_call( eth, args )
40
+ end
41
+ elsif arity == 2
42
+ define_method( name ) do |arg0,arg1|
43
+ args = [arg0, arg1]
44
+ _do_call( eth, args )
45
+ end
46
+ else
47
+ raise ArgumentError, "unsupported no. of arguments #{m[:inputs]} (arity); sorry"
48
+ end
49
+ end
50
+
51
+
52
+
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
+
58
+
59
+
60
+
61
+ def initialize( contract_address )
62
+ @contract_address = contract_address
63
+ end
64
+
65
+ ######
66
+ # private helper to call method
67
+ def _do_call( eth, args )
68
+ puts " calling method >#{eth.name}< with args >#{args}<... "
69
+ eth.do_call( self.class.rpc, @contract_address, args )
70
+ end
71
+
72
+ end # class Contract
73
+ end # module ArtQ
@@ -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.1.0'
4
+ VERSION = '0.3.0'
5
5
  end # module ArtQ
6
6
 
7
7
 
data/lib/artq.rb CHANGED
@@ -4,6 +4,9 @@ require 'optparse'
4
4
 
5
5
  ## our own code
6
6
  require_relative 'artq/version' # let version go first
7
+ require_relative 'artq/contract'
8
+ require_relative 'artq/layers'
9
+ require_relative 'artq/tokens'
7
10
 
8
11
 
9
12
 
@@ -50,6 +53,10 @@ class Tool
50
53
 
51
54
  if ['i','inf','info'].include?( command )
52
55
  do_info( contract_address )
56
+ elsif ['l', 'layer', 'layers'].include?( command )
57
+ do_layers( contract_address )
58
+ elsif ['t', 'token', 'tokens'].include?( command )
59
+ do_tokens( contract_address )
53
60
  else
54
61
  puts "!! ERROR - unknown command >#{command}<, sorry"
55
62
  end
@@ -58,38 +65,23 @@ class Tool
58
65
  end
59
66
 
60
67
 
61
- ETH_name = Ethlite::ContractMethod.new( 'name',
62
- inputs: [],
63
- outputs: ['string'] )
64
-
65
- ETH_symbol = Ethlite::ContractMethod.new( 'symbol',
66
- inputs: [],
67
- outputs: ['string'] )
68
-
69
- ETH_totalSupply = Ethlite::ContractMethod.new( 'totalSupply',
70
- inputs: [],
71
- outputs: ['uint256'] )
72
-
73
- ETH_tokenURI = Ethlite::ContractMethod.new( 'tokenURI',
74
- inputs: ['uint256'],
75
- outputs: ['string'] )
76
68
 
77
69
 
78
70
 
79
71
  def self.do_info( contract_address )
80
- puts "==> query art collection contract info @ >#{contract_address}<:"
72
+ puts "==> query info for art collection contract @ >#{contract_address}<:"
81
73
 
82
- rpc = JsonRpc.new( ENV['INFURA_URI'] )
74
+ c = Contract.new( contract_address )
83
75
 
84
- name = ETH_name.do_call( rpc, contract_address, [] )
85
- symbol = ETH_symbol.do_call( rpc, contract_address, [] )
86
- totalSupply = ETH_totalSupply.do_call( rpc, contract_address, [] )
76
+ name = c.name
77
+ symbol = c.symbol
78
+ totalSupply = c.totalSupply
87
79
 
88
- meta = []
80
+ recs = []
89
81
  tokenIds = (0..2)
90
82
  tokenIds.each do |tokenId|
91
- tokenURI = ETH_tokenURI.do_call( rpc, contract_address, [tokenId] )
92
- meta << [tokenId, tokenURI]
83
+ tokenURI = c.tokenURI( tokenId )
84
+ recs << [tokenId, tokenURI]
93
85
  end
94
86
 
95
87
  puts " name: >#{name}<"
@@ -97,38 +89,36 @@ class Tool
97
89
  puts " totalSupply: >#{totalSupply}<"
98
90
  puts
99
91
  puts " tokenURIs #{tokenIds}:"
100
- meta.each do |tokenId, tokenURI|
92
+ recs.each do |tokenId, tokenURI|
101
93
  puts " tokenId #{tokenId}:"
102
- if tokenURI.start_with?( 'data:application/json;base64')
103
- ## on-blockchain!!!
104
- ## decode base64
105
-
106
- str = tokenURI.sub( 'data:application/json;base64', '' )
107
- str = Base64.decode64( str )
108
- data = JSON.parse( str )
109
-
110
-
111
- ## check for image_data - and replace if base64 encoded
112
- image_data = data['image_data']
113
-
114
- if image_data.start_with?( 'data:image/svg+xml;base64' )
115
- data['image_data'] = '...'
116
- str = image_data.sub( 'data:image/svg+xml;base64', '' )
117
- image_data = Base64.decode64( str )
118
- end
119
-
120
- 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
121
99
  puts
122
100
  puts " image_data:"
123
- puts image_data
124
- else
125
- puts " #{tokenURI}"
101
+ puts meta.image_data
126
102
  end
127
103
  end
128
104
  end
129
- end # class Tool
130
105
 
131
106
 
107
+
108
+ def self.do_layers( contract_address )
109
+ puts "==> query layers for art collection contract @ >#{contract_address}<:"
110
+
111
+ ArtQ.download_layers( contract_address,
112
+ outdir: "./tmp/#{contract_address}" )
113
+ end
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
120
+
121
+ end # class Tool
132
122
  end # module ArtQ
133
123
 
134
124
 
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.1.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-22 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
@@ -75,6 +75,9 @@ files:
75
75
  - Rakefile
76
76
  - bin/artq
77
77
  - lib/artq.rb
78
+ - lib/artq/contract.rb
79
+ - lib/artq/layers.rb
80
+ - lib/artq/tokens.rb
78
81
  - lib/artq/version.rb
79
82
  homepage: https://github.com/pixelartexchange/artbase
80
83
  licenses: