abibase 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []