Class: ABI::Encoder
- Inherits:
-
Object
- Object
- ABI::Encoder
- Defined in:
- lib/abicoder/encoder.rb
Instance Method Summary collapse
- #ceil32(x) ⇒ Object
-
#encode(types, args) ⇒ Object
Encodes multiple arguments using the head/tail mechanism.
- #encode_address(arg) ⇒ Object
- #encode_bool(arg) ⇒ Object
- #encode_bytes(arg, length = nil) ⇒ Object
- #encode_dynamic_array(type, args) ⇒ Object
- #encode_int(arg, bits) ⇒ Object
- #encode_primitive_type(type, arg) ⇒ Object
- #encode_static_array(type, args) ⇒ Object
- #encode_string(arg) ⇒ Object
-
#encode_tuple(tuple, args) ⇒ Object
todo/check: if static tuple gets encoded different without offset (head/tail).
-
#encode_type(type, arg) ⇒ String
Encodes a single value (static or dynamic).
- #encode_uint(arg, bits) ⇒ Object
- #encode_uint256(arg) ⇒ Object
-
#lpad(bin) ⇒ Object
rename to lpad32 or such - why? why not?.
-
#lpad_hex(hex) ⇒ Object
rename to lpad32_hex or such - why? why not?.
-
#lpad_int(n) ⇒ Object
rename to lpad32_int or such - why? why not?.
-
#rpad(bin, l = 32) ⇒ Object
encoding helpers / utils with “hard-coded” fill symbol as BYTE_ZERO.
Instance Method Details
#ceil32(x) ⇒ Object
268 269 270 |
# File 'lib/abicoder/encoder.rb', line 268 def ceil32(x) x % 32 == 0 ? x : (x + 32 - x%32) end |
#encode(types, args) ⇒ Object
Encodes multiple arguments using the head/tail mechanism.
returns binary string (with BINARY / ASCII_8BIT encoding)
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/abicoder/encoder.rb', line 23 def encode( types, args ) ## enforce args.size and types.size must match - why? why not? raise ArgumentError, "Wrong number of args: found #{args.size}, expecting #{types.size}" unless args.size == types.size ## for convenience check if types is a String ## otherwise assume ABI::Type already types = types.map { |type| type.is_a?( Type ) ? type : Type.parse( type ) } ## todo/check: use args.map (instead of types) ## might allow encoding less args than types? - why? why not? head_size = types .map {|type| type.size || 32 } .sum head, tail = ''.b, ''.b ## note: use string buffer with BINARY / ASCII_8BIT encoding!!! types.each_with_index do |type, i| if type.dynamic? head += encode_uint256( head_size + tail.size ) tail += encode_type( type, args[i] ) else head += encode_type( type, args[i] ) end end head + tail end |
#encode_address(arg) ⇒ Object
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
# File 'lib/abicoder/encoder.rb', line 211 def encode_address( arg ) if arg.is_a?( Integer ) lpad_int( arg ) elsif arg.size == 20 ## note: make sure encoding is always binary!!! arg = arg.b if arg.encoding != Encoding::BINARY lpad( arg ) elsif arg.size == 40 lpad_hex( arg ) elsif arg.size == 42 && arg[0,2] == '0x' ## todo/fix: allow 0X too - why? why not? lpad_hex( arg[2..-1] ) else raise EncodingError, "Could not parse address: #{arg}" end end |
#encode_bool(arg) ⇒ Object
158 159 160 161 162 |
# File 'lib/abicoder/encoder.rb', line 158 def encode_bool( arg ) ## raise EncodingError or ArgumentError - why? why not? raise ArgumentError, "arg is not bool: #{arg}" unless arg.is_a?(TrueClass) || arg.is_a?(FalseClass) lpad( arg ? BYTE_ONE : BYTE_ZERO ) ## was lpad_int( arg ? 1 : 0 ) end |
#encode_bytes(arg, length = nil) ⇒ Object
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
# File 'lib/abicoder/encoder.rb', line 193 def encode_bytes( arg, length=nil ) ## raise EncodingError or ArgumentError - why? why not? raise EncodingError, "Expecting string: #{arg}" unless arg.is_a?(::String) arg = arg.b if arg.encoding != Encoding::BINARY if length # fixed length type raise ValueOutOfBounds, "invalid bytes length #{length}" if arg.size > length raise ValueOutOfBounds, "invalid bytes length #{length}" if length < 0 || length > 32 rpad( arg ) else # variable length type (if length is nil) raise ValueOutOfBounds, "Integer invalid or out of range: #{arg.size}" if arg.size > UINT_MAX size = lpad_int( arg.size ) value = rpad( arg, ceil32(arg.size) ) size + value end end |
#encode_dynamic_array(type, args) ⇒ Object
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/abicoder/encoder.rb', line 74 def encode_dynamic_array( type, args ) raise ArgumentError, "arg must be an array" unless args.is_a?(::Array) head, tail = ''.b, ''.b ## note: use string buffer with BINARY / ASCII_8BIT encoding!!! if type.is_a?( Array ) ## dynamic array head += encode_uint256( args.size ) else ## fixed array raise ArgumentError, "Wrong array size: found #{args.size}, expecting #{type.dim}" unless args.size == type.dim end subtype = type.subtype args.each do |arg| if subtype.dynamic? head += encode_uint256( 32*args.size + tail.size ) tail += encode_type( subtype, arg ) else head += encode_type( subtype, arg ) end end head + tail end |
#encode_int(arg, bits) ⇒ Object
173 174 175 176 177 178 |
# File 'lib/abicoder/encoder.rb', line 173 def encode_int( arg, bits ) ## raise EncodingError or ArgumentError - why? why not? raise ArgumentError, "arg is not integer: #{arg}" unless arg.is_a?(Integer) raise ValueOutOfBounds, arg unless arg >= -2**(bits-1) && arg < 2**(bits-1) lpad_int( arg % 2**bits ) end |
#encode_primitive_type(type, arg) ⇒ Object
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/abicoder/encoder.rb', line 133 def encode_primitive_type( type, arg ) case type when Uint ## note: for now size in bits always required encode_uint( arg, type.bits ) when Int ## note: for now size in bits always required encode_int( arg, type.bits ) when Bool encode_bool( arg ) when String encode_string( arg ) when FixedBytes encode_bytes( arg, type.length ) when Bytes encode_bytes( arg ) when Address encode_address( arg ) else raise EncodingError, "Unhandled type: #{type.class.name} #{type.format}" end end |
#encode_static_array(type, args) ⇒ Object
98 99 100 101 102 103 |
# File 'lib/abicoder/encoder.rb', line 98 def encode_static_array( type, args ) raise ArgumentError, "arg must be an array" unless args.is_a?(::Array) raise ArgumentError, "Wrong array size: found #{args.size}, expecting #{type.dim}" unless args.size == type.dim args.map {|arg| encode_type( type.subtype, arg ) }.join end |
#encode_string(arg) ⇒ Object
181 182 183 184 185 186 187 188 189 190 |
# File 'lib/abicoder/encoder.rb', line 181 def encode_string( arg ) ## raise EncodingError or ArgumentError - why? why not? raise EncodingError, "Expecting string: #{arg}" unless arg.is_a?(::String) arg = arg.b if arg.encoding != Encoding::BINARY ## was: name == 'UTF-8' raise ValueOutOfBounds, "Integer invalid or out of range: #{arg.size}" if arg.size > UINT_MAX size = lpad_int( arg.size ) value = rpad( arg, ceil32(arg.size) ) size + value end |
#encode_tuple(tuple, args) ⇒ Object
todo/check: if static tuple gets encoded different
without offset (head/tail)
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/abicoder/encoder.rb', line 110 def encode_tuple( tuple, args ) raise ArgumentError, "arg must be an array" unless args.is_a?(::Array) raise ArgumentError, "Wrong array size (for tuple): found #{args.size}, expecting #{tuple.type.size} tuple elements" unless args.size == tuple.types.size head_size = tuple.types .map {|type| type.size || 32 } .sum head, tail = ''.b, ''.b ## note: use string buffer with BINARY / ASCII_8BIT encoding!!! tuple.types.each_with_index do |type, i| if type.dynamic? head += encode_uint256( head_size + tail.size ) tail += encode_type( type, args[i] ) else head += encode_type( type, args[i] ) end end head + tail end |
#encode_type(type, arg) ⇒ String
Encodes a single value (static or dynamic).
59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/abicoder/encoder.rb', line 59 def encode_type( type, arg ) if type.is_a?( Tuple ) encode_tuple( type, arg ) elsif type.is_a?( Array ) || type.is_a?( FixedArray ) if type.dynamic? encode_dynamic_array( type, arg ) else encode_static_array( type, arg ) end else # assume primitive type encode_primitive_type( type, arg ) end end |
#encode_uint(arg, bits) ⇒ Object
166 167 168 169 170 171 |
# File 'lib/abicoder/encoder.rb', line 166 def encode_uint( arg, bits ) ## raise EncodingError or ArgumentError - why? why not? raise ArgumentError, "arg is not integer: #{arg}" unless arg.is_a?(Integer) raise ValueOutOfBounds, arg unless arg >= 0 && arg < 2**bits lpad_int( arg ) end |
#encode_uint256(arg) ⇒ Object
165 |
# File 'lib/abicoder/encoder.rb', line 165 def encode_uint256( arg ) encode_uint( arg, 256 ); end |
#lpad(bin) ⇒ Object
rename to lpad32 or such - why? why not?
241 242 243 244 245 |
# File 'lib/abicoder/encoder.rb', line 241 def lpad( bin ) ## note: same as builtin String#rjust !!! l=32 # note: default l word is 32 bytes return bin if bin.size >= l BYTE_ZERO * (l - bin.size) + bin end |
#lpad_hex(hex) ⇒ Object
rename to lpad32_hex or such - why? why not?
258 259 260 261 262 263 264 |
# File 'lib/abicoder/encoder.rb', line 258 def lpad_hex( hex ) raise TypeError, "Value must be a string" unless hex.is_a?( ::String ) raise TypeError, 'Non-hexadecimal digit found' unless hex =~ /\A[0-9a-fA-F]*\z/ bin = [hex].pack("H*") lpad( bin ) end |
#lpad_int(n) ⇒ Object
rename to lpad32_int or such - why? why not?
248 249 250 251 252 253 254 255 |
# File 'lib/abicoder/encoder.rb', line 248 def lpad_int( n ) raise ArgumentError, "Integer invalid or out of range: #{n}" unless n.is_a?(Integer) && n >= 0 && n <= UINT_MAX hex = n.to_s(16) hex = "0#{hex}" if hex.size.odd? bin = [hex].pack("H*") lpad( bin ) end |
#rpad(bin, l = 32) ⇒ Object
encoding helpers / utils
with "hard-coded" fill symbol as BYTE_ZERO
233 234 235 236 237 |
# File 'lib/abicoder/encoder.rb', line 233 def rpad( bin, l=32 ) ## note: same as builtin String#ljust !!! # note: default l word is 32 bytes return bin if bin.size >= l bin + BYTE_ZERO * (l - bin.size) end |