# Copyright (c) 2016-2022 The Ruby-Eth Contributors## Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at## http://www.apache.org/licenses/LICENSE-2.0## Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License.# -*- encoding : ascii-8bit -*-# Provides the {Eth} module.moduleEth# Provides a Ruby implementation of the Ethereum Applicatoin Binary Interface (ABI).moduleAbi# Provides a class to handle and parse common ABI types.classType# Provides a specific parser error if type cannot be determined.classParseError<StandardError;end# The base attribute, e.g., `string` or `bytes`.attr:base_type# The sub-type attribute, e.g., `256` as size of an uint256.attr:sub_type# The dimension attribute, e.g., `[10]` for an array of size 10.attr:dimensions# Create a new Type object for base types, sub types, and dimensions.# Should not be used; use {Type.parse} instead.## @param base_type [String] the base-type attribute.# @param sub_type [String] the sub-type attribute.# @param dimensions [Array] the dimension attribute.# @return [Eth::Abi::Type] an ABI type object.definitialize(base_type,sub_type,dimensions)sub_type=sub_type.to_s@base_type=base_type@sub_type=sub_type@dimensions=dimensionsend# Converts the self.parse method into a constructor.konstructor:parse# Attempts to parse a string containing a common Solidity type.# Creates a new Type upon success (using konstructor).## @param type [String] a common Solidity type.# @return [Eth::Abi::Type] a parsed Type object.# @raise [ParseError] if it fails to parse the type.defparse(type)_,base_type,sub_type,dimension=/([a-z]*)([0-9]*x?[0-9]*)((\[[0-9]*\])*)/.match(type).to_a# type dimension can only be numericdims=dimension.scan(/\[[0-9]*\]/)raiseParseError,"Unknown characters found in array declaration"ifdims.join!=dimension# enforce base typesvalidate_base_typebase_type,sub_type# return a new Type (using konstructor)sub_type=sub_type.to_s@base_type=base_type@sub_type=sub_type@dimensions=dims.map{|x|x[1...-1].to_i}end# Creates a new uint256 type used for size.## @return [Eth::Abi::Type] a uint256 size type.defself.size_type@size_type||=new("uint",256,[])end# Compares two types for their attributes.## @param another_type [Eth::Abi::Type] another type to be compared.# @return [Boolean] true if all attributes match.def==(another_type)base_type==another_type.base_typeandsub_type==another_type.sub_typeanddimensions==another_type.dimensionsend# Computes the size of a type if possible.## @return [Integer] the size of the type; or nil if not available.defsizes=nilifdimensions.empty?unless["string","bytes"].include?(base_type)andsub_type.empty?s=32endelseunlessdimensions.last==0unlessnested_sub.is_dynamic?s=dimensions.last*nested_sub.sizeendendend@size||=send# Helpes to determine whether array is of dynamic size.## @return [Boolean] true if array is of dynamic size.defis_dynamic?size.nil?end# Types can have nested sub-types in arrays.## @return [Eth::Abi::Type] nested sub-type.defnested_sub@nested_sub||=self.class.new(base_type,sub_type,dimensions[0...-1])endprivate# Validates all known base types and raises if an issue occurs.defvalidate_base_type(base_type,sub_type)casebase_typewhen"string"# string can not have any suffixraiseParseError,"String type must have no suffix or numerical suffix"unlesssub_type.empty?when"bytes"# bytes can be no longer than 32 bytesraiseParseError,"Maximum 32 bytes for fixed-length string or bytes"unlesssub_type.empty?||sub_type.to_i<=32when"uint","int"# integers must have a numerical suffixraiseParseError,"Integer type must have numerical suffix"unlesssub_type=~/\A[0-9]+\z/# integer size must be validsize=sub_type.to_iraiseParseError,"Integer size out of bounds"unlesssize>=8&&size<=256raiseParseError,"Integer size must be multiple of 8"unlesssize%8==0when"ureal","real","fixed","ufixed"# floats must have valid dimensional suffixraiseParseError,"Real type must have suffix of form <high>x<low>, e.g. 128x128"unlesssub_type=~/\A[0-9]+x[0-9]+\z/high,low=sub_type.split("x").map(&:to_i)total=high+lowraiseParseError,"Real size out of bounds (max 32 bytes)"unlesstotal>=8&&total<=256raiseParseError,"Real high/low sizes must be multiples of 8"unlesshigh%8==0&&low%8==0when"hash"# hashs must have numerical suffixraiseParseError,"Hash type must have numerical suffix"unlesssub_type=~/\A[0-9]+\z/when"address"# addresses cannot have any suffixraiseParseError,"Address cannot have suffix"unlesssub_type.empty?when"bool"# booleans cannot have any suffixraiseParseError,"Bool cannot have suffix"unlesssub_type.empty?else# we cannot parse arbitrary types such as 'decimal' or 'hex'raiseParseError,"Unknown base type"endendendendend