artq 0.0.1 → 0.2.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: 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: []