artq 0.0.1 → 0.1.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 +4 -4
- data/Manifest.txt +1 -0
- data/README.md +171 -2
- data/Rakefile +1 -1
- data/bin/artq +17 -0
- data/lib/artq/version.rb +1 -1
- data/lib/artq.rb +131 -1
- metadata +8 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c9779a590a436cc490adf3f14a21c2a663c349a56cc2d3546fa3d7ca490ed157
|
4
|
+
data.tar.gz: 8c54bc4cdbc6e1626f13f5d445b39a0fa6d029f9330aefa030f37d7f8fa83558
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c75af5b52d7b37b752f9e7de03b5e3d7f84f8ead09cc3f537c1897c2468990a812b2de9b6fd2a44c1c54237bf6cc4120e63ea0b31b1c4bd907db58c56412ef82
|
7
|
+
data.tar.gz: d37f4a40864b66dccf69b629f07572f10abb38036047bd35e04947499f7de767800f87f8fe852414b9201835782589a8e6d237a60748d36d22d558140b2f763c
|
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,178 @@ 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
|
+
#### "Off-Blockchain" Token Metadata - Case No. 1
|
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
|
41
|
+
```
|
42
|
+
|
43
|
+
resulting in:
|
44
|
+
|
45
|
+
```
|
46
|
+
name: >Moonbirds<
|
47
|
+
symbol: >MOONBIRD<
|
48
|
+
totalSupply: >10000<
|
49
|
+
|
50
|
+
tokenURIs 0..2:
|
51
|
+
tokenId: 0
|
52
|
+
https://live---metadata-5covpqijaa-uc.a.run.app/metadata/0
|
53
|
+
tokenId: 1
|
54
|
+
https://live---metadata-5covpqijaa-uc.a.run.app/metadata/1
|
55
|
+
tokenId: 2
|
56
|
+
https://live---metadata-5covpqijaa-uc.a.run.app/metadata/2
|
57
|
+
```
|
58
|
+
|
59
|
+
Note: By default the tokenURI method gets called / queried
|
60
|
+
for the first tokens (e.g. 0, 1, 2, etc.).
|
61
|
+
|
62
|
+
If you get a link back (e.g. starting with `https://` or `ipfs://`)
|
63
|
+
than the art collection is "off-blockchain" and
|
64
|
+
you MUST follow / request the link to get the token metadata.
|
65
|
+
|
66
|
+
|
67
|
+
For example if you request <https://live---metadata-5covpqijaa-uc.a.run.app/metadata/0>
|
68
|
+
you get back:
|
69
|
+
|
70
|
+
``` json
|
71
|
+
{"name":"#0",
|
72
|
+
"image":"https://live---metadata-5covpqijaa-uc.a.run.app/images/0",
|
73
|
+
"external_url":"https://moonbirds.xyz/",
|
74
|
+
"attributes":[
|
75
|
+
{"trait_type":"Eyes","value":"Angry"},
|
76
|
+
{"trait_type":"Outerwear","value":"Hoodie Down"},
|
77
|
+
{"trait_type":"Body","value":"Tabby"},
|
78
|
+
{"trait_type":"Feathers","value":"Gray"},
|
79
|
+
{"trait_type":"Background","value":"Green"},
|
80
|
+
{"trait_type":"Beak","value":"Small"}],
|
81
|
+
"x_debug":["orig:9650"]}
|
82
|
+
```
|
83
|
+
|
84
|
+
|
85
|
+
|
86
|
+
#### "On-Blockchain" Token Metadata (With Inline Image) - Case No. 2
|
87
|
+
|
88
|
+
|
89
|
+
|
90
|
+
Let's try The Saudis - an ("on-blockchain") pixel art collection -
|
91
|
+
with the token contract / service at [0xe21ebcd28d37a67757b9bc7b290f4c4928a430b1](https://etherscan.io/address/0xe21ebcd28d37a67757b9bc7b290f4c4928a430b1):
|
92
|
+
|
93
|
+
```
|
94
|
+
$ artq 0xe21ebcd28d37a67757b9bc7b290f4c4928a430b1
|
95
|
+
```
|
96
|
+
|
97
|
+
resulting in:
|
98
|
+
|
99
|
+
```
|
100
|
+
name: >The Saudis<
|
101
|
+
symbol: >SAUD<
|
102
|
+
totalSupply: >5555<
|
103
|
+
|
104
|
+
tokenURIs 0..2:
|
105
|
+
tokenId 0:
|
106
|
+
```
|
107
|
+
``` json
|
108
|
+
{"name":"The Saudis #0",
|
109
|
+
"description":"Max Bidding",
|
110
|
+
"image_data": "...",
|
111
|
+
"external_url":"https://token.thesaudisnft.com/0",
|
112
|
+
"attributes":
|
113
|
+
[{"trait_type":"Head", "value":"Light 1"},
|
114
|
+
{"trait_type":"Hair", "value":"Bald"},
|
115
|
+
{"trait_type":"Facial Hair", "value":"Normal Brown Beard & Mustache"},
|
116
|
+
{"trait_type":"Headwear", "value":"Haram Police Cap"},
|
117
|
+
{"trait_type":"Eyewear", "value":"Regular Pixel Shades"},
|
118
|
+
{"trait_type":"Mouthpiece", "value":"None"}]}
|
119
|
+
```
|
120
|
+
|
121
|
+
```
|
122
|
+
tokenId 1:
|
123
|
+
```
|
124
|
+
``` json
|
125
|
+
{"name":"The Saudis #1",
|
126
|
+
"description":"Max Bidding",
|
127
|
+
"image_data": "...",
|
128
|
+
"external_url":"https://token.thesaudisnft.com/1",
|
129
|
+
"attributes":
|
130
|
+
[{"trait_type":"Head", "value":"Light 1"},
|
131
|
+
{"trait_type":"Hair", "value":"Long Widow's Peak"},
|
132
|
+
{"trait_type":"Facial Hair", "value":"Messy White Beard"},
|
133
|
+
{"trait_type":"Headwear", "value":"Brown Shemagh & Agal"},
|
134
|
+
{"trait_type":"Eyewear", "value":"Big Purple Shades"},
|
135
|
+
{"trait_type":"Mouthpiece", "value":"None"}]}
|
136
|
+
```
|
137
|
+
|
138
|
+
```
|
139
|
+
tokenId 2:
|
140
|
+
```
|
141
|
+
``` json
|
142
|
+
{"name":"The Saudis #2 🛢" ,
|
143
|
+
"description":"Max Bidding",
|
144
|
+
"image_data": "...",
|
145
|
+
"external_url":"https://token.thesaudisnft.com/2",
|
146
|
+
"attributes":
|
147
|
+
[{"trait_type":"Head", "value":"Dark 1"},
|
148
|
+
{"trait_type":"Hair", "value":"Short Buzz Cut"},
|
149
|
+
{"trait_type":"Facial Hair", "value":"Gradient Beard"},
|
150
|
+
{"trait_type":"Headwear", "value":"Brown Shemagh & Agal"},
|
151
|
+
{"trait_type":"Eyewear", "value":"Big Pixel Shades"},
|
152
|
+
{"trait_type":"Mouthpiece", "value":"Shadowless Vape"}]}
|
153
|
+
```
|
154
|
+
|
155
|
+
Note: The artq command-line tool "auto-magically"
|
156
|
+
decodes "on-blockchain" metadata in the base64 format
|
157
|
+
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:
|
158
|
+
|
159
|
+
``` xml
|
160
|
+
<svg xmlns="http://www.w3.org/2000/svg"
|
161
|
+
xmlns:xlink="http://www.w3.org/1999/xlink" image-rendering="pixelated"
|
162
|
+
height="336" width="336">
|
163
|
+
<foreignObject x="0" y="0" width="336" height="336">
|
164
|
+
<img xmlns="http://www.w3.org/1999/xhtml" height="336" width="336" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAJklEQVR42mNMbp32n4GGgHHUglELRi0YtWDUglELRi0YtWBoWAAAuD470bkESf4AAAAASUVORK5CYII="/>
|
165
|
+
</foreignObject>
|
166
|
+
<foreignObject x="0" y="0" width="336" height="336">
|
167
|
+
<img xmlns="http://www.w3.org/1999/xhtml" height="336" width="336" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAtklEQVRIiWNgGAXDHjASoeY/JfoJKfh/e2MDTklV/waCZjARYziPlCZWBVB5fD7EawHc8C/PrhNSRr4FlBhOlAWUAhZiFK3fdYqBgYGBIdDNDEOMEMDlA5TUUzZ1G4OHkRiKAmxi2ABRPsCWVPElX2SAMw6gaZxigMsCnJlH1b+BJMuJCiJkQGzQwMDgSKYwgBw0xPqEJAtIDR4GBgqDiJjSlGgf4Eg5BOsDUlMRMRUUCqB5KgIAHCwuITa/cDMAAAAASUVORK5CYII=" />
|
168
|
+
</foreignObject>
|
169
|
+
<foreignObject x="0" y="0" width="336" height="336">
|
170
|
+
<img xmlns="http://www.w3.org/1999/xhtml" height="336" width="336" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAaklEQVRIiWNgGAWjYBSMglEwBAAjPklnSZb/DAwMjDzCAgwbr7whywImItT8//L2Az4H4AUsRDqEoEGUWsDgLMnC8JSJFc6X/vebKhYwMiC5nlhDkQExcYAvIeBNJMRaADMI3TCChg8PAADd6xC7QG7FfAAAAABJRU5ErkJggg==" />
|
171
|
+
</foreignObject>
|
172
|
+
<foreignObject x="0" y="0" width="336" height="336">
|
173
|
+
<img xmlns="http://www.w3.org/1999/xhtml" height="336" width="336" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAcElEQVRIie2SsQ3AIAwEnwzDCpTMyiguGQEvQ5okQoljTNL6KiSes3gAnAlhIdu/nNOCoxAx5WvNlcyet40+CmccA0XXZrbcaIXQyuMm5gFBqEGEKyGmvFwRMLzBrC6tIvU3nGKu1LWcxeU4juP8YQdbhhrmTahwzwAAAABJRU5ErkJggg==" />
|
174
|
+
</foreignObject>
|
175
|
+
<foreignObject x="0" y="0" width="336" height="336">
|
176
|
+
<img xmlns="http://www.w3.org/1999/xhtml" height="336" width="336" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAATElEQVRIie2PwQoAIAhDXf//z+tQgYnWoVOwdxHm2NRMCPE/qBYkDQBfs1rpDuEk10SmV5QFPmh+k3lSfTv0UDAM7hNfGvVbkRDiZzpfrCH/gTYLqwAAAABJRU5ErkJggg==" />
|
177
|
+
</foreignObject>
|
178
|
+
<foreignObject x="0" y="0" width="336" height="336">
|
179
|
+
<img xmlns="http://www.w3.org/1999/xhtml" height="336" width="336" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAJklEQVRIie3NMQEAAAjDMMC/52ECvlRA00nqs3m9AwAAAAAAAJy1C7oDLddyCRYAAAAASUVORK5CYII=" />
|
180
|
+
</foreignObject>
|
181
|
+
</svg>
|
182
|
+
```
|
183
|
+
|
184
|
+
|
16
185
|
|
17
186
|
|
18
187
|
## 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
|
data/lib/artq/version.rb
CHANGED
data/lib/artq.rb
CHANGED
@@ -1,6 +1,136 @@
|
|
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
|
+
|
8
|
+
|
9
|
+
|
10
|
+
module ArtQ
|
11
|
+
|
12
|
+
class Tool
|
13
|
+
|
14
|
+
def self.main( args=ARGV )
|
15
|
+
puts "==> welcome to artq tool with args:"
|
16
|
+
pp args
|
17
|
+
|
18
|
+
options = {
|
19
|
+
}
|
20
|
+
|
21
|
+
parser = OptionParser.new do |opts|
|
22
|
+
|
23
|
+
opts.on("--rpc STRING",
|
24
|
+
"JSON RPC Host (default: nil)") do |str|
|
25
|
+
options[ :rpc] = str
|
26
|
+
end
|
27
|
+
|
28
|
+
opts.on("-h", "--help", "Prints this help") do
|
29
|
+
puts opts
|
30
|
+
exit
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
parser.parse!( args )
|
35
|
+
puts "options:"
|
36
|
+
pp options
|
37
|
+
|
38
|
+
puts "args:"
|
39
|
+
pp args
|
40
|
+
|
41
|
+
if args.size < 1
|
42
|
+
puts "!! ERROR - no collection found - use <collection> <command>..."
|
43
|
+
puts ""
|
44
|
+
exit
|
45
|
+
end
|
46
|
+
|
47
|
+
contract_address = args[0] ## todo/check - use collection_name/slug or such?
|
48
|
+
command = args[1] || 'info'
|
49
|
+
|
50
|
+
|
51
|
+
if ['i','inf','info'].include?( command )
|
52
|
+
do_info( contract_address )
|
53
|
+
else
|
54
|
+
puts "!! ERROR - unknown command >#{command}<, sorry"
|
55
|
+
end
|
56
|
+
|
57
|
+
puts "bye"
|
58
|
+
end
|
59
|
+
|
60
|
+
|
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
|
+
|
77
|
+
|
78
|
+
|
79
|
+
def self.do_info( contract_address )
|
80
|
+
puts "==> query art collection contract info @ >#{contract_address}<:"
|
81
|
+
|
82
|
+
rpc = JsonRpc.new( ENV['INFURA_URI'] )
|
83
|
+
|
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, [] )
|
87
|
+
|
88
|
+
meta = []
|
89
|
+
tokenIds = (0..2)
|
90
|
+
tokenIds.each do |tokenId|
|
91
|
+
tokenURI = ETH_tokenURI.do_call( rpc, contract_address, [tokenId] )
|
92
|
+
meta << [tokenId, tokenURI]
|
93
|
+
end
|
94
|
+
|
95
|
+
puts " name: >#{name}<"
|
96
|
+
puts " symbol: >#{symbol}<"
|
97
|
+
puts " totalSupply: >#{totalSupply}<"
|
98
|
+
puts
|
99
|
+
puts " tokenURIs #{tokenIds}:"
|
100
|
+
meta.each do |tokenId, tokenURI|
|
101
|
+
puts " tokenId #{tokenId}:"
|
102
|
+
if tokenURI.start_with?( 'data:application/json;base64')
|
103
|
+
## on-blockchain!!!
|
104
|
+
## decode base64
|
105
|
+
|
106
|
+
str = tokenURI.sub( 'data:application/json;base64', '' )
|
107
|
+
str = Base64.decode64( str )
|
108
|
+
data = JSON.parse( str )
|
109
|
+
|
110
|
+
|
111
|
+
## check for image_data - and replace if base64 encoded
|
112
|
+
image_data = data['image_data']
|
113
|
+
|
114
|
+
if image_data.start_with?( 'data:image/svg+xml;base64' )
|
115
|
+
data['image_data'] = '...'
|
116
|
+
str = image_data.sub( 'data:image/svg+xml;base64', '' )
|
117
|
+
image_data = Base64.decode64( str )
|
118
|
+
end
|
119
|
+
|
120
|
+
pp data
|
121
|
+
puts
|
122
|
+
puts " image_data:"
|
123
|
+
puts image_data
|
124
|
+
else
|
125
|
+
puts " #{tokenURI}"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end # class Tool
|
130
|
+
|
131
|
+
|
132
|
+
end # module ArtQ
|
133
|
+
|
4
134
|
|
5
135
|
|
6
136
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: artq
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gerald Bauer
|
@@ -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,6 +73,7 @@ files:
|
|
72
73
|
- Manifest.txt
|
73
74
|
- README.md
|
74
75
|
- Rakefile
|
76
|
+
- bin/artq
|
75
77
|
- lib/artq.rb
|
76
78
|
- lib/artq/version.rb
|
77
79
|
homepage: https://github.com/pixelartexchange/artbase
|
@@ -98,6 +100,6 @@ requirements: []
|
|
98
100
|
rubygems_version: 3.3.7
|
99
101
|
signing_key:
|
100
102
|
specification_version: 4
|
101
|
-
summary: artq - query (ethereum) blockchain contracts / services for data about
|
102
|
-
collections via json-rpc
|
103
|
+
summary: artq - query (ethereum) blockchain contracts / services for (meta) data about
|
104
|
+
art collections via json-rpc
|
103
105
|
test_files: []
|