artq 0.1.0 → 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: c9779a590a436cc490adf3f14a21c2a663c349a56cc2d3546fa3d7ca490ed157
4
- data.tar.gz: 8c54bc4cdbc6e1626f13f5d445b39a0fa6d029f9330aefa030f37d7f8fa83558
3
+ metadata.gz: c0f00a597b6533cbdce4aa3286531d8533f9d3c0cf57b8147b7160d4b1d206d3
4
+ data.tar.gz: e192cfb230111adb5dd69a1213a1446a9dc629fc853cacb8b10e5d80ac723e8e
5
5
  SHA512:
6
- metadata.gz: c75af5b52d7b37b752f9e7de03b5e3d7f84f8ead09cc3f537c1897c2468990a812b2de9b6fd2a44c1c54237bf6cc4120e63ea0b31b1c4bd907db58c56412ef82
7
- data.tar.gz: d37f4a40864b66dccf69b629f07572f10abb38036047bd35e04947499f7de767800f87f8fe852414b9201835782589a8e6d237a60748d36d22d558140b2f763c
6
+ metadata.gz: 6bf4be0a1990ce7c1d6d6e2c30b936cc0a145401b2992b32e4f2c83153881746fdaab77de434a38409bc17c6c0cbb93ee4673a589767197b314b11b935078426
7
+ data.tar.gz: 65db2f2e2d112926111cb05398dd992a28d64f793a6f5d955b01a4cf03f1f25d9334d998b16088302479a07e6a3191712bc41d932c1b87d821f5d68b3e7b9974
data/Manifest.txt CHANGED
@@ -4,4 +4,5 @@ README.md
4
4
  Rakefile
5
5
  bin/artq
6
6
  lib/artq.rb
7
+ lib/artq/contract.rb
7
8
  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,164 @@ 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: See the [**Art Factory Sandbox**](https://github.com/pixelartexchange/artfactory.sandbox) for more art collections with "on-blockchain" layers.
343
+
344
+
185
345
 
186
346
 
187
347
  ## 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
+ 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.1.0'
4
+ VERSION = '0.2.0'
5
5
  end # module ArtQ
6
6
 
7
7
 
data/lib/artq.rb CHANGED
@@ -4,11 +4,16 @@ require 'optparse'
4
4
 
5
5
  ## our own code
6
6
  require_relative 'artq/version' # let version go first
7
+ require_relative 'artq/contract'
7
8
 
8
9
 
9
10
 
10
11
  module ArtQ
11
12
 
13
+
14
+
15
+
16
+
12
17
  class Tool
13
18
 
14
19
  def self.main( args=ARGV )
@@ -50,6 +55,8 @@ class Tool
50
55
 
51
56
  if ['i','inf','info'].include?( command )
52
57
  do_info( contract_address )
58
+ elsif ['l', 'layer', 'layers'].include?( command )
59
+ do_layers( contract_address )
53
60
  else
54
61
  puts "!! ERROR - unknown command >#{command}<, sorry"
55
62
  end
@@ -58,37 +65,22 @@ 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
80
  meta = []
89
81
  tokenIds = (0..2)
90
82
  tokenIds.each do |tokenId|
91
- tokenURI = ETH_tokenURI.do_call( rpc, contract_address, [tokenId] )
83
+ tokenURI = c.tokenURI( tokenId )
92
84
  meta << [tokenId, tokenURI]
93
85
  end
94
86
 
@@ -126,9 +118,95 @@ class Tool
126
118
  end
127
119
  end
128
120
  end
129
- end # class Tool
130
121
 
131
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
132
210
  end # module ArtQ
133
211
 
134
212
 
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.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
@@ -75,6 +75,7 @@ files:
75
75
  - Rakefile
76
76
  - bin/artq
77
77
  - lib/artq.rb
78
+ - lib/artq/contract.rb
78
79
  - lib/artq/version.rb
79
80
  homepage: https://github.com/pixelartexchange/artbase
80
81
  licenses: