Class: Type

Inherits:
Object
  • Object
show all
Includes:
ContractErrors
Defined in:
lib/0xfacet/typed/type.rb

Constant Summary collapse

INTEGER_TYPES =
(8..256).step(8).flat_map do |num|
 ["uint#{num}", "int#{num}"]
end.map(&:to_sym)
TYPES =
[:string, :mapping, :address, :bytes32, :contract,
:bool, :address, :uint256, :int256, :array, :datetime, :bytes] + INTEGER_TYPES

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(type_name, metadata = {}) ⇒ Type

Returns a new instance of Type.



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/0xfacet/typed/type.rb', line 25

def initialize(type_name,  = {})
  if type_name.is_a?(Array)
    if type_name.length != 1
      raise "Invalid array type #{type_name.inspect}"
    end
    
    value_type = type_name.first
    
    if TYPES.exclude?(value_type)
      raise "Invalid type #{value_type.inspect}"
    end
    
     = { value_type: value_type }
    type_name = :array
  end
  
  type_name = type_name.to_sym
  
  if TYPES.exclude?(type_name)
    raise "Invalid type #{type_name}"
  end
  
  self.name = type_name.to_sym
  self. = .deep_dup
end

Instance Attribute Details

#initial_lengthObject

Returns the value of attribute initial_length.



4
5
6
# File 'lib/0xfacet/typed/type.rb', line 4

def initial_length
  @initial_length
end

#key_typeObject

Returns the value of attribute key_type.



4
5
6
# File 'lib/0xfacet/typed/type.rb', line 4

def key_type
  @key_type
end

#metadataObject

Returns the value of attribute metadata.



4
5
6
# File 'lib/0xfacet/typed/type.rb', line 4

def 
  @metadata
end

#nameObject

Returns the value of attribute name.



4
5
6
# File 'lib/0xfacet/typed/type.rb', line 4

def name
  @name
end

#value_typeObject

Returns the value of attribute value_type.



4
5
6
# File 'lib/0xfacet/typed/type.rb', line 4

def value_type
  @value_type
end

Class Method Details

.create(type_or_name, metadata = {}) ⇒ Object



51
52
53
54
55
# File 'lib/0xfacet/typed/type.rb', line 51

def self.create(type_or_name,  = {})
  return type_or_name if type_or_name.is_a?(self)
  
  new(type_or_name, )
end

.value_typesObject



19
20
21
22
23
# File 'lib/0xfacet/typed/type.rb', line 19

def self.value_types
  TYPES.select do |type|
    create(type).is_value_type?
  end
end

Instance Method Details

#!=(other) ⇒ Object



275
276
277
# File 'lib/0xfacet/typed/type.rb', line 275

def !=(other)
  !(self == other)
end

#==(other) ⇒ Object



269
270
271
272
273
# File 'lib/0xfacet/typed/type.rb', line 269

def ==(other)
  other.is_a?(self.class) &&
  other.name == name &&
  other..except(:initial_length) == .except(:initial_length)
end

#can_be_assigned_from?(other_type) ⇒ Boolean

Returns:

  • (Boolean)


73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/0xfacet/typed/type.rb', line 73

def can_be_assigned_from?(other_type)
  return true if self == other_type

  if is_uint? && other_type.is_uint? || is_int? && other_type.is_int?
    return extract_integer_bits >= other_type.extract_integer_bits
  end
  
  if address? && other_type.contract?
    return true
  end

  false
end

#check_and_normalize_literal(literal) ⇒ Object

Raises:



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
# File 'lib/0xfacet/typed/type.rb', line 145

def check_and_normalize_literal(literal)
  if literal.is_a?(TypedVariable)
    raise VariableTypeError, "Only literals can be passed to check_and_normalize_literal: #{literal.inspect}"
  end
  
  if address?
    if literal.is_a?(ContractType::Proxy)
      return literal.address
    end
    
    unless literal.is_a?(String) && literal.match?(/\A0x[a-f0-9]{40}\z/i)
      raise_variable_type_error(literal)
    end
    
    return literal.downcase
  elsif is_uint?
    if literal.is_a?(String)
      literal = parse_integer(literal)
    end
      
    if literal.is_a?(Integer) && literal.between?(0, 2 ** extract_integer_bits - 1)
      return literal
    end
    
    raise_variable_type_error(literal)
  elsif is_int?
    if literal.is_a?(String)
      literal = parse_integer(literal)
    end
    
    if literal.is_a?(Integer) && literal.between?(-2 ** (extract_integer_bits - 1), 2 ** (extract_integer_bits - 1) - 1)
      return literal
    end
    
    raise_variable_type_error(literal)
  elsif string?
    unless literal.is_a?(String)
      raise_variable_type_error(literal)
    end
    
    return literal.freeze
  elsif bool?
    unless literal == true || literal == false
      raise_variable_type_error(literal)
    end
    
    return literal
  elsif bytes32?
    unless literal.is_a?(String) && literal.match?(/\A0x[a-f0-9]{64}\z/i)
      raise_variable_type_error(literal)
    end
    
    return literal.downcase
  elsif bytes?
    if literal.is_a?(String) && literal.length == 0
      return literal
    end
    
    unless literal.is_a?(String) && literal.match?(/\A0x[a-fA-F0-9]*\z/) && literal.size.even?
      raise_variable_type_error(literal)
    end
    
    return literal.downcase
  elsif datetime?
    dummy_uint = Type.create(:uint256)
    
    begin
      return dummy_uint.check_and_normalize_literal(literal)
    rescue VariableTypeError => e
      raise_variable_type_error(literal)
    end
  elsif mapping?
    if literal.is_a?(MappingType::Proxy)
      return literal
    end
    
    unless literal.is_a?(Hash)
      raise VariableTypeError.new("invalid #{literal}")
    end
  
    proxy = MappingType::Proxy.new(literal, key_type: key_type, value_type: value_type)
    
    return proxy
  elsif array?
    if literal.is_a?(ArrayType::Proxy)
      return literal
    end
    
    unless literal.is_a?(Array)
      raise_variable_type_error(literal)
    end
    
    data = literal.map do |value|
      TypedVariable.create(value_type, value)
    end
  
    proxy = ArrayType::Proxy.new(data, value_type: value_type, initial_length: initial_length)
    
    return proxy
  elsif contract?
    if literal.is_a?(ContractType::Proxy)
      return literal
    else
      raise_variable_type_error("No literals allowed for contract types")
    end
  end
  
  raise VariableTypeError.new("Unknown type #{self.inspect}: #{literal.inspect}")
end

#default_valueObject



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/0xfacet/typed/type.rb', line 104

def default_value
  is_int256_uint256_datetime = is_int? || is_uint? || datetime?

  val = case
  when is_int256_uint256_datetime
    0
  when address?
    "0x" + "0" * 40
  when bytes32?
    "0x" + "0" * 64
  when string? || bytes?
    ''
  when bool?
    false
  when mapping?
    MappingType::Proxy.new(key_type: key_type, value_type: value_type)
  when array?
    ArrayType::Proxy.new(value_type: value_type, initial_length: initial_length)
  when contract?
    ContractType::Proxy.new(contract_interface: [:interface], address: nil, contract_type: nil)
  else
    raise "Unknown default value for #{self.inspect}"
  end
  
  check_and_normalize_literal(val)
end

#eql?(other) ⇒ Boolean

Returns:

  • (Boolean)


283
284
285
# File 'lib/0xfacet/typed/type.rb', line 283

def eql?(other)
  hash == other.hash
end

#extract_integer_bitsObject



263
264
265
266
267
# File 'lib/0xfacet/typed/type.rb', line 263

def extract_integer_bits
  return name.to_s[4..-1].to_i if is_uint?
  return name.to_s[3..-1].to_i if is_int?
  raise "Not an integer type: #{self}"
end

#hashObject



279
280
281
# File 'lib/0xfacet/typed/type.rb', line 279

def hash
  [name, ].hash
end

#is_int?Boolean

Returns:

  • (Boolean)


259
260
261
# File 'lib/0xfacet/typed/type.rb', line 259

def is_int?
  name.to_s.start_with?('int')
end

#is_uint?Boolean

Returns:

  • (Boolean)


255
256
257
# File 'lib/0xfacet/typed/type.rb', line 255

def is_uint?
  name.to_s.start_with?('uint')
end

#is_value_type?Boolean

Returns:

  • (Boolean)


287
288
289
# File 'lib/0xfacet/typed/type.rb', line 287

def is_value_type?
  !mapping? && !array?
end

#parse_integer(literal) ⇒ Object



135
136
137
138
139
140
141
142
143
# File 'lib/0xfacet/typed/type.rb', line 135

def parse_integer(literal)
  base = literal.start_with?("0x") ? 16 : 10
  
  begin
    Integer(literal, base)
  rescue ArgumentError => e
    raise_variable_type_error(literal)
  end
end

#raise_variable_type_error(literal) ⇒ Object

Raises:



131
132
133
# File 'lib/0xfacet/typed/type.rb', line 131

def raise_variable_type_error(literal)
  raise VariableTypeError.new("Invalid #{self}: #{literal.inspect}")
end

#to_sObject



100
101
102
# File 'lib/0xfacet/typed/type.rb', line 100

def to_s
  name.to_s
end

#values_can_be_compared?(other_type) ⇒ Boolean

Returns:

  • (Boolean)


87
88
89
90
91
92
93
94
# File 'lib/0xfacet/typed/type.rb', line 87

def values_can_be_compared?(other_type)
  return true if can_be_assigned_from?(other_type)

  if is_uint? && other_type.is_uint? || is_int? && other_type.is_int?
    return true
  end
  false
end