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 +4 -4
- data/Manifest.txt +2 -0
- data/README.md +331 -2
- data/Rakefile +1 -1
- data/bin/artq +17 -0
- data/lib/artq/contract.rb +73 -0
- data/lib/artq/version.rb +1 -1
- data/lib/artq.rb +209 -1
- metadata +10 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c0f00a597b6533cbdce4aa3286531d8533f9d3c0cf57b8147b7160d4b1d206d3
|
4
|
+
data.tar.gz: e192cfb230111adb5dd69a1213a1446a9dc629fc853cacb8b10e5d80ac723e8e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6bf4be0a1990ce7c1d6d6e2c30b936cc0a145401b2992b32e4f2c83153881746fdaab77de434a38409bc17c6c0cbb93ee4673a589767197b314b11b935078426
|
7
|
+
data.tar.gz: 65db2f2e2d112926111cb05398dd992a28d64f793a6f5d955b01a4cf03f1f25d9334d998b16088302479a07e6a3191712bc41d932c1b87d821f5d68b3e7b9974
|
data/Manifest.txt
CHANGED
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
|
-
|
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
data/lib/artq.rb
CHANGED
@@ -1,6 +1,214 @@
|
|
1
|
+
require 'ethlite'
|
2
|
+
require 'optparse'
|
1
3
|
|
2
4
|
|
3
|
-
|
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
|
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-
|
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
|
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
|
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: []
|