ordinals 0.1.0 → 1.0.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 +3 -0
- data/README.md +4 -3
- data/Rakefile +3 -2
- data/lib/ordinals/api.rb +224 -0
- data/lib/ordinals/collection.rb +91 -29
- data/lib/ordinals/stats.rb +142 -0
- data/lib/ordinals/tool.rb +121 -0
- data/lib/ordinals.rb +61 -64
- metadata +21 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 23ab1c1b894f3d7d528d90943d890e07c40d878cc56b54206384be335ae47fa6
|
|
4
|
+
data.tar.gz: f3ca9276b04d13052424d9a4834204d7153a92681507efac9c8511949e6ec362
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a91a05ebbaf2ab66030ef45bf9a8da6744df0f40d3014451364f1d802a0193c0a7450f90647612e95c6469b7c21a6f8c6f04a10e3a354d820cafa88de32ccc6b
|
|
7
|
+
data.tar.gz: 30ca5d81426d13f3149a37f9eb8a7bc3a703c22d9cf0685c991033273ae81efe4917fce3d00f68aadfaf9742ab0d626bdc6e290a2f52c8b2c967b0453ebaa475
|
data/Manifest.txt
CHANGED
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ordinals
|
|
2
2
|
|
|
3
|
-
ordinals gem - "right-clicker" (off-chain) ordinals (pixel art) machinery & helpers for Bitcoin & co.
|
|
3
|
+
ordinals gem - "right-clicker" (off-chain) ordinals (pixel art) machinery & helpers for Bitcoin, Litcoin, Dogecoin & co.
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
* home :: [github.com/pixelartexchange/ordinals.sandbox](https://github.com/pixelartexchange/ordinals.sandbox)
|
|
@@ -9,7 +9,8 @@ ordinals gem - "right-clicker" (off-chain) ordinals (pixel art) machinery & help
|
|
|
9
9
|
* rdoc :: [rubydoc.info/gems/ordinals](http://rubydoc.info/gems/ordinals)
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
|
|
13
|
+
## Command-Line Usage
|
|
13
14
|
|
|
14
15
|
Let's use the 100 Ordinal Punks collection to try out the
|
|
15
16
|
`ordbase` command-line tool shipping with the ordinals package.
|
|
@@ -165,6 +166,6 @@ Use it as you please with no restrictions whatsoever.
|
|
|
165
166
|
|
|
166
167
|
## Questions? Comments?
|
|
167
168
|
|
|
169
|
+
Post them over at the [Help & Support](https://github.com/geraldb/help) page. Thanks.
|
|
168
170
|
|
|
169
|
-
Post them on the [D.I.Y. Punk (Pixel) Art reddit](https://old.reddit.com/r/DIYPunkArt). Thanks.
|
|
170
171
|
|
data/Rakefile
CHANGED
|
@@ -10,9 +10,9 @@ end
|
|
|
10
10
|
|
|
11
11
|
Hoe.spec 'ordinals' do
|
|
12
12
|
|
|
13
|
-
self.version = '
|
|
13
|
+
self.version = '1.0.0'
|
|
14
14
|
|
|
15
|
-
self.summary = 'ordinals gem - "right-clicker" (off-chain) ordinals (pixel art) machinery & helpers for Bitcoin & co.'
|
|
15
|
+
self.summary = 'ordinals gem - "right-clicker" (off-chain) ordinals (pixel art) machinery & helpers for Bitcoin, Litecoin, Dogecoin & co.'
|
|
16
16
|
self.description = summary
|
|
17
17
|
|
|
18
18
|
self.urls = { home: 'https://github.com/pixelartexchange/ordinals.sandbox' }
|
|
@@ -27,6 +27,7 @@ Hoe.spec 'ordinals' do
|
|
|
27
27
|
self.extra_deps = [
|
|
28
28
|
['cocos'],
|
|
29
29
|
['pixelart'],
|
|
30
|
+
['nokogiri'], ## required / used by api support (html parsing)
|
|
30
31
|
]
|
|
31
32
|
|
|
32
33
|
self.licenses = ['Public Domain']
|
data/lib/ordinals/api.rb
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
module Ordinals
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Api ## change/rename Api to Client - why? why not?
|
|
7
|
+
def self.litecoin
|
|
8
|
+
@litecoin ||= new( 'https://litecoin.earlyordies.com' )
|
|
9
|
+
@litecoin
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.bitcoin
|
|
13
|
+
@bitcoin ||= new( 'https://ordinals.com' )
|
|
14
|
+
@bitcoin
|
|
15
|
+
end
|
|
16
|
+
## todo: add ltc and btc alias - why? why not?
|
|
17
|
+
|
|
18
|
+
def self.dogecoin
|
|
19
|
+
## note: "doginals" call inscriptions
|
|
20
|
+
## shibescriptions
|
|
21
|
+
@dogecoin ||= new( 'https://doginals.com', inscription: 'shibescription' )
|
|
22
|
+
@dogecoin
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def initialize( base, inscription: 'inscription' )
|
|
27
|
+
@base = base
|
|
28
|
+
@inscription = inscription
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
###
|
|
33
|
+
# use a struct-like content class - why? why not?
|
|
34
|
+
class Content
|
|
35
|
+
attr_reader :data,
|
|
36
|
+
:type,
|
|
37
|
+
:length
|
|
38
|
+
def initialize( data, type, length )
|
|
39
|
+
@data = data
|
|
40
|
+
@type = type
|
|
41
|
+
@length = length
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
alias_method :blob, :data
|
|
45
|
+
end ## (nested) class Content
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def content( id )
|
|
50
|
+
src = "#{@base}/content/#{id}"
|
|
51
|
+
res = get( src )
|
|
52
|
+
|
|
53
|
+
content_type = res.content_type
|
|
54
|
+
content_length = res.content_length
|
|
55
|
+
|
|
56
|
+
## note - content_length -- returns an integer (number)
|
|
57
|
+
## puts "content_length:"
|
|
58
|
+
## print content_length.inspect
|
|
59
|
+
## print " - #{content_length.class.name}\n"
|
|
60
|
+
|
|
61
|
+
content = Content.new(
|
|
62
|
+
res.blob,
|
|
63
|
+
content_type,
|
|
64
|
+
content_length )
|
|
65
|
+
content
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
=begin
|
|
70
|
+
<dl>
|
|
71
|
+
<dt>id</dt>
|
|
72
|
+
<dd class=monospace>d026ac5994f698dba475681359b6c29d6d39a895484b95e06b7ae49921d80df2i0</dd>
|
|
73
|
+
<dt>address</dt>
|
|
74
|
+
<dd class=monospace>bc1pqapcyswesvccgqsmuncd96ylghs9juthqeshdr8smmh9w7azn8zsghjjar</dd>
|
|
75
|
+
<dt>output value</dt>
|
|
76
|
+
<dd>10000</dd>
|
|
77
|
+
<dt>sat</dt>
|
|
78
|
+
<dd><a href=/sat/1320953397332258>1320953397332258</a></dd>
|
|
79
|
+
<dt>preview</dt>
|
|
80
|
+
<dd><a href=/preview/d026ac5994f698dba475681359b6c29d6d39a895484b95e06b7ae49921d80df2i0>link</a></dd>
|
|
81
|
+
<dt>content</dt>
|
|
82
|
+
<dd><a href=/content/d026ac5994f698dba475681359b6c29d6d39a895484b95e06b7ae49921d80df2i0>link</a></dd>
|
|
83
|
+
<dt>content length</dt>
|
|
84
|
+
<dd>71997 bytes</dd>
|
|
85
|
+
<dt>content type</dt>
|
|
86
|
+
<dd>text/plain;charset=utf-8</dd>
|
|
87
|
+
<dt>timestamp</dt>
|
|
88
|
+
<dd><time>2023-02-11 21:39:00 UTC</time></dd>
|
|
89
|
+
<dt>genesis height</dt>
|
|
90
|
+
<dd><a href=/block/776090>776090</a></dd>
|
|
91
|
+
<dt>genesis fee</dt>
|
|
92
|
+
<dd>273630</dd>
|
|
93
|
+
<dt>genesis transaction</dt>
|
|
94
|
+
<dd><a class=monospace href=/tx/d026ac5994f698dba475681359b6c29d6d39a895484b95e06b7ae49921d80df2>d026ac5994f698dba475681359b6c29d6d39a895484b95e06b7ae49921d80df2</a></dd>
|
|
95
|
+
<dt>location</dt>
|
|
96
|
+
<dd class=monospace>d026ac5994f698dba475681359b6c29d6d39a895484b95e06b7ae49921d80df2:0:0</dd>
|
|
97
|
+
<dt>output</dt>
|
|
98
|
+
<dd><a class=monospace href=/output/d026ac5994f698dba475681359b6c29d6d39a895484b95e06b7ae49921d80df2:0>d026ac5994f698dba475681359b6c29d6d39a895484b95e06b7ae49921d80df2:0</a></dd>
|
|
99
|
+
<dt>offset</dt>
|
|
100
|
+
<dd>0</dd>
|
|
101
|
+
</dl>
|
|
102
|
+
|
|
103
|
+
<dl>
|
|
104
|
+
<dt>id</dt>
|
|
105
|
+
<dd class=monospace>acda637db995df796b35035fd978cc1a947f1e6fd5215968da88b7e38a7e4b37i0</dd>
|
|
106
|
+
<dt>address</dt>
|
|
107
|
+
<dd class=monospace>bc1qx3scwushtwenxtxlnjet4x2w5vg35etdtse5u0</dd>
|
|
108
|
+
<dt>output value</dt>
|
|
109
|
+
<dd>8020</dd>
|
|
110
|
+
<dt>sat</dt>
|
|
111
|
+
<dd><a href=/sat/1883186433806857>1883186433806857</a></dd>
|
|
112
|
+
<dt>preview</dt>
|
|
113
|
+
<dd><a href=/preview/acda637db995df796b35035fd978cc1a947f1e6fd5215968da88b7e38a7e4b37i0>link</a></dd>
|
|
114
|
+
<dt>content</dt>
|
|
115
|
+
<dd><a href=/content/acda637db995df796b35035fd978cc1a947f1e6fd5215968da88b7e38a7e4b37i0>link</a></dd>
|
|
116
|
+
<dt>content length</dt>
|
|
117
|
+
<dd>529 bytes</dd>
|
|
118
|
+
<dt>content type</dt>
|
|
119
|
+
<dd>image/png</dd>
|
|
120
|
+
<dt>timestamp</dt>
|
|
121
|
+
<dd><time>2023-01-31 19:34:47 UTC</time></dd>
|
|
122
|
+
<dt>genesis height</dt>
|
|
123
|
+
<dd><a href=/block/774489>774489</a></dd>
|
|
124
|
+
<dt>genesis fee</dt>
|
|
125
|
+
<dd>5340</dd>
|
|
126
|
+
<dt>genesis transaction</dt>
|
|
127
|
+
<dd><a class=monospace href=/tx/acda637db995df796b35035fd978cc1a947f1e6fd5215968da88b7e38a7e4b37>acda637db995df796b35035fd978cc1a947f1e6fd5215968da88b7e38a7e4b37</a></dd>
|
|
128
|
+
<dt>location</dt>
|
|
129
|
+
<dd class=monospace>6630ff2153985504b180fc16721d559a4cecdc66f4be6acf33509ec2100c0aa5:0:0</dd>
|
|
130
|
+
<dt>output</dt>
|
|
131
|
+
<dd><a class=monospace href=/output/6630ff2153985504b180fc16721d559a4cecdc66f4be6acf33509ec2100c0aa5:0>6630ff2153985504b180fc16721d559a4cecdc66f4be6acf33509ec2100c0aa5:0</a></dd>
|
|
132
|
+
<dt>offset</dt>
|
|
133
|
+
<dd>0</dd>
|
|
134
|
+
</dl>
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
<title>Inscription 407</title>
|
|
139
|
+
|
|
140
|
+
id =
|
|
141
|
+
genesis transaction + offset ???
|
|
142
|
+
|
|
143
|
+
genesis transaction: acda637db995df796b35035fd978cc1a947f1e6fd5215968da88b7e38a7e4b37
|
|
144
|
+
id: acda637db995df796b35035fd978cc1a947f1e6fd5215968da88b7e38a7e4b37i0
|
|
145
|
+
address: bc1qx3scwushtwenxtxlnjet4x2w5vg35etdtse5u0
|
|
146
|
+
output value: 8020
|
|
147
|
+
sat: 1883186433806857
|
|
148
|
+
content length: 529 bytes
|
|
149
|
+
content type: image/png
|
|
150
|
+
timestamp: 2023-01-31 19:34:47 UTC
|
|
151
|
+
genesis height: 774489
|
|
152
|
+
genesis fee: 5340
|
|
153
|
+
genesis transaction: acda637db995df796b35035fd978cc1a947f1e6fd5215968da88b7e38a7e4b37>acda637db995df796b35035fd978cc1a947f1e6fd5215968da88b7e38a7e4b37
|
|
154
|
+
location: 6630ff2153985504b180fc16721d559a4cecdc66f4be6acf33509ec2100c0aa5:0:0
|
|
155
|
+
output: 6630ff2153985504b180fc16721d559a4cecdc66f4be6acf33509ec2100c0aa5:0
|
|
156
|
+
offset: 0
|
|
157
|
+
|
|
158
|
+
=end
|
|
159
|
+
|
|
160
|
+
def inscription( id )
|
|
161
|
+
src = "#{@base}/#{@inscription}/#{id}"
|
|
162
|
+
res = get( src )
|
|
163
|
+
|
|
164
|
+
data = _parse_inscription( res.text )
|
|
165
|
+
data
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _parse_inscription( html )
|
|
170
|
+
doc = Nokogiri::HTML( html )
|
|
171
|
+
|
|
172
|
+
items = []
|
|
173
|
+
|
|
174
|
+
title = doc.css( 'head title' )
|
|
175
|
+
items << ['title', title.text]
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
dls = doc.css( 'body dl' )
|
|
179
|
+
dls[0].css( 'dt,dd' ).each do |el|
|
|
180
|
+
if el.name == 'dt'
|
|
181
|
+
items << [el.text]
|
|
182
|
+
elsif el.name == 'dd'
|
|
183
|
+
items[-1] << el.text
|
|
184
|
+
else
|
|
185
|
+
puts "!! ERROR - unexpected tag; expected dd|dl; got: #{el.name}"
|
|
186
|
+
exit 1
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
items
|
|
190
|
+
|
|
191
|
+
## convert to hash
|
|
192
|
+
## and check for duplicate
|
|
193
|
+
data = {}
|
|
194
|
+
items.each do |k,v|
|
|
195
|
+
k = k.strip
|
|
196
|
+
v = v.strip
|
|
197
|
+
if data.has_key?( k )
|
|
198
|
+
puts "!! ERROR - duplicate key >#{k}< in:"
|
|
199
|
+
pp items
|
|
200
|
+
exit 1
|
|
201
|
+
end
|
|
202
|
+
data[ k ] = v
|
|
203
|
+
end
|
|
204
|
+
data
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def get( src )
|
|
209
|
+
res = Webclient.get( src )
|
|
210
|
+
if res.status.ok?
|
|
211
|
+
res
|
|
212
|
+
else
|
|
213
|
+
puts "!! ERROR - HTTP #{res.status.code} #{res.status.message} - failed web request >#{src}<; sorry"
|
|
214
|
+
exit 1
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end # class Api
|
|
218
|
+
|
|
219
|
+
##
|
|
220
|
+
## add convenience alias
|
|
221
|
+
API = Api
|
|
222
|
+
|
|
223
|
+
end ## module Ordinals
|
|
224
|
+
|
data/lib/ordinals/collection.rb
CHANGED
|
@@ -44,8 +44,8 @@ alias_method :size, :count
|
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
def each_ordinal( &block )
|
|
47
|
-
ordinals.
|
|
48
|
-
block.call( rec )
|
|
47
|
+
ordinals.each_with_index do |rec, i| ## pass along hash rec for now - why? why not?
|
|
48
|
+
block.call( rec, i )
|
|
49
49
|
end
|
|
50
50
|
end
|
|
51
51
|
|
|
@@ -54,9 +54,9 @@ end
|
|
|
54
54
|
|
|
55
55
|
|
|
56
56
|
def each_image( &block )
|
|
57
|
-
each_ordinal do |rec|
|
|
58
|
-
id
|
|
59
|
-
num
|
|
57
|
+
each_ordinal do |rec, i|
|
|
58
|
+
id = rec['id']
|
|
59
|
+
num = rec.has_key?('num') ? rec['num'].to_i(10) : i+1
|
|
60
60
|
|
|
61
61
|
path = "./#{@slug}/#{@width}x#{@height}/#{num}.png"
|
|
62
62
|
img = Image.read( path )
|
|
@@ -78,9 +78,9 @@ end
|
|
|
78
78
|
|
|
79
79
|
|
|
80
80
|
def pixelate
|
|
81
|
-
each_ordinal do |rec|
|
|
82
|
-
id
|
|
83
|
-
num
|
|
81
|
+
each_ordinal do |rec, i|
|
|
82
|
+
id = rec['id']
|
|
83
|
+
num = rec.has_key?('num') ? rec['num'].to_i(10) : i+1
|
|
84
84
|
|
|
85
85
|
outpath = "./#{@slug}/#{@width}x#{@height}/#{num}.png"
|
|
86
86
|
next if File.exist?( outpath )
|
|
@@ -113,14 +113,24 @@ def pixelate
|
|
|
113
113
|
end
|
|
114
114
|
|
|
115
115
|
|
|
116
|
+
|
|
117
|
+
|
|
116
118
|
def make_composite
|
|
117
119
|
cols, rows = case count
|
|
118
120
|
when 10 then [5, 2]
|
|
121
|
+
when 12 then [4, 3]
|
|
119
122
|
when 15 then [5, 3]
|
|
123
|
+
when 20 then [5, 4]
|
|
124
|
+
when 50 then [10, 5]
|
|
120
125
|
when 69 then [10, 7]
|
|
121
|
-
when
|
|
126
|
+
when 80 then [10, 8]
|
|
127
|
+
when 88 then [10, 9]
|
|
128
|
+
when 98,99 then [10, 10]
|
|
122
129
|
when 100 then [10, 10]
|
|
130
|
+
when 101 then [11, 10]
|
|
123
131
|
when 111 then [11, 11]
|
|
132
|
+
when 130 then [10, 13]
|
|
133
|
+
when 512 then [20, 26]
|
|
124
134
|
else
|
|
125
135
|
raise ArgumentError, "sorry - unknown composite count #{count} for now"
|
|
126
136
|
end
|
|
@@ -141,51 +151,103 @@ end
|
|
|
141
151
|
|
|
142
152
|
|
|
143
153
|
def convert_images
|
|
144
|
-
## todo: check for gifs too - why? why not?
|
|
145
154
|
Image.convert( image_dir, from: 'jpg',
|
|
146
155
|
to: 'png' )
|
|
156
|
+
|
|
157
|
+
Image.convert( image_dir, from: 'gif',
|
|
158
|
+
to: 'png' )
|
|
159
|
+
|
|
160
|
+
Image.convert( image_dir, from: 'webp',
|
|
161
|
+
to: 'png' )
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def fix_images ## todo - find a different names for resaving png images?
|
|
167
|
+
## "repair" png images
|
|
168
|
+
## starting w/
|
|
169
|
+
## - why?
|
|
170
|
+
##
|
|
171
|
+
## verify_signature! - ChunkyPNG::SignatureMismatch:
|
|
172
|
+
## PNG signature not found,
|
|
173
|
+
## found "RIFF\\xFE\\b\\x00\\x00"
|
|
174
|
+
## instead of "\\x89PNG\\r\\n\\x1A\\n"
|
|
175
|
+
|
|
176
|
+
Image.convert( image_dir, from: 'png',
|
|
177
|
+
to: 'png' )
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def download_meta ## inscription metadata
|
|
182
|
+
each_ordinal do |rec, i|
|
|
183
|
+
id = rec['id']
|
|
184
|
+
num = rec.has_key?('num') ? rec['num'].to_i(10) : i+1
|
|
185
|
+
|
|
186
|
+
chain = Ordinals.config.chain
|
|
187
|
+
path = "../ordinals.cache/#{chain}/#{id}.json"
|
|
188
|
+
next if File.exist?( path )
|
|
189
|
+
|
|
190
|
+
puts "==> downloading #{chain} inscription meta #{num} w/ id #{id}..."
|
|
191
|
+
|
|
192
|
+
data = Ordinals.client.inscription( id )
|
|
193
|
+
|
|
194
|
+
write_json( path, data )
|
|
195
|
+
|
|
196
|
+
sleep( 1.0 ) ## sleep (delay_in_s)
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def dump_stats
|
|
201
|
+
stats = InscriptionStats.new
|
|
202
|
+
|
|
203
|
+
each_ordinal do |rec, i|
|
|
204
|
+
id = rec['id']
|
|
205
|
+
|
|
206
|
+
chain = Ordinals.config.chain
|
|
207
|
+
path = "../ordinals.cache/#{chain}/#{id}.json"
|
|
208
|
+
|
|
209
|
+
data = read_json( path )
|
|
210
|
+
stats.update( data )
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
pp stats.data
|
|
214
|
+
puts
|
|
215
|
+
puts stats.format
|
|
147
216
|
end
|
|
148
217
|
|
|
149
218
|
|
|
150
219
|
def download_images
|
|
151
|
-
each_ordinal do |rec|
|
|
152
|
-
id
|
|
153
|
-
num
|
|
220
|
+
each_ordinal do |rec, i|
|
|
221
|
+
id = rec['id']
|
|
222
|
+
num = rec.has_key?('num') ? rec['num'].to_i(10) : i+1
|
|
154
223
|
|
|
155
224
|
next if File.exist?( "#{image_dir}/#{num}.png" )
|
|
156
225
|
|
|
157
226
|
puts "==> downloading image ##{num}..."
|
|
158
227
|
|
|
159
|
-
image_url = "https://ordinals.com/content/#{id}"
|
|
160
228
|
|
|
161
|
-
|
|
229
|
+
content = Ordinals.client.content( id )
|
|
162
230
|
|
|
163
|
-
|
|
164
|
-
content_type = res.content_type
|
|
165
|
-
content_length = res.content_length
|
|
231
|
+
puts " content_type: #{content.type}, content_length: #{content.length}"
|
|
166
232
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
format = if content_type =~ %r{image/jpeg}i
|
|
233
|
+
format = if content.type =~ %r{image/jpeg}i
|
|
170
234
|
'jpg'
|
|
171
|
-
elsif
|
|
235
|
+
elsif content.type =~ %r{image/png}i
|
|
172
236
|
'png'
|
|
173
|
-
elsif
|
|
237
|
+
elsif content.type =~ %r{image/gif}i
|
|
174
238
|
'gif'
|
|
239
|
+
elsif content.type =~ %r{image/webp}i
|
|
240
|
+
'webp'
|
|
175
241
|
else
|
|
176
242
|
puts "!! ERROR:"
|
|
177
|
-
puts " unknown image format content type: >#{
|
|
243
|
+
puts " unknown image format content type: >#{content.type}<"
|
|
178
244
|
exit 1
|
|
179
245
|
end
|
|
180
246
|
|
|
181
247
|
## save image - using b(inary) mode
|
|
182
|
-
write_blob( "#{image_dir}/#{num}.#{format}",
|
|
248
|
+
write_blob( "#{image_dir}/#{num}.#{format}", content.blob )
|
|
183
249
|
|
|
184
250
|
sleep( 1.0 ) ## sleep (delay_in_s)
|
|
185
|
-
else
|
|
186
|
-
puts "!! ERROR - failed to download image; sorry - #{res.status.code} #{res.status.message}"
|
|
187
|
-
exit 1
|
|
188
|
-
end
|
|
189
251
|
end
|
|
190
252
|
end
|
|
191
253
|
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
class InscriptionStats
|
|
4
|
+
|
|
5
|
+
TITLE_RX = /^Inscription[ ]+(?<ord>[0-9]+)$/
|
|
6
|
+
BYTES_RX = /^(?<bytes>[0-9]+)[ ]+bytes$/
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
@stats = {
|
|
10
|
+
count: 0,
|
|
11
|
+
ord: { '<1000' => 0,
|
|
12
|
+
'<10000' => 0,
|
|
13
|
+
'<100000' => 0,
|
|
14
|
+
'<1000000' => 0,
|
|
15
|
+
'min' => nil,
|
|
16
|
+
'max' => nil, },
|
|
17
|
+
type: Hash.new(0),
|
|
18
|
+
bytes: { '<100' => 0,
|
|
19
|
+
'<1000' => 0,
|
|
20
|
+
'<10000' => 0,
|
|
21
|
+
'<100000' => 0,
|
|
22
|
+
'<1000000' => 0,
|
|
23
|
+
'min' => nil,
|
|
24
|
+
'max' => nil, },
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@days = Hash.new(0)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def size() @stats[:count]; end
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def update( data )
|
|
35
|
+
data = JSON.parse( data) if data.is_a?( String )
|
|
36
|
+
|
|
37
|
+
@stats[ :count ] += 1
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
ord = nil
|
|
41
|
+
if m=TITLE_RX.match( data['title'] )
|
|
42
|
+
ord = m[:ord].to_i(10)
|
|
43
|
+
else
|
|
44
|
+
puts "!! ERROR - cannot find ord in inscription title; sorry"
|
|
45
|
+
pp data
|
|
46
|
+
exit 1
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
if ord < 1000 then @stats[:ord]['<1000'] += 1
|
|
50
|
+
elsif ord < 10000 then @stats[:ord]['<10000'] += 1
|
|
51
|
+
elsif ord < 100000 then @stats[:ord]['<100000'] += 1
|
|
52
|
+
elsif ord < 1000000 then @stats[:ord]['<1000000'] += 1
|
|
53
|
+
else
|
|
54
|
+
puts "!! ERROR ord out-of-bounds"
|
|
55
|
+
exit 1
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
## update min/max
|
|
59
|
+
@stats[:ord]['min'] = ord if ord < (@stats[:ord]['min'] || 9999999)
|
|
60
|
+
@stats[:ord]['max'] = ord if ord > (@stats[:ord]['max'] || -1)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
bytes = nil
|
|
64
|
+
if m=BYTES_RX.match( data['content length'] )
|
|
65
|
+
bytes = m[:bytes].to_i(10)
|
|
66
|
+
else
|
|
67
|
+
puts "!! ERROR - cannot find bytes in inscription content length; sorry"
|
|
68
|
+
pp data
|
|
69
|
+
exit 1
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
if bytes < 100 then @stats[:bytes]['<100'] += 1
|
|
73
|
+
elsif bytes < 1000 then @stats[:bytes]['<1000'] += 1
|
|
74
|
+
elsif bytes < 10000 then @stats[:bytes]['<10000'] += 1
|
|
75
|
+
elsif bytes < 100000 then @stats[:bytes]['<100000'] += 1
|
|
76
|
+
elsif bytes < 1000000 then @stats[:bytes]['<1000000'] += 1
|
|
77
|
+
else
|
|
78
|
+
puts "!! ERROR bytes (content-length) out-of-bounds"
|
|
79
|
+
exit 1
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
## update min/max
|
|
83
|
+
@stats[:bytes]['min'] = bytes if bytes < (@stats[:bytes]['min'] || 9999999)
|
|
84
|
+
@stats[:bytes]['max'] = bytes if bytes > (@stats[:bytes]['max'] || -1)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
type = data['content type']
|
|
88
|
+
|
|
89
|
+
@stats[:type][ type ] += 1
|
|
90
|
+
|
|
91
|
+
## "timestamp"=>"2023-02-05 01:45:22 UTC",
|
|
92
|
+
ts = Time.strptime( data['timestamp'], '%Y-%m-%d %H:%M:%S' )
|
|
93
|
+
|
|
94
|
+
@days[ ts.strftime('%Y-%m-%d') ] +=1
|
|
95
|
+
end # method update
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def data ## rename to model or such - why? why not?
|
|
99
|
+
## sort days
|
|
100
|
+
days = @days.sort {|l,r| l[0] <=> r[0] }
|
|
101
|
+
## add to stats
|
|
102
|
+
@stats[:days] = {}
|
|
103
|
+
days.each {|k,v| @stats[:days][k] = v }
|
|
104
|
+
@stats
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
=begin
|
|
108
|
+
{:count=>101,
|
|
109
|
+
:ord=>{"<1000"=>0, "<10000"=>0, "<100000"=>101, "<1000000"=>0, "min"=>14343, "max"=>42901},
|
|
110
|
+
:type=>{"image/png"=>101},
|
|
111
|
+
:bytes=>{"<100"=>0, "<1000"=>101, "<10000"=>0, "<100000"=>0, "<1000000"=>0, "min"=>274, "max"=>512},
|
|
112
|
+
:days=>{"2023-02-22"=>10, "2023-02-25"=>17, "2023-02-26"=>74}}
|
|
113
|
+
=end
|
|
114
|
+
|
|
115
|
+
def format ## rename to pretty_print or such - why? why not?
|
|
116
|
+
stat = data
|
|
117
|
+
|
|
118
|
+
buf = String.new('')
|
|
119
|
+
buf << "#{stat[:count]} inscription(s)\n"
|
|
120
|
+
|
|
121
|
+
buf << "- from ##{stat[:ord]['min']} to ##{stat[:ord]['max']} (min. to max.)\n"
|
|
122
|
+
buf << " - <1000 => #{stat[:ord]['<1000']}\n" if stat[:ord]["<1000"] > 0
|
|
123
|
+
buf << " - <10000 => #{stat[:ord]['<10000']}\n" if stat[:ord]["<10000"] > 0
|
|
124
|
+
buf << " - <100000 => #{stat[:ord]['<100000']}\n" if stat[:ord]["<100000"] > 0
|
|
125
|
+
buf << " - <1000000 => #{stat[:ord]['<1000000']}\n" if stat[:ord]["<1000000"] > 0
|
|
126
|
+
|
|
127
|
+
buf << "- format(s)\n"
|
|
128
|
+
stat[:type].each do |k,v|
|
|
129
|
+
buf << " - #{k} => #{v}\n"
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
buf << "- day(s)\n"
|
|
133
|
+
stat[:days].each do |k,v|
|
|
134
|
+
buf << " - #{k} => #{v}\n"
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
buf
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
end ## class InscriptionStats
|
|
141
|
+
|
|
142
|
+
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
module Ordinals
|
|
2
|
+
class Tool
|
|
3
|
+
|
|
4
|
+
def self.main( args=ARGV )
|
|
5
|
+
puts "==> welcome to ordinals/ordbase tool with args:"
|
|
6
|
+
pp args
|
|
7
|
+
|
|
8
|
+
options = {
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
parser = OptionParser.new do |opts|
|
|
12
|
+
opts.on( "--doge", "--dogecoin", "Use Dogecoin / DOGE") do
|
|
13
|
+
## switch to doge
|
|
14
|
+
Ordinals.config.chain = :doge
|
|
15
|
+
end
|
|
16
|
+
opts.on( "--ltc", "--litecoin", "Use Litecoin / LTC") do
|
|
17
|
+
## switch to ltc
|
|
18
|
+
Ordinals.config.chain = :ltc
|
|
19
|
+
end
|
|
20
|
+
opts.on( "--btc", "--bitcoin", "Use Bitcoin / BTC") do
|
|
21
|
+
## switch to btc (default)
|
|
22
|
+
Ordinals.config.chain = :btc
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
opts.on("-h", "--help", "Prints this help") do
|
|
26
|
+
puts opts
|
|
27
|
+
exit
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
parser.parse!( args )
|
|
32
|
+
puts "options:"
|
|
33
|
+
pp options
|
|
34
|
+
|
|
35
|
+
puts "args:"
|
|
36
|
+
pp args
|
|
37
|
+
|
|
38
|
+
if args.size < 1
|
|
39
|
+
puts "!! ERROR - no collection found - use <collection> <command>..."
|
|
40
|
+
puts ""
|
|
41
|
+
exit
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
slug = args[0]
|
|
45
|
+
command = args[1] || 'image'
|
|
46
|
+
|
|
47
|
+
if ['m', 'meta'].include?( command )
|
|
48
|
+
do_download_meta( slug )
|
|
49
|
+
elsif ['stat', 'stats'].include?( command )
|
|
50
|
+
do_dump_stats( slug )
|
|
51
|
+
elsif ['i','img', 'image'].include?( command )
|
|
52
|
+
do_download_images( slug )
|
|
53
|
+
elsif ['conv','convert'].include?( command )
|
|
54
|
+
do_convert_images( slug )
|
|
55
|
+
elsif ['fix'].include?( command )
|
|
56
|
+
do_fix_images( slug )
|
|
57
|
+
elsif ['px','pix', 'pixelate' ].include?( command )
|
|
58
|
+
do_pixelate( slug )
|
|
59
|
+
elsif ['comp','composite' ].include?( command )
|
|
60
|
+
do_make_composite( slug )
|
|
61
|
+
else
|
|
62
|
+
puts "!! ERROR - unknown command >#{command}<, sorry"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
puts "bye"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def self.do_dump_stats( slug )
|
|
70
|
+
puts "==> dump inscription stats for collection >#{slug}<..."
|
|
71
|
+
|
|
72
|
+
col = Collection.new( slug )
|
|
73
|
+
col.dump_stats
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def self.do_download_meta( slug )
|
|
77
|
+
puts "==> download meta for collection >#{slug}<..."
|
|
78
|
+
|
|
79
|
+
col = Collection.new( slug )
|
|
80
|
+
col.download_meta
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def self.do_download_images( slug )
|
|
84
|
+
puts "==> download images for collection >#{slug}<..."
|
|
85
|
+
|
|
86
|
+
col = Collection.new( slug )
|
|
87
|
+
col.download_images
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def self.do_convert_images( slug )
|
|
92
|
+
puts "==> convert images for collection >#{slug}<..."
|
|
93
|
+
|
|
94
|
+
col = Collection.new( slug )
|
|
95
|
+
col.convert_images
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def self.do_fix_images( slug )
|
|
99
|
+
puts "==> fix images for collection >#{slug}<..."
|
|
100
|
+
|
|
101
|
+
col = Collection.new( slug )
|
|
102
|
+
col.fix_images
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def self.do_pixelate( slug )
|
|
107
|
+
puts "==> downsample / pixelate images for collection >#{slug}<..."
|
|
108
|
+
|
|
109
|
+
col = Collection.new( slug )
|
|
110
|
+
col.pixelate
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def self.do_make_composite( slug )
|
|
114
|
+
puts "==> make composite for collection >#{slug}<..."
|
|
115
|
+
|
|
116
|
+
col = Collection.new( slug )
|
|
117
|
+
col.make_composite
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
end # class Tool
|
|
121
|
+
end # module Ordinals
|
data/lib/ordinals.rb
CHANGED
|
@@ -5,91 +5,88 @@ require 'pixelart'
|
|
|
5
5
|
require 'optparse'
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
require_relative 'ordinals/collection'
|
|
11
|
-
|
|
8
|
+
## add nokogiri for api (html parsing)
|
|
9
|
+
require 'nokogiri'
|
|
12
10
|
|
|
13
11
|
|
|
14
|
-
module Ordinals
|
|
15
|
-
class Tool
|
|
16
|
-
|
|
17
|
-
def self.main( args=ARGV )
|
|
18
|
-
puts "==> welcome to ordinals/ordbase tool with args:"
|
|
19
|
-
pp args
|
|
20
12
|
|
|
21
|
-
options = {
|
|
22
|
-
}
|
|
23
13
|
|
|
24
|
-
parser = OptionParser.new do |opts|
|
|
25
14
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
15
|
+
module Ordinals
|
|
16
|
+
class Configuration
|
|
17
|
+
|
|
18
|
+
#######################
|
|
19
|
+
## accessors
|
|
20
|
+
def chain=(value)
|
|
21
|
+
if value.is_a?( String ) || value.is_a?( Symbol )
|
|
22
|
+
case value.downcase.to_s
|
|
23
|
+
when 'btc', 'bitcoin', 'bitcon'
|
|
24
|
+
@chain = 'btc'
|
|
25
|
+
@client = Ordinals::Api.bitcoin
|
|
26
|
+
when 'ltc', 'litecoin', 'litecon'
|
|
27
|
+
@chain = 'ltc'
|
|
28
|
+
@client = Ordinals::Api.litecoin
|
|
29
|
+
when 'doge', 'dogecoin', 'dogecon'
|
|
30
|
+
@chain = 'doge'
|
|
31
|
+
@client = Ordinals::Api.dogecoin
|
|
32
|
+
else
|
|
33
|
+
raise ArgumentError, "unknown chain - expected btc | ltc | doge; got #{value}"
|
|
34
|
+
end
|
|
35
|
+
else
|
|
36
|
+
raise ArgumentError, "only string or symbol supported for now; sorry - got: #{value.inspect} : #{value.class.name}"
|
|
37
|
+
end
|
|
30
38
|
end
|
|
31
39
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
puts "args:"
|
|
37
|
-
pp args
|
|
38
|
-
|
|
39
|
-
if args.size < 1
|
|
40
|
-
puts "!! ERROR - no collection found - use <collection> <command>..."
|
|
41
|
-
puts ""
|
|
42
|
-
exit
|
|
40
|
+
def chain
|
|
41
|
+
## note - default to btc/bitcon if not set
|
|
42
|
+
self.chain = 'btc' unless defined?( @chain )
|
|
43
|
+
@chain
|
|
43
44
|
end
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
elsif ['conv','convert'].include?( command )
|
|
51
|
-
do_convert_images( slug )
|
|
52
|
-
elsif ['px','pixelate' ].include?( command )
|
|
53
|
-
do_pixelate( slug )
|
|
54
|
-
elsif ['comp','composite' ].include?( command )
|
|
55
|
-
do_make_composite( slug )
|
|
56
|
-
else
|
|
57
|
-
puts "!! ERROR - unknown command >#{command}<, sorry"
|
|
46
|
+
## note: read-only for now - why? why not?
|
|
47
|
+
def client
|
|
48
|
+
## note - default to btc/bitcon if not set
|
|
49
|
+
self.chain = 'btc' unless defined?( @client )
|
|
50
|
+
@client
|
|
58
51
|
end
|
|
52
|
+
end # class Configuration
|
|
59
53
|
|
|
60
|
-
puts "bye"
|
|
61
|
-
end
|
|
62
54
|
|
|
55
|
+
## lets you use
|
|
56
|
+
## Ordinals.configure do |config|
|
|
57
|
+
## config.chain = :btc
|
|
58
|
+
## end
|
|
59
|
+
def self.configure() yield( config ); end
|
|
60
|
+
def self.config() @config ||= Configuration.new; end
|
|
63
61
|
|
|
62
|
+
## add some convenience shortcut helpers (no config. required) - why? why not?
|
|
63
|
+
def self.client() config.client; end
|
|
64
|
+
def self.chain() config.chain; end
|
|
64
65
|
|
|
65
|
-
def self.
|
|
66
|
-
|
|
66
|
+
def self.btc?() config.chain == 'btc'; end
|
|
67
|
+
def self.ltc?() config.chain == 'ltc'; end
|
|
68
|
+
def self.doge?() config.chain == 'doge'; end
|
|
69
|
+
class << self
|
|
70
|
+
alias_method :bitcoin?, :btc?
|
|
71
|
+
alias_method :bitcon?, :btc?
|
|
67
72
|
|
|
68
|
-
|
|
69
|
-
|
|
73
|
+
alias_method :litecoin?, :ltc?
|
|
74
|
+
alias_method :litecon?, :ltc?
|
|
75
|
+
|
|
76
|
+
alias_method :dogecoin?, :doge?
|
|
77
|
+
alias_method :dogecon?, :doge?
|
|
70
78
|
end
|
|
79
|
+
end # module Ordinals
|
|
71
80
|
|
|
72
81
|
|
|
73
|
-
def self.do_convert_images( slug )
|
|
74
|
-
puts "==> convert images for collection >#{slug}<..."
|
|
75
82
|
|
|
76
|
-
col = Collection.new( slug )
|
|
77
|
-
col.convert_images
|
|
78
|
-
end
|
|
79
83
|
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
## our own code
|
|
85
|
+
require_relative 'ordinals/api'
|
|
86
|
+
require_relative 'ordinals/collection'
|
|
87
|
+
require_relative 'ordinals/stats'
|
|
82
88
|
|
|
83
|
-
|
|
84
|
-
col.pixelate
|
|
85
|
-
end
|
|
89
|
+
require_relative 'ordinals/tool'
|
|
86
90
|
|
|
87
|
-
def self.do_make_composite( slug )
|
|
88
|
-
puts "==> make composite for collection >#{slug}<..."
|
|
89
91
|
|
|
90
|
-
col = Collection.new( slug )
|
|
91
|
-
col.make_composite
|
|
92
|
-
end
|
|
93
92
|
|
|
94
|
-
end # class Tool
|
|
95
|
-
end # module Ordinals
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ordinals
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 1.0.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: 2023-
|
|
11
|
+
date: 2023-03-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: cocos
|
|
@@ -38,6 +38,20 @@ dependencies:
|
|
|
38
38
|
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: nokogiri
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
41
55
|
- !ruby/object:Gem::Dependency
|
|
42
56
|
name: rdoc
|
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -73,7 +87,7 @@ dependencies:
|
|
|
73
87
|
- !ruby/object:Gem::Version
|
|
74
88
|
version: '3.23'
|
|
75
89
|
description: ordinals gem - "right-clicker" (off-chain) ordinals (pixel art) machinery
|
|
76
|
-
& helpers for Bitcoin & co.
|
|
90
|
+
& helpers for Bitcoin, Litecoin, Dogecoin & co.
|
|
77
91
|
email: wwwmake@googlegroups.com
|
|
78
92
|
executables:
|
|
79
93
|
- ordbase
|
|
@@ -89,7 +103,10 @@ files:
|
|
|
89
103
|
- Rakefile
|
|
90
104
|
- bin/ordbase
|
|
91
105
|
- lib/ordinals.rb
|
|
106
|
+
- lib/ordinals/api.rb
|
|
92
107
|
- lib/ordinals/collection.rb
|
|
108
|
+
- lib/ordinals/stats.rb
|
|
109
|
+
- lib/ordinals/tool.rb
|
|
93
110
|
homepage: https://github.com/pixelartexchange/ordinals.sandbox
|
|
94
111
|
licenses:
|
|
95
112
|
- Public Domain
|
|
@@ -115,5 +132,5 @@ rubygems_version: 3.3.7
|
|
|
115
132
|
signing_key:
|
|
116
133
|
specification_version: 4
|
|
117
134
|
summary: ordinals gem - "right-clicker" (off-chain) ordinals (pixel art) machinery
|
|
118
|
-
& helpers for Bitcoin & co.
|
|
135
|
+
& helpers for Bitcoin, Litecoin, Dogecoin & co.
|
|
119
136
|
test_files: []
|