Class: TEALrb::Contract
Constant Summary
collapse
- VOID_OPS =
%w[assert err return app_global_put b bnz bz store
stores app_local_put app_global_del app_local_del callsub retsub
log itxn_submit itxn_next].freeze
Constants included
from TEALrb
MAINNET
Class Attribute Summary collapse
Instance Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
Methods included from ABI
#abi_return
#byte_b32, #byte_b64
Methods included from MaybeOps
#acct_has_balance?, #acct_param_value, #app_global_ex_exists?, #app_global_ex_value, #app_local_ex_exists?, #app_local_ex_value, #app_param_exists?, #app_param_value, #asset_holding_exists?, #asset_holding_value, #asset_param_exists?, #asset_param_value, #box_exists?, #box_len_exists?, #box_len_value, #box_value
#acct_params_get, #add, #addr, #addw, #app_global_del, #app_global_get, #app_global_get_ex, #app_global_put, #app_local_del, #app_local_get, #app_local_get_ex, #app_local_put, #app_opted_in, #app_params_get, #approve, #arg, #arg_0, #arg_1, #arg_2, #arg_3, #args, #assert, #asset_holding_get, #asset_params_get, #b, #balance, #base32, #base64_decode, #big_endian_add, #big_endian_divide, #big_endian_equal, #big_endian_less, #big_endian_less_eq, #big_endian_modulo, #big_endian_more, #big_endian_more_eq, #big_endian_multiply, #big_endian_not_equal, #big_endian_subtract, #bitlen, #bitwise_and, #bitwise_byte_invert, #bitwise_invert, #bitwise_or, #bitwise_xor, #bnz, #boolean_and, #boolean_or, #box_create, #box_del, #box_extract, #box_get, #box_len, #box_put, #box_replace, #bsqrt, #btoi, #bury, #byte, #bytec, #bytec_0, #bytec_1, #bytec_2, #bytec_3, #bytecblock, #bz, #bzero, #callsub, #concat, #cover, #dig, #divide, #divmodw, #divw, #dup, #dup2, #dupn, #ecdsa_pk_decompress, #ecdsa_pk_recover, #ecdsa_verify, #ed25519verify, #ed25519verify_bare, #equal, #err, #exp, #expw, #extract, #extract3, #extract_uint16, #extract_uint32, #extract_uint64, #frame_bury, #frame_dig, #gaid, #gaids, #getbit, #getbyte, #gitxn, #gitxna, #gitxnas, #gload, #gloads, #gloadss, #greater, #greater_eq, #gtxn, #gtxna, #gtxnas, #gtxns, #gtxnsa, #gtxnsas, #int, #intc, #intc_0, #intc_1, #intc_2, #intc_3, #intcblock, #itob, #itxn_begin, #itxn_field, #itxn_next, #itxn_submit, #itxna, #itxnas, #json_ref, #keccak256, #label, #len, #less, #less_eq, #load, #loads, #log, #match, #method_signature, #min_balance, #modulo, #multiply, #mulw, #not_equal, #padded_bitwise_and, #padded_bitwise_or, #padded_bitwise_xor, #pop, #popn, #proto, #pushbytes, #pushbytess, #pushint, #pushints, #replace, #replace2, #retsub, #select, #setbit, #setbyte, #sha256, #sha3_256, #sha512_256, #shl, #shr, #sqrt, #store, #stores, #substring, #substring3, #subtract, #swap, #switch, #teal_return, #txn, #txna, #txnas, #uncover, #vrf_verify, #zero?
Constructor Details
sets the ‘#pragma version`, defines teal methods, and defines subroutines
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
# File 'lib/tealrb/contract.rb', line 90
def initialize
self.class.parse(self.class)
@teal = TEAL.new ["#pragma version #{self.class.version}"], self
@scratch = Scratch.new self
@contract = self
@if_count = 0
self.class.method_hashes.each do |mh|
define_subroutine(mh[:name], method(mh[:name]))
end
compile
end
|
Class Attribute Details
.abi_interface ⇒ Object
Returns the value of attribute abi_interface.
19
20
21
|
# File 'lib/tealrb/contract.rb', line 19
def abi_interface
@abi_interface
end
|
.debug ⇒ Object
Returns the value of attribute debug.
19
20
21
|
# File 'lib/tealrb/contract.rb', line 19
def debug
@debug
end
|
.disable_abi_routing ⇒ Object
Returns the value of attribute disable_abi_routing.
19
20
21
|
# File 'lib/tealrb/contract.rb', line 19
def disable_abi_routing
@disable_abi_routing
end
|
.method_hashes ⇒ Object
Returns the value of attribute method_hashes.
19
20
21
|
# File 'lib/tealrb/contract.rb', line 19
def method_hashes
@method_hashes
end
|
.src_map ⇒ Object
Returns the value of attribute src_map.
19
20
21
|
# File 'lib/tealrb/contract.rb', line 19
def src_map
@src_map
end
|
.subroutines ⇒ Object
Returns the value of attribute subroutines.
19
20
21
|
# File 'lib/tealrb/contract.rb', line 19
def subroutines
@subroutines
end
|
.version ⇒ Object
Returns the value of attribute version.
19
20
21
|
# File 'lib/tealrb/contract.rb', line 19
def version
@version
end
|
Instance Attribute Details
#eval_location ⇒ Object
Returns the value of attribute eval_location.
15
16
17
|
# File 'lib/tealrb/contract.rb', line 15
def eval_location
@eval_location
end
|
#if_count ⇒ Object
Returns the value of attribute if_count.
16
17
18
|
# File 'lib/tealrb/contract.rb', line 16
def if_count
@if_count
end
|
#teal ⇒ Object
Returns the value of attribute teal.
16
17
18
|
# File 'lib/tealrb/contract.rb', line 16
def teal
@teal
end
|
Class Method Details
.inherited(klass) ⇒ Object
22
23
24
25
26
27
28
29
30
31
32
|
# File 'lib/tealrb/contract.rb', line 22
def inherited(klass)
klass.version = 6
klass.subroutines = {}
klass.abi_interface = ABI::ABIDescription.new
klass.abi_interface.name = klass.to_s
klass.method_hashes = []
klass.debug = false
klass.disable_abi_routing = false
klass.src_map = true
super
end
|
.parse(klass) ⇒ Object
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
# File 'lib/tealrb/contract.rb', line 34
def parse(klass)
YARD::Tags::Library.define_tag('ABI Method', :abi)
YARD::Tags::Library.define_tag('Subroutine', :subroutine)
YARD::Tags::Library.define_tag('OnCompletes', :on_completion)
YARD::Tags::Library.define_tag('Create', :create)
YARD.parse Object.const_source_location(klass.to_s).first
parsed_methods = {}
YARD::Registry.all.each do |y|
next unless y.type == :method
next unless klass.instance_methods.include? y.name
next if y.parent.type == :class && y.parent.to_s != klass.to_s
if parsed_methods.keys.include? y.name
raise "#{y.name} defined in two locations: \n #{parsed_methods[y.name]}\n #{y.file}:#{y.line}"
end
parsed_methods[y.name] = "#{y.file}:#{y.line}"
tags = y.tags.map(&:tag_name)
next unless tags.include?('abi') || tags.include?('subroutine')
method_hash = { name: y.name.to_s, desc: y.base_docstring, args: [], returns: { type: 'void' },
on_completion: ['NoOp'], create: y.has_tag?('create') }
y.tags.each do |t|
method_hash[:returns] = { type: t.types&.first&.downcase } if t.tag_name == 'return'
method_hash[:on_completion] = t.text[1..-2].split(',').map(&:strip) if t.tag_name == 'on_completion'
next unless t.tag_name == 'param'
method_hash[:args] << { name: t.name, type: t.types&.first&.downcase, desc: t.text }
end
klass.method_hashes << method_hash
klass.abi_interface.add_method(**method_hash) if tags.include? 'abi'
end
end
|
Instance Method Details
#abi_hash ⇒ Object
the hash of the abi description
367
368
369
|
# File 'lib/tealrb/contract.rb', line 367
def abi_hash
self.class.abi_interface.to_h
end
|
#account(_account = nil) ⇒ Object
Also known as:
accounts
114
115
116
|
# File 'lib/tealrb/contract.rb', line 114
def account(_account = nil)
@account ||= Account.new self
end
|
#app(_app = nil) ⇒ Object
Also known as:
apps
120
121
122
|
# File 'lib/tealrb/contract.rb', line 120
def app(_app = nil)
@app ||= App.new self
end
|
#app_args ⇒ Object
162
163
164
|
# File 'lib/tealrb/contract.rb', line 162
def app_args
AppArgs.new self
end
|
#asset(_asset = nil) ⇒ Object
Also known as:
assets
126
127
128
|
# File 'lib/tealrb/contract.rb', line 126
def asset(_asset = nil)
@asset ||= Asset.new self
end
|
#box ⇒ Object
150
151
152
|
# File 'lib/tealrb/contract.rb', line 150
def box
Box.new self
end
|
insert comment into TEAL source
350
351
352
353
354
355
356
357
358
|
# File 'lib/tealrb/contract.rb', line 350
def (content, inline: false)
content = " #{content}" unless content[0] == ' '
if inline
last_line = @teal.pop
@teal << "#{last_line} //#{content}"
else
@teal << "//#{content}"
end
end
|
#compile ⇒ Object
transpiles #main and routes abi methods. To disable abi routing, set ‘@disable_abi_routing` to true in your Contract subclass
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
|
# File 'lib/tealrb/contract.rb', line 381
def compile
@teal << 'main:' if @teal.include? 'b main'
route_abi_methods unless self.class.disable_abi_routing
return unless respond_to? :main
@eval_location = method(:main).source_location
eval_tealrb(
rewrite(
method(:main).source,
method_rewriter: true
),
debug_context: 'main'
)
end
|
#compile_string(string) ⇒ nil
transpiles the given string to TEAL
374
375
376
377
|
# File 'lib/tealrb/contract.rb', line 374
def compile_string(string)
eval_tealrb(rewrite(string), debug_context: 'compile_string')
nil
end
|
#compiled_program ⇒ Object
401
402
403
404
405
406
407
|
# File 'lib/tealrb/contract.rb', line 401
def compiled_program
compile_response = Algod.new.compile(formatted_teal)
generate_source_map(formatted_teal) if compile_response.status != 200
JSON.parse(compile_response.body)['result']
end
|
#define_subroutine(name, definition) ⇒ nil
defines a TEAL subroutine
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
|
# File 'lib/tealrb/contract.rb', line 320
def define_subroutine(name, definition)
return if method(name).source_location.first == __FILE__
@eval_location = method(name).source_location
define_singleton_method(name) do |*_args|
callsub(name)
end
@teal << 'b main' unless @teal.include? 'b main'
label(name)
= definition.parameters.map(&:last).join(', ')
= "#{name}(#{comment_params})"
(, inline: true)
method_hash = self.class.method_hashes.find { _1[:name] == name.to_s }
new_source = generate_subroutine_source(definition, method_hash)
new_source = "#{new_source}retsub"
eval_tealrb(new_source, debug_context: "subroutine: #{name}")
nil
end
|
#dump(directory = Dir.pwd, name: self.class.to_s.downcase, abi: true, src_map: true) ⇒ Object
236
237
238
239
240
241
242
243
244
245
246
|
# File 'lib/tealrb/contract.rb', line 236
def dump(directory = Dir.pwd, name: self.class.to_s.downcase, abi: true, src_map: true)
src = formatted_teal
File.write(File.join(directory, "#{name}.teal"), src)
File.write(File.join(directory, "#{name}.abi.json"), JSON.pretty_generate(abi_hash)) if abi
return unless src_map
src_map_json = JSON.pretty_generate(generate_source_map(src))
File.write(File.join(directory, "#{name}.src_map.json"), src_map_json)
end
|
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
|
# File 'lib/tealrb/contract.rb', line 248
def formatted_teal
new_lines = []
= []
@teal.compact.each do |ln|
ln = ln.strip
next if ln.empty?
if ln[/^#pragma/]
new_lines << { text: ln, void: true, comments: }
= []
elsif ln[%r{^//}]
<< ln
else
op = ln.split.first
label_regex = %r{\S+:($| //)}
label_type = label_number = label_end = nil
if ln[label_regex]
label_type = ln[/^if/] || ln[/^while/]
label_number = ln[/(?<=^if)\d+/] || ln[/(?<=^while)\d+/]
label_end = ln[/_end:/]
end
new_lines << { text: ln, void: VOID_OPS.include?(op), comments: , label: ln[label_regex],
label_type: label_type, label_number: label_number, label_end: label_end }
= []
end
end
output = []
indent_level = 0
current_labels = { 'while' => [], 'if' => [] }
new_lines.each do |ln|
indent_level = 1 if ln[:label] && indent_level.zero? && !ln[:label_type]
if ln[:label_type]
indent_level += 1 unless current_labels[ln[:label_type]].include?(ln[:label_number])
current_labels[ln[:label_type]] << ln[:label_number]
end
ln_indent_level = indent_level
ln_indent_level -= 1 if ln[:label]
output << '' if !output.last&.empty? && (ln[:label] || ln[:comments].any?)
ln[:comments].each { output << (("\t" * ln_indent_level) + _1) }
output << (("\t" * ln_indent_level) + ln[:text])
output << '' if ln[:void] && !output.last.empty?
next unless ln[:label_end]
indent_level -= 1
current_labels[ln[:label_type]].delete ln[:label_number]
end
output.join("\n")
end
|
#generate_source_map(src) ⇒ Object
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
|
# File 'lib/tealrb/contract.rb', line 174
def generate_source_map(src)
last_location = nil
src_map_hash = {}
src.each_line.with_index do |ln, i|
if ln[/src_map:/]
last_location = ln[/(?<=src_map:)\S+/]
next
end
src_map_hash[i + 1] ||= { location: last_location } if last_location
end
compile_response = Algod.new.compile(src)
case compile_response.status
when 400
msg = JSON.parse(compile_response.body)['message']
e_msg = StringIO.new
e_msg.puts 'Error(s) while attempting to compile TEAL'
msg.each_line do |ln|
teal_line = ln.split(':').first.to_i
ln_msg = ln.split(':')[1..].join(':').strip
next if ln_msg == '"0"'
if src_map_hash[teal_line]
e_msg.puts " #{teal_line} - #{src_map_hash[teal_line][:location]}: #{ln_msg}"
else
e_msg.puts " #{ln}"
end
end
raise e_msg.string
when 200
json_body = JSON.parse(compile_response.body)
pc_mapping = json_body['sourcemap']['mapping']
pc_array = pc_mapping.split(';').map do |v|
SourceMap::VLQ.decode_array(v)[2]
end
last_line = 1
line_to_pc = {}
pc_to_line = {}
pc_array.each_with_index do |line_delta, pc|
last_line += line_delta.to_i
next if last_line == 1
line_to_pc[last_line] ||= []
line_to_pc[last_line] << pc
pc_to_line[pc] = last_line
end
line_to_pc.each do |line, pcs|
src_map_hash[line][:pcs] = pcs if src_map_hash[line]
end
end
src_map_hash
end
|
#global(field = nil, *_args) ⇒ Object
138
139
140
141
142
143
144
|
# File 'lib/tealrb/contract.rb', line 138
def global(field = nil, *_args)
if field
global_opcode(field)
else
@global ||= Global.new self
end
end
|
#global_opcode ⇒ Object
13
|
# File 'lib/tealrb/contract.rb', line 13
alias global_opcode global
|
#group_txn(_group_txn = nil) ⇒ Object
Also known as:
group_txns
132
133
134
|
# File 'lib/tealrb/contract.rb', line 132
def group_txn(_group_txn = nil)
@group_txn ||= GroupTxn.new self
end
|
#inner_txn ⇒ Object
154
155
156
|
# File 'lib/tealrb/contract.rb', line 154
def inner_txn
InnerTxn.new self
end
|
#local ⇒ Object
166
167
168
|
# File 'lib/tealrb/contract.rb', line 166
def local
Local.new self
end
|
#logs ⇒ Object
158
159
160
|
# File 'lib/tealrb/contract.rb', line 158
def logs
Logs.new self
end
|
#main ⇒ Object
397
398
399
|
# File 'lib/tealrb/contract.rb', line 397
def main
nil
end
|
#placeholder(string) ⇒ Object
inserts a string into TEAL source
362
363
364
|
# File 'lib/tealrb/contract.rb', line 362
def placeholder(string)
@teal << string
end
|
#rb(input) ⇒ Object
return the input without transpiling to TEAL
312
313
314
|
# File 'lib/tealrb/contract.rb', line 312
def rb(input)
input
end
|
#teal_source ⇒ Object
110
111
112
|
# File 'lib/tealrb/contract.rb', line 110
def teal_source
@teal.compact.join("\n")
end
|
#this_txn ⇒ Object
146
147
148
|
# File 'lib/tealrb/contract.rb', line 146
def this_txn
ThisTxn.new self
end
|
#txn_type ⇒ Object
170
171
172
|
# File 'lib/tealrb/contract.rb', line 170
def txn_type
TxnType.new self
end
|