abibase 0.0.1

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.
Files changed (7) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +3 -0
  3. data/Manifest.txt +5 -0
  4. data/README.md +31 -0
  5. data/Rakefile +43 -0
  6. data/lib/abibase.rb +390 -0
  7. metadata +158 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 892b874b015e3b8aa49f47fbd86e11a36811b25a9b2dc96f8653ac6cac0ebed8
4
+ data.tar.gz: 245236223c5506416834085bf1999a4253380fbb5c2169e434b309154d574387
5
+ SHA512:
6
+ metadata.gz: 2ea4084b2b2ad686bc18aa789772934ee7ea44d02947d48d4965ec3048963636b08c9e6f46893ae7a5e8d87a0db75b9632b1cf0b1d1abfcd8397e05e6d327943
7
+ data.tar.gz: 6ee51891fc973dc08c1fa373a6c50027a054212aa8ea2fdac116684b3551b141840a251bc2275f549609e92db13c6791a7834f6bca355d16c68412a25e118096
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ### 0.0.1 / 2023-01-28
2
+
3
+ * Everything is new. First release
data/Manifest.txt ADDED
@@ -0,0 +1,5 @@
1
+ CHANGELOG.md
2
+ Manifest.txt
3
+ README.md
4
+ Rakefile
5
+ lib/abibase.rb
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # Application Binary Inteface (ABI) Command Line Tool For Ethereum & Co.
2
+
3
+ abibase - command line tool / helper to manage application binary interfaces (abis)
4
+
5
+
6
+ * home :: [github.com/rubycocos/blockchain](https://github.com/rubycocos/blockchain)
7
+ * bugs :: [github.com/rubycocos/blockchain/issues](https://github.com/rubycocos/blockchain/issues)
8
+ * gem :: [rubygems.org/gems/abibase](https://rubygems.org/gems/abibase)
9
+ * rdoc :: [rubydoc.info/gems/abibase](http://rubydoc.info/gems/abibase)
10
+
11
+
12
+
13
+ ## Usage
14
+
15
+ to be done
16
+
17
+
18
+
19
+
20
+ ## License
21
+
22
+ The scripts are dedicated to the public domain.
23
+ Use it as you please with no restrictions whatsoever.
24
+
25
+
26
+ ## Questions? Comments?
27
+
28
+
29
+ Post them on the [D.I.Y. Punk (Pixel) Art reddit](https://old.reddit.com/r/DIYPunkArt). Thanks.
30
+
31
+
data/Rakefile ADDED
@@ -0,0 +1,43 @@
1
+ require 'hoe'
2
+
3
+
4
+ ###
5
+ # hack/ quick fix for broken intuit_values - overwrite with dummy
6
+ class Hoe
7
+ def intuit_values( input ); end
8
+ end
9
+
10
+
11
+ Hoe.spec 'abibase' do
12
+
13
+ self.version = '0.0.1'
14
+
15
+ self.summary = "abibase - command line tool / helper to manage application binary interfaces (abis)"
16
+ self.description = summary
17
+
18
+ self.urls = { home: 'https://github.com/rubycocos/blockchain' }
19
+
20
+ self.author = 'Gerald Bauer'
21
+ self.email = 'wwwmake@googlegroups.com'
22
+
23
+ # switch extension to .markdown for gihub formatting
24
+ self.readme_file = 'README.md'
25
+ self.history_file = 'CHANGELOG.md'
26
+
27
+ self.extra_deps = [
28
+ ## ['ethlite'],
29
+ ['etherscan-lite'],
30
+ ['abiparser'],
31
+ ['natspec'],
32
+ ['abidoc'],
33
+ ['abigen'],
34
+ ]
35
+
36
+ self.licenses = ['Public Domain']
37
+
38
+ self.spec_extras = {
39
+ required_ruby_version: '>= 2.3'
40
+ }
41
+
42
+ end
43
+
data/lib/abibase.rb ADDED
@@ -0,0 +1,390 @@
1
+ ################
2
+ # download ABIs via Etherscan
3
+
4
+ require 'optparse'
5
+
6
+
7
+ require 'etherscan-lite'
8
+ require 'abidoc'
9
+
10
+
11
+
12
+ def format_code( txt )
13
+ ## {{ to {
14
+ ## and }} to }
15
+ txt = txt.strip.sub( /\A\{\{/, '{').sub( /\}\}\z/, '}' )
16
+
17
+ data = JSON.parse( txt )
18
+ ## pp data
19
+
20
+ language = data['language']
21
+ pp language
22
+ if language != 'Solidity'
23
+ puts "!! ERROR - expected Solidity for language; got: #{language}"
24
+ exit 1
25
+ end
26
+
27
+ sources = data['sources']
28
+ puts " #{sources.size} source(s)"
29
+
30
+ buf = ''
31
+ sources.each do |name, h|
32
+ buf << "///////////////////////////////////////////\n"
33
+ buf << "// File: #{name}\n\n"
34
+ buf << h['content']
35
+ buf << "\n\n"
36
+ end
37
+ buf
38
+ end
39
+
40
+
41
+
42
+
43
+
44
+ class ContractDetailsCache
45
+
46
+ def initialize( path )
47
+ @path = path
48
+ @table = if File.exist?( path )
49
+ build_table( read_csv( path ) )
50
+ else
51
+ {}
52
+ end
53
+ end
54
+
55
+ def [](addr)
56
+ if @table.has_key?( addr )
57
+ @table[ addr ]
58
+ else ## fetch missing data
59
+ data = Etherscan.getcontractdetails( contractaddress: addr )
60
+ ## note: add new data to cache
61
+ @table[ addr ] = data
62
+ data
63
+ end
64
+ end
65
+
66
+
67
+
68
+ def build_table( recs )
69
+ h = {}
70
+ recs.each do |rec|
71
+ ## (re)use contractdetails format / hash keys
72
+ h[ rec['address'] ] = {
73
+ 'contractAddress' => rec['address'],
74
+ 'contractCreator' => rec['creator'],
75
+ 'txHash' => rec['txid'],
76
+ 'blockNumber' => rec['blocknumber'],
77
+ 'timestamp' => rec['timestamp']
78
+ }
79
+ end
80
+ h
81
+ end
82
+
83
+ def save
84
+ ##############
85
+ ## save cache - sort by blocknumber
86
+ entries = @table.values.sort { |l,r| l['blockNumber'].to_i(16) <=> r['blockNumber'].to_i(16) }
87
+ buf = ''
88
+ buf << ['blocknumber', 'timestamp', 'address', 'creator', 'txid'].join( ', ' )
89
+ buf << "\n"
90
+ entries.each do |entry|
91
+ buf << [entry['blockNumber'],
92
+ entry['timestamp'],
93
+ entry['contractAddress'],
94
+ entry['contractCreator'],
95
+ entry['txHash']
96
+ ].join( ', ' )
97
+ buf << "\n"
98
+ end
99
+ write_text( @path, buf )
100
+ end
101
+ end # class ContractDetailsCache
102
+
103
+
104
+
105
+
106
+
107
+
108
+
109
+ module Abi
110
+
111
+ class Tool
112
+
113
+ def self.main( args=ARGV )
114
+ puts "==> welcome to abibase tool with args:"
115
+ pp args
116
+
117
+ options = {}
118
+
119
+ parser = OptionParser.new do |opts|
120
+ opts.on("-h", "--help", "Prints this help") do
121
+ puts opts
122
+ exit
123
+ end
124
+ end
125
+
126
+ parser.parse!( args )
127
+ puts "options:"
128
+ pp options
129
+
130
+ puts "args:"
131
+ pp args
132
+
133
+ command = args[0] || 'download'
134
+
135
+ if ['d', 'dl', 'download'].include?( command )
136
+ do_download_abis
137
+ elsif ['code'].include?( command )
138
+ do_download_code
139
+ elsif ['doc', 'docs'].include?( command )
140
+ do_generate_docs
141
+ elsif ['t', 'time', 'timeline'].include?( command )
142
+ do_generate_timeline # & download contract details (txid,timestamp,etc.)
143
+ else
144
+ puts "!! ERROR - unknown command >#{command}<, sorry"
145
+ end
146
+
147
+ puts "bye"
148
+ end
149
+
150
+
151
+
152
+
153
+
154
+ def self.do_download_abis
155
+ puts "==> download abis..."
156
+
157
+ recs = read_csv( "./contracts.csv" )
158
+ puts " #{recs.size} record(s)"
159
+
160
+ delay_in_s = 1
161
+
162
+ recs.each_with_index do |rec,i|
163
+ addr = rec['address'].downcase
164
+ names = rec['names']
165
+ puts "==> [#{i+1}/#{recs.size}] #{names} @ #{addr}..."
166
+
167
+ outpath = "./address/#{addr}/abi.json"
168
+
169
+ if File.exist?( outpath )
170
+ # already download / found in cache
171
+ else
172
+ puts " sleeping in #{delay_in_s} sec(s)..."
173
+ sleep( delay_in_s )
174
+ data = Etherscan.getabi( address: addr )
175
+ pp data ## note: returns abi data as a json string (parse again!!)
176
+ abi = JSON.parse( data )
177
+ pp abi
178
+
179
+ write_json( outpath, abi )
180
+ end
181
+ end
182
+ end
183
+
184
+
185
+
186
+
187
+ def self.do_download_code
188
+ puts "==> download (source) code..."
189
+
190
+ recs = read_csv( "./contracts.csv" )
191
+ puts " #{recs.size} record(s)"
192
+
193
+ delay_in_s = 1
194
+
195
+ recs.each_with_index do |rec,i|
196
+ addr = rec['address'].downcase
197
+ names = rec['names']
198
+ puts "==> [#{i+1}/#{recs.size}] #{names} @ #{addr}..."
199
+
200
+ outpath_code = "./address/#{addr}/contract.sol"
201
+ outpath_meta = "./address/#{addr}/contract.yml"
202
+
203
+ if File.exist?( outpath_code )
204
+ # already download / found in cache
205
+ else
206
+ puts " sleeping in #{delay_in_s} sec(s)..."
207
+ sleep( delay_in_s )
208
+
209
+ data = Etherscan.getsourcecode( address: addr )
210
+ pp data ## note: returns abi data as a json string (parse again!!)
211
+
212
+ ## note: returns an array
213
+ if data.size != 1
214
+ puts "!! ERROR - expected array of size 1; got #{data.size}"
215
+ exit 1
216
+ end
217
+
218
+ code = data[0]['SourceCode']
219
+
220
+ ## note: unroll multi-file format if present (starts with {{ .. }})
221
+ code = format_code( code ) if code.start_with?( /[ \t\n\r]*\{\{/ )
222
+
223
+
224
+ ## fix: use "universal new line or such ?? - why lines get duplicated??"
225
+ ## hack: use write_blob
226
+ write_blob( outpath_code, code )
227
+
228
+ ## remove SourceCode & ABI entries
229
+ data[0].delete('SourceCode')
230
+ data[0].delete('ABI')
231
+ puts "meta:"
232
+ pp data[0]
233
+
234
+ ## save rest (remaining) as yml
235
+ write_text( outpath_meta, YAML.dump( data[0] ))
236
+ end
237
+ end
238
+ end
239
+
240
+
241
+
242
+ def self.do_generate_docs
243
+ puts "==> generate docs..."
244
+
245
+ paths = Dir.glob( "./address/**/abi.json" )
246
+ ## paths = paths[0..2]
247
+ paths.each do |path|
248
+ basename = File.basename( File.dirname( path ))
249
+
250
+ abi = ABI.read( path )
251
+
252
+ natspec = if File.exist?( "./address/#{basename}/contract.md" )
253
+ Natspec.read( "./address/#{basename}/contract.md" )
254
+ else
255
+ nil
256
+ end
257
+
258
+ buf = abi.generate_doc( title: "Contract ABI - #{basename}",
259
+ natspec: natspec )
260
+ puts buf
261
+ write_text( "./address/#{basename}/README.md", buf )
262
+
263
+ buf = abi.generate_interface( name: '' ) # solidity interface declarations (source code)
264
+ write_text( "./address/#{basename}/interface.sol", buf )
265
+ end
266
+ end
267
+
268
+
269
+ def self.do_generate_timeline
270
+ puts "==> generate timeline..."
271
+
272
+ ## collection all addresses
273
+ addresses = []
274
+ paths = Dir.glob( "./address/**/abi.json" )
275
+ ## paths = paths[0..2]
276
+ paths.each do |path|
277
+ basename = File.basename( File.dirname( path ))
278
+
279
+ addresses << basename.downcase
280
+ end
281
+
282
+ pp addresses
283
+ puts " #{addresses.size} record(s)"
284
+
285
+ ## update contractdetails.csv
286
+ ## build cache
287
+ cache = ContractDetailsCache.new( './contractdetails.csv' )
288
+
289
+ recs = []
290
+ addresses.each_with_index do |addr,i|
291
+ rec = cache[ addr ]
292
+ recs << rec
293
+ end
294
+ ### note: save back contractdetails cache
295
+ cache.save
296
+
297
+ ############
298
+ ## sort by blocknumber (reverse chronological)
299
+ recs = recs.sort do |l,r|
300
+ l['blockNumber'].to_i(16) <=> r['blockNumber'].to_i(16)
301
+ end
302
+
303
+ pp recs
304
+
305
+ ## create report / page
306
+ buf = <<TXT
307
+ # Awesome (Ethereum) Contracts / Blockchain Services
308
+
309
+ Cache of (ethereum) contract ABIs (application binary interfaces)
310
+ and open source code (if verified / available)
311
+
312
+
313
+ TXT
314
+
315
+ meta = read_csv( './contracts.csv')
316
+ ## build name lookup by address
317
+ contracts = meta.reduce( {} ) {|h,rec| h[rec['address']]=rec['names']; h }
318
+ pp contracts
319
+
320
+ recs.each_with_index do |rec,i|
321
+ addr = rec['contractAddress'].downcase
322
+ timestamp = rec['timestamp'].to_i(16)
323
+ date = Time.at( timestamp).utc
324
+
325
+ tooltip = date.strftime('%b %-d, %Y')
326
+
327
+ names = contracts[addr]
328
+ if names.nil?
329
+ puts "!! ERROR: no name found for contract; sorry:"
330
+ pp rec
331
+ exit 1
332
+ end
333
+
334
+ names = names.split( '|' )
335
+ names = names.map { |name| name.gsub( /[ \t]+/, ' ' ).strip }
336
+ name = names[0]
337
+
338
+ buf << " · " if i > 0
339
+ buf << %Q{[#{name}](address/#{addr} "#{tooltip}")}
340
+ buf << "\n"
341
+ end
342
+
343
+
344
+ buf << "\n"
345
+ buf << "## Timeline\n\n"
346
+
347
+ recs.each do |rec|
348
+
349
+ addr = rec['contractAddress']
350
+ creator = rec['contractCreator']
351
+ txid = rec['txHash']
352
+ blocknumber = rec['blockNumber'].to_i(16)
353
+ timestamp = rec['timestamp'].to_i(16)
354
+ date = Time.at( timestamp).utc
355
+
356
+ names = contracts[addr]
357
+ if names.nil?
358
+ puts "!! ERROR: no name found for contract; sorry:"
359
+ pp rec
360
+ exit 1
361
+ end
362
+
363
+ names = names.split( '|' )
364
+ names = names.map { |name| name.gsub( /[ \t]+/, ' ' ).strip }
365
+ name = names[0]
366
+
367
+ buf << "### #{names.join( ' | ')} - #{date.strftime('%b %-d, %Y')}\n\n"
368
+
369
+ buf << "contract @ [**#{addr}**](address/#{addr})"
370
+ buf << " - [Etherscan](https://etherscan.io/address/#{addr})"
371
+ buf << ", [Bloxy](https://bloxy.info/address/#{addr})"
372
+ ## buf << ", [ABIDocs](https://abidocs.dev/contracts/#{addr})"
373
+ buf << "\n\n"
374
+
375
+ # buf << "created by [#{creator}](https://etherscan.io/address/#{creator}))"
376
+ # buf << " at block no. #{blocknumber} (#{date})"
377
+ # buf << " - txid [#{txid}](https://etherscan.io/tx/#{txid})"
378
+ # buf << "\n\n"
379
+ end
380
+
381
+
382
+ write_text( './README.md', buf )
383
+
384
+
385
+
386
+
387
+
388
+ end
389
+ end # class Tool
390
+ end # module Abi
metadata ADDED
@@ -0,0 +1,158 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: abibase
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Gerald Bauer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-01-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: etherscan-lite
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: abiparser
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: natspec
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'
55
+ - !ruby/object:Gem::Dependency
56
+ name: abidoc
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: abigen
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rdoc
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '4.0'
90
+ - - "<"
91
+ - !ruby/object:Gem::Version
92
+ version: '7'
93
+ type: :development
94
+ prerelease: false
95
+ version_requirements: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '4.0'
100
+ - - "<"
101
+ - !ruby/object:Gem::Version
102
+ version: '7'
103
+ - !ruby/object:Gem::Dependency
104
+ name: hoe
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '3.23'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '3.23'
117
+ description: abibase - command line tool / helper to manage application binary interfaces
118
+ (abis)
119
+ email: wwwmake@googlegroups.com
120
+ executables: []
121
+ extensions: []
122
+ extra_rdoc_files:
123
+ - CHANGELOG.md
124
+ - Manifest.txt
125
+ - README.md
126
+ files:
127
+ - CHANGELOG.md
128
+ - Manifest.txt
129
+ - README.md
130
+ - Rakefile
131
+ - lib/abibase.rb
132
+ homepage: https://github.com/rubycocos/blockchain
133
+ licenses:
134
+ - Public Domain
135
+ metadata: {}
136
+ post_install_message:
137
+ rdoc_options:
138
+ - "--main"
139
+ - README.md
140
+ require_paths:
141
+ - lib
142
+ required_ruby_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '2.3'
147
+ required_rubygems_version: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ requirements: []
153
+ rubygems_version: 3.3.7
154
+ signing_key:
155
+ specification_version: 4
156
+ summary: abibase - command line tool / helper to manage application binary interfaces
157
+ (abis)
158
+ test_files: []