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, l = 32, symbol = BYTE_ZERO) ⇒ Object
note: same as builtin String#rjust !!!.
- #lpad_hex(hex, l = 32, symbol = BYTE_ZERO) ⇒ Object
- #lpad_int(n, l = 32, symbol = BYTE_ZERO) ⇒ Object
-
#rpad(bin, l = 32, symbol = BYTE_ZERO) ⇒ Object
encoding helpers / utils.
Instance Method Details
#ceil32(x) ⇒ Object
273 274 275 |
# File 'lib/abicoder/encoder.rb', line 273 def ceil32(x) x % 32 == 0 ? x : (x + 32 - x%32) end |
#encode(types, args) ⇒ Object
Encodes multiple arguments using the head/tail mechanism.
22 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 22 def encode( types, args ) ## todo/fix: enforce args.size and types.size must match!! ## raise ArgumentError - expected x arguments got y!!! ## 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 = '', '' 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
226 227 228 229 230 231 232 233 234 235 236 237 238 |
# File 'lib/abicoder/encoder.rb', line 226 def encode_address( arg ) if arg.is_a?( Integer ) lpad_int( arg ) elsif arg.size == 20 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
166 167 168 169 |
# File 'lib/abicoder/encoder.rb', line 166 def encode_bool( arg ) raise ArgumentError, "arg is not bool: #{arg}" unless arg.is_a?(TrueClass) || arg.is_a?(FalseClass) lpad_int( arg ? 1 : 0 ) end |
#encode_bytes(arg, length = nil) ⇒ Object
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/abicoder/encoder.rb', line 209 def encode_bytes( arg, length=nil ) raise EncodingError, "Expecting string: #{arg}" unless arg.is_a?(::String) arg = arg.b 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
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/abicoder/encoder.rb', line 82 def encode_dynamic_array( type, args ) raise ArgumentError, "arg must be an array" unless args.is_a?(::Array) head, tail = '', '' 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
179 180 181 182 183 |
# File 'lib/abicoder/encoder.rb', line 179 def encode_int( arg, bits ) 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
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/abicoder/encoder.rb', line 141 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
106 107 108 109 110 111 |
# File 'lib/abicoder/encoder.rb', line 106 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
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/abicoder/encoder.rb', line 186 def encode_string( arg ) ## todo/fix: do NOT auto-unpack hexstring EVER!!! ## remove arg.unpack('U*') if arg.encoding == Encoding::UTF_8 ## was: name == 'UTF-8' arg = arg.b else begin arg.unpack('U*') rescue ArgumentError raise ValueError, "string must be UTF-8 encoded" end end 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)
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/abicoder/encoder.rb', line 118 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 = '', '' 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 72 73 74 75 76 77 78 79 |
# File 'lib/abicoder/encoder.rb', line 59 def encode_type( type, arg ) ## case 1) string or bytes (note:are dynamic too!!! most go first) ## use type == Type.new( 'string', nil, [] ) - same as Type.new('string')) ## or type == Type.new( 'bytes', nil, [] ) - same as Type.new('bytes') ## - why? why not? if type.is_a?( String ) encode_string( arg ) elsif type.is_a?( Bytes ) encode_bytes( arg ) elsif 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 static (primitive) type encode_primitive_type( type, arg ) end end |
#encode_uint(arg, bits) ⇒ Object
173 174 175 176 177 |
# File 'lib/abicoder/encoder.rb', line 173 def encode_uint( arg, bits ) 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
172 |
# File 'lib/abicoder/encoder.rb', line 172 def encode_uint256( arg ) encode_uint( arg, 256 ); end |
#lpad(bin, l = 32, symbol = BYTE_ZERO) ⇒ Object
note: same as builtin String#rjust !!!
249 250 251 252 |
# File 'lib/abicoder/encoder.rb', line 249 def lpad( bin, l=32, symbol=BYTE_ZERO ) ## note: same as builtin String#rjust !!! return bin if bin.size >= l symbol * (l - bin.size) + bin end |
#lpad_hex(hex, l = 32, symbol = BYTE_ZERO) ⇒ Object
263 264 265 266 267 268 269 |
# File 'lib/abicoder/encoder.rb', line 263 def lpad_hex( hex, l=32, symbol=BYTE_ZERO ) 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, l, symbol ) end |
#lpad_int(n, l = 32, symbol = BYTE_ZERO) ⇒ Object
254 255 256 257 258 259 260 261 |
# File 'lib/abicoder/encoder.rb', line 254 def lpad_int( n, l=32, symbol=BYTE_ZERO ) 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, l, symbol ) end |
#rpad(bin, l = 32, symbol = BYTE_ZERO) ⇒ Object
encoding helpers / utils
244 245 246 247 |
# File 'lib/abicoder/encoder.rb', line 244 def rpad( bin, l=32, symbol=BYTE_ZERO ) ## note: same as builtin String#ljust !!! return bin if bin.size >= l bin + symbol * (l - bin.size) end |