artq 0.1.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: 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: