artq 0.0.1 → 0.2.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: e392049f9000bfe8a6b16ee2cad088a5fc7d2d4d76e0be088522ee6839e00822
4
- data.tar.gz: '0904cfde265b254156d8a52cafbd6999a016d75e5ccad53a5bb5c1312efaae85'
3
+ metadata.gz: c0f00a597b6533cbdce4aa3286531d8533f9d3c0cf57b8147b7160d4b1d206d3
4
+ data.tar.gz: e192cfb230111adb5dd69a1213a1446a9dc629fc853cacb8b10e5d80ac723e8e
5
5
  SHA512:
6
- metadata.gz: dc71ccf9a977d4764218f3681ee72b3a34c3cd00638768c905c9037c77922fa4c3ad8dd154e195565b3d8d9b4e079b4b36cdda340036eb4d58f7456e668bfaa2
7
- data.tar.gz: cf8653c6642afe656cad20b63e03b3689a315a246490dd6d7d153518f10f1269382a4a31d6b8eff0ed646bbc9828d538485730d52adb08d81597c9c19d1a5ba9
6
+ metadata.gz: 6bf4be0a1990ce7c1d6d6e2c30b936cc0a145401b2992b32e4f2c83153881746fdaab77de434a38409bc17c6c0cbb93ee4673a589767197b314b11b935078426
7
+ data.tar.gz: 65db2f2e2d112926111cb05398dd992a28d64f793a6f5d955b01a4cf03f1f25d9334d998b16088302479a07e6a3191712bc41d932c1b87d821f5d68b3e7b9974
data/Manifest.txt CHANGED
@@ -2,5 +2,7 @@ CHANGELOG.md
2
2
  Manifest.txt
3
3
  README.md
4
4
  Rakefile
5
+ bin/artq
5
6
  lib/artq.rb
7
+ lib/artq/contract.rb
6
8
  lib/artq/version.rb
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # ArtQ
2
2
 
3
- artq - query (ethereum) blockchain contracts / services for data about art collections via json-rpc
3
+ artq - query (ethereum) blockchain contracts / services for (meta) data about art collections via json-rpc
4
4
 
5
5
 
6
6
  * home :: [github.com/pixelartexchange/artbase](https://github.com/pixelartexchange/artbase)
@@ -10,9 +10,338 @@ artq - query (ethereum) blockchain contracts / services for data about art colle
10
10
 
11
11
 
12
12
 
13
+
13
14
  ## Usage
14
15
 
15
- To be done
16
+
17
+ ### Step 0: Setup JSON RPC Client
18
+
19
+ The ArtQ command line tool
20
+ gets the eth node uri via the INFURA_URI enviroment variable / key for now.
21
+ Set the environment variable / key
22
+ depending on your operating system (OS) e.g.:
23
+
24
+ ```
25
+ set INFURA_URI=https://mainnet.infura.io/v3/<YOUR_KEY_HERE>
26
+ ```
27
+
28
+
29
+ ### Query (Token) Collection Info
30
+
31
+ To use the artq command line tool pass in the art collection contract address in the hex (string) format.
32
+
33
+
34
+ #### Case No. 1 - "Off-Blockchain" Token Metadata
35
+
36
+ Let's try Moonbirds - an "off-blockchain" pixel art collection -
37
+ with the token contract / service at [0x23581767a106ae21c074b2276d25e5c3e136a68b](https://etherscan.io/address/0x23581767a106ae21c074b2276d25e5c3e136a68b):
38
+
39
+ ```
40
+ $ artq 0x23581767a106ae21c074b2276d25e5c3e136a68b # or
41
+ $ artq 0x23581767a106ae21c074b2276d25e5c3e136a68b info
42
+ ```
43
+
44
+ resulting in:
45
+
46
+ ```
47
+ name: >Moonbirds<
48
+ symbol: >MOONBIRD<
49
+ totalSupply: >10000<
50
+
51
+ tokenURIs 0..2:
52
+ tokenId 0:
53
+ https://live---metadata-5covpqijaa-uc.a.run.app/metadata/0
54
+ tokenId 1:
55
+ https://live---metadata-5covpqijaa-uc.a.run.app/metadata/1
56
+ tokenId 2:
57
+ https://live---metadata-5covpqijaa-uc.a.run.app/metadata/2
58
+ ```
59
+
60
+ Note: By default the tokenURI method gets called / queried
61
+ for the first tokens (e.g. 0, 1, 2, etc.).
62
+
63
+ If you get a link back (e.g. starting with `https://` or `ipfs://`)
64
+ than the art collection is "off-blockchain" and
65
+ you MUST follow / request the link to get the token metadata.
66
+
67
+
68
+ For example if you request <https://live---metadata-5covpqijaa-uc.a.run.app/metadata/0>
69
+ you get back:
70
+
71
+ ``` json
72
+ {"name":"#0",
73
+ "image":"https://live---metadata-5covpqijaa-uc.a.run.app/images/0",
74
+ "external_url":"https://moonbirds.xyz/",
75
+ "attributes":[
76
+ {"trait_type":"Eyes","value":"Angry"},
77
+ {"trait_type":"Outerwear","value":"Hoodie Down"},
78
+ {"trait_type":"Body","value":"Tabby"},
79
+ {"trait_type":"Feathers","value":"Gray"},
80
+ {"trait_type":"Background","value":"Green"},
81
+ {"trait_type":"Beak","value":"Small"}],
82
+ "x_debug":["orig:9650"]}
83
+ ```
84
+
85
+
86
+
87
+ #### Case No. 2 - "On-Blockchain" Token Metadata (With Inline Image)
88
+
89
+
90
+
91
+ Let's try The Saudis - an "on-blockchain" pixel art collection -
92
+ with the token contract / service at [0xe21ebcd28d37a67757b9bc7b290f4c4928a430b1](https://etherscan.io/address/0xe21ebcd28d37a67757b9bc7b290f4c4928a430b1):
93
+
94
+ ```
95
+ $ artq 0xe21ebcd28d37a67757b9bc7b290f4c4928a430b1 # or
96
+ $ artq 0xe21ebcd28d37a67757b9bc7b290f4c4928a430b1 info
97
+ ```
98
+
99
+ resulting in:
100
+
101
+ ```
102
+ name: >The Saudis<
103
+ symbol: >SAUD<
104
+ totalSupply: >5555<
105
+
106
+ tokenURIs 0..2:
107
+ tokenId 0:
108
+ ```
109
+ ``` json
110
+ {"name":"The Saudis #0",
111
+ "description":"Max Bidding",
112
+ "image_data": "...",
113
+ "external_url":"https://token.thesaudisnft.com/0",
114
+ "attributes":
115
+ [{"trait_type":"Head", "value":"Light 1"},
116
+ {"trait_type":"Hair", "value":"Bald"},
117
+ {"trait_type":"Facial Hair", "value":"Normal Brown Beard & Mustache"},
118
+ {"trait_type":"Headwear", "value":"Haram Police Cap"},
119
+ {"trait_type":"Eyewear", "value":"Regular Pixel Shades"},
120
+ {"trait_type":"Mouthpiece", "value":"None"}]}
121
+ ```
122
+
123
+ ```
124
+ tokenId 1:
125
+ ```
126
+ ``` json
127
+ {"name":"The Saudis #1",
128
+ "description":"Max Bidding",
129
+ "image_data": "...",
130
+ "external_url":"https://token.thesaudisnft.com/1",
131
+ "attributes":
132
+ [{"trait_type":"Head", "value":"Light 1"},
133
+ {"trait_type":"Hair", "value":"Long Widow's Peak"},
134
+ {"trait_type":"Facial Hair", "value":"Messy White Beard"},
135
+ {"trait_type":"Headwear", "value":"Brown Shemagh & Agal"},
136
+ {"trait_type":"Eyewear", "value":"Big Purple Shades"},
137
+ {"trait_type":"Mouthpiece", "value":"None"}]}
138
+ ```
139
+
140
+ ```
141
+ tokenId 2:
142
+ ```
143
+ ``` json
144
+ {"name":"The Saudis #2 🛢" ,
145
+ "description":"Max Bidding",
146
+ "image_data": "...",
147
+ "external_url":"https://token.thesaudisnft.com/2",
148
+ "attributes":
149
+ [{"trait_type":"Head", "value":"Dark 1"},
150
+ {"trait_type":"Hair", "value":"Short Buzz Cut"},
151
+ {"trait_type":"Facial Hair", "value":"Gradient Beard"},
152
+ {"trait_type":"Headwear", "value":"Brown Shemagh & Agal"},
153
+ {"trait_type":"Eyewear", "value":"Big Pixel Shades"},
154
+ {"trait_type":"Mouthpiece", "value":"Shadowless Vape"}]}
155
+ ```
156
+
157
+ Note: The artq command-line tool "auto-magically"
158
+ decodes "on-blockchain" metadata in the base64 format
159
+ and inline svg images in the base64 format get "cut" from the metadata and "pasted" decoded. Example for tokenId 0, that is, The Saudis #0:
160
+
161
+ ``` xml
162
+ <svg xmlns="http://www.w3.org/2000/svg"
163
+ xmlns:xlink="http://www.w3.org/1999/xlink" image-rendering="pixelated"
164
+ height="336" width="336">
165
+ <foreignObject x="0" y="0" width="336" height="336">
166
+ <img xmlns="http://www.w3.org/1999/xhtml" height="336" width="336" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAJklEQVR42mNMbp32n4GGgHHUglELRi0YtWDUglELRi0YtWBoWAAAuD470bkESf4AAAAASUVORK5CYII="/>
167
+ </foreignObject>
168
+ <foreignObject x="0" y="0" width="336" height="336">
169
+ <img xmlns="http://www.w3.org/1999/xhtml" height="336" width="336" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAtklEQVRIiWNgGAXDHjASoeY/JfoJKfh/e2MDTklV/waCZjARYziPlCZWBVB5fD7EawHc8C/PrhNSRr4FlBhOlAWUAhZiFK3fdYqBgYGBIdDNDEOMEMDlA5TUUzZ1G4OHkRiKAmxi2ABRPsCWVPElX2SAMw6gaZxigMsCnJlH1b+BJMuJCiJkQGzQwMDgSKYwgBw0xPqEJAtIDR4GBgqDiJjSlGgf4Eg5BOsDUlMRMRUUCqB5KgIAHCwuITa/cDMAAAAASUVORK5CYII=" />
170
+ </foreignObject>
171
+ <foreignObject x="0" y="0" width="336" height="336">
172
+ <img xmlns="http://www.w3.org/1999/xhtml" height="336" width="336" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAaklEQVRIiWNgGAWjYBSMglEwBAAjPklnSZb/DAwMjDzCAgwbr7whywImItT8//L2Az4H4AUsRDqEoEGUWsDgLMnC8JSJFc6X/vebKhYwMiC5nlhDkQExcYAvIeBNJMRaADMI3TCChg8PAADd6xC7QG7FfAAAAABJRU5ErkJggg==" />
173
+ </foreignObject>
174
+ <foreignObject x="0" y="0" width="336" height="336">
175
+ <img xmlns="http://www.w3.org/1999/xhtml" height="336" width="336" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAcElEQVRIie2SsQ3AIAwEnwzDCpTMyiguGQEvQ5okQoljTNL6KiSes3gAnAlhIdu/nNOCoxAx5WvNlcyet40+CmccA0XXZrbcaIXQyuMm5gFBqEGEKyGmvFwRMLzBrC6tIvU3nGKu1LWcxeU4juP8YQdbhhrmTahwzwAAAABJRU5ErkJggg==" />
176
+ </foreignObject>
177
+ <foreignObject x="0" y="0" width="336" height="336">
178
+ <img xmlns="http://www.w3.org/1999/xhtml" height="336" width="336" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAATElEQVRIie2PwQoAIAhDXf//z+tQgYnWoVOwdxHm2NRMCPE/qBYkDQBfs1rpDuEk10SmV5QFPmh+k3lSfTv0UDAM7hNfGvVbkRDiZzpfrCH/gTYLqwAAAABJRU5ErkJggg==" />
179
+ </foreignObject>
180
+ <foreignObject x="0" y="0" width="336" height="336">
181
+ <img xmlns="http://www.w3.org/1999/xhtml" height="336" width="336" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAJklEQVRIie3NMQEAAAjDMMC/52ECvlRA00nqs3m9AwAAAAAAAJy1C7oDLddyCRYAAAAASUVORK5CYII=" />
182
+ </foreignObject>
183
+ </svg>
184
+ ```
185
+
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: See the [**Art Factory Sandbox**](https://github.com/pixelartexchange/artfactory.sandbox) for more art collections with "on-blockchain" layers.
343
+
344
+
16
345
 
17
346
 
18
347
  ## License
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ Hoe.spec 'artq' do
6
6
 
7
7
  self.version = ArtQ::VERSION
8
8
 
9
- self.summary = "artq - query (ethereum) blockchain contracts / services for data about art collections via json-rpc"
9
+ self.summary = "artq - query (ethereum) blockchain contracts / services for (meta) data about art collections via json-rpc"
10
10
  self.description = summary
11
11
 
12
12
  self.urls = { home: 'https://github.com/pixelartexchange/artbase' }
data/bin/artq ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ ###################
4
+ # == DEV TIPS:
5
+ #
6
+ # For local testing run like:
7
+ #
8
+ # ruby -Ilib bin/artq
9
+ #
10
+ # Set the executable bit in Linux. Example:
11
+ #
12
+ # % chmod a+x bin/artq
13
+ #
14
+
15
+ require 'artq'
16
+
17
+ ArtQ::Tool.main
@@ -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
+ def self.rpc
54
+ @rpc ||= JsonRpc.new( ENV['INFURA_URI'] )
55
+ end
56
+
57
+ def self.rpc=(value)
58
+ @rpc = value.is_a?( String ) ? JsonRpc.new( value ) : value
59
+ end
60
+
61
+
62
+ def initialize( contract_address )
63
+ @contract_address = contract_address
64
+ end
65
+
66
+ ######
67
+ # private helper to call method
68
+ def _do_call( eth, args )
69
+ eth.do_call( self.class.rpc, @contract_address, args )
70
+ end
71
+
72
+ end # class Contract
73
+ end # module ArtQ
data/lib/artq/version.rb CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
 
3
3
  module ArtQ
4
- VERSION = '0.0.1'
4
+ VERSION = '0.2.0'
5
5
  end # module ArtQ
6
6
 
7
7
 
data/lib/artq.rb CHANGED
@@ -1,6 +1,214 @@
1
+ require 'ethlite'
2
+ require 'optparse'
1
3
 
2
4
 
3
- require_relative 'artq/version'
5
+ ## our own code
6
+ require_relative 'artq/version' # let version go first
7
+ require_relative 'artq/contract'
8
+
9
+
10
+
11
+ module ArtQ
12
+
13
+
14
+
15
+
16
+
17
+ class Tool
18
+
19
+ def self.main( args=ARGV )
20
+ puts "==> welcome to artq tool with args:"
21
+ pp args
22
+
23
+ options = {
24
+ }
25
+
26
+ parser = OptionParser.new do |opts|
27
+
28
+ opts.on("--rpc STRING",
29
+ "JSON RPC Host (default: nil)") do |str|
30
+ options[ :rpc] = str
31
+ end
32
+
33
+ opts.on("-h", "--help", "Prints this help") do
34
+ puts opts
35
+ exit
36
+ end
37
+ end
38
+
39
+ parser.parse!( args )
40
+ puts "options:"
41
+ pp options
42
+
43
+ puts "args:"
44
+ pp args
45
+
46
+ if args.size < 1
47
+ puts "!! ERROR - no collection found - use <collection> <command>..."
48
+ puts ""
49
+ exit
50
+ end
51
+
52
+ contract_address = args[0] ## todo/check - use collection_name/slug or such?
53
+ command = args[1] || 'info'
54
+
55
+
56
+ if ['i','inf','info'].include?( command )
57
+ do_info( contract_address )
58
+ elsif ['l', 'layer', 'layers'].include?( command )
59
+ do_layers( contract_address )
60
+ else
61
+ puts "!! ERROR - unknown command >#{command}<, sorry"
62
+ end
63
+
64
+ puts "bye"
65
+ end
66
+
67
+
68
+
69
+
70
+
71
+ def self.do_info( contract_address )
72
+ puts "==> query info for art collection contract @ >#{contract_address}<:"
73
+
74
+ c = Contract.new( contract_address )
75
+
76
+ name = c.name
77
+ symbol = c.symbol
78
+ totalSupply = c.totalSupply
79
+
80
+ meta = []
81
+ tokenIds = (0..2)
82
+ tokenIds.each do |tokenId|
83
+ tokenURI = c.tokenURI( tokenId )
84
+ meta << [tokenId, tokenURI]
85
+ end
86
+
87
+ puts " name: >#{name}<"
88
+ puts " symbol: >#{symbol}<"
89
+ puts " totalSupply: >#{totalSupply}<"
90
+ puts
91
+ puts " tokenURIs #{tokenIds}:"
92
+ meta.each do |tokenId, tokenURI|
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
113
+ puts
114
+ puts " image_data:"
115
+ puts image_data
116
+ else
117
+ puts " #{tokenURI}"
118
+ end
119
+ end
120
+ end
121
+
122
+
123
+
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
+ def self.do_layers( contract_address )
131
+ puts "==> query layers for art collection contract @ >#{contract_address}<:"
132
+
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
206
+ end
207
+
208
+
209
+ end # class Tool
210
+ end # module ArtQ
211
+
4
212
 
5
213
 
6
214
 
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.0.1
4
+ version: 0.2.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-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ethlite
@@ -58,10 +58,11 @@ dependencies:
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
60
  version: '3.23'
61
- description: artq - query (ethereum) blockchain contracts / services for data about
62
- art collections via json-rpc
61
+ description: artq - query (ethereum) blockchain contracts / services for (meta) data
62
+ about art collections via json-rpc
63
63
  email: wwwmake@googlegroups.com
64
- executables: []
64
+ executables:
65
+ - artq
65
66
  extensions: []
66
67
  extra_rdoc_files:
67
68
  - CHANGELOG.md
@@ -72,7 +73,9 @@ files:
72
73
  - Manifest.txt
73
74
  - README.md
74
75
  - Rakefile
76
+ - bin/artq
75
77
  - lib/artq.rb
78
+ - lib/artq/contract.rb
76
79
  - lib/artq/version.rb
77
80
  homepage: https://github.com/pixelartexchange/artbase
78
81
  licenses:
@@ -98,6 +101,6 @@ requirements: []
98
101
  rubygems_version: 3.3.7
99
102
  signing_key:
100
103
  specification_version: 4
101
- summary: artq - query (ethereum) blockchain contracts / services for data about art
102
- collections via json-rpc
104
+ summary: artq - query (ethereum) blockchain contracts / services for (meta) data about
105
+ art collections via json-rpc
103
106
  test_files: []