Solidity

Solidity Value Types


Solidity Value Types Booleans

Booleans are variables, that can contain a value of either true or false.

It also works with these two operators:

  • ! – logical negation
  • && – logical conjunction
  • | | – logical conjunction
  • == – equality
  • != – inequality

Operators && and | | adhere to the common short-circuit rules.

This means that if in an expression a(x) | | b(y) the expression a(x) evaluates to true, the b(y) expression will not be evaluated even if it causes side effects.


Solidity Value Types Integers

In Solidity, you have two types of integers, coming in various sizes:

  • int – signed integers
  • uint – unsigned integers

Speaking of size, to specify it, you have keywords such as uint8 up to uint256, that is, of 8 to 256 bits.

the simple uint and int are similar to uint256 and int256, respectively.

Integers work with these operators:

  • Comparison operators (evaluates to bool)
    • <= – less than or equal
    • < – less than
    • == – equal to
    • != – not equal to
    • >= – greater than or equal
    • > – greater than
  • Bit operators
    • & – bitwise AND
    • | – bitwise inclusive OR
    • ^ – bitwise XOR (exclusive OR)
    • ~ – bitwise NOT
  • Arithmetic operators
    • + – addition
    • – subtraction
    • unary – subtract on a single operand
    • unary + – add on a single operand
    • * – multiply a single operand
    • / – division
    • % – remainder (of division)
    • ** – exponentiation
    • << – left shift
    • >> – right shift

Division truncates everytime, as it is compiled to the DIV opcode of EVM, except for when both operators are literals (literal expressions).

Division or modulus with zero will throw a runtime exception.

The result of a shift operation is the type of the operand to the left. For example: expression x << y is equal to x * 2**y, while x >> y is equal to x /  2**y, meaning that shifting negative numbers sign will extend. However, shifting by a negative amount throws a runtime exception.

Note: Truncating – rounding values towards zero.

Warning: The results that are produced using right shift, which are negative values of signed integer types, are different from the ones produced in other programming languages. In the case of Solidity, right shift will map division so the shifted values, that are negative, would be truncated. In other languages, the shift right rounds negative values resulting from division down.


Solidity Value Types Fixed Point Numbers

Firstly, it is important to note that fixed point numbers are not supported by Solidity fully yet.

It is possible to declare them, but not assign to them or from them.

There are two types of keywords used to declare fixed point numbers:

  • fixed – signed fixed point number
  • ufixed – unsigned fixed point number

This value type also can be declared keywords such as ufixedMxN and fixedMxN. The M representsthe amount of bits that the type takes, with N representing the amount of decimal points that are available. M has to be divisible by 8, and a number from 8 to 256. N has to be a value between 0 and 80, also being inclusive.

The keywords ufixed and fixed work as aliases for keywords ufixed128x19 and fixed128x19, respectively.

These operators work with fixed point numbers:

  • Comparison operators (evaluates to bool)
    • <= – less than or equal
    • < – less than
    • == – equal to
    • != – not equal to
    • >= – greater than or equal
    • > – greater than
  • Arithmetic operators
    • + – addition
    • – subtraction
    • unary – subtract on a single operand
    • unary + – add on a single operand
    • * – multiply a single operand
    • / – division
    • % – remainder (of division)

The most notable difference between floating point (float and double, as commonly known in many other programming langues) and fixed point numbers, is the number of bits that are used for the integer and fractional part (past the decimal dot) is flexible with the floating point numbers and strictly defined in the latter, fixed point numbers. In general, with floating point numbers, most of the space is used for representing the number, while the amount of bits used to define where the decimal point is, is minimal.


Solidity Value Types Address

The value type called address holds a 20 byte value, which is the size of an Ethereum address.

These types also have members and serve as a base for contracts.

Addresses work with these operators:

  • <= – less than or equal
  • < – less than
  • == – equal to
  • != – not equal to
  • >= – greater than or equal
  • > – greater than

Since version 0.5.0, contracts do not derive from addreesses, but can still be converted to addresses.


Solidity Value Types Members Of Addresses

Balance And Transfer

Using the property balance, you can query the balance of an address, while transfer can be used for sending Ether other addresses.

Example

address a = 0x123;
address sampleAddress = this;
if (a.balance < 10 && sampleAddress.balance >= 10) a.transfer(10);

 

Try on Remix Try live on Hosting

It is also important to note, that if a is a contract’s address, then its fallback function, given that one is present, will be executed together with the transfer call.

Because it is a contract feature, this cannot be prevented.

If the execution runs out of gas or fails in some other way, then the transfer is reverted and stopped with an exception.

Send

Send is the lower level counterpart of the member transfer.

In case the execution fails, the contract stops with an exception, with send returning false.

Note: using send also has its dangers. If the stack depth is at 1024, which the caller can always force,  it fails, same happening if the recipient runs out of gas. So, if you want to make safe Ether transers, consider checking the return value of send, using transfer, or using a pattern, where the recipient would withdraw the money.

Call, Callcode And Delegatecall

Moreover, to interface with contracts which does not adhere to the ABI, the call function is provided, taking an arbitrary number of arguments of any type. These arguments will be padded to 32 bits, in addition to being concatenated.

One exception may be where the very first argument is encoded to precisely four bytes. If that’s the case, it is not padded to allow the use of signature functions.

Example

address regName = 0x72ba7d8e73fe8eb666ea66babc8116a41bfb10e2;
regName.call("register", "name");
regName.call(bytes4(keccak256("fun(uint256)")), a);

 

Try on Remix Try live on Hosting

call will return a boolean which indicates whether the function that was returned had been terminated. The return value true would imply that was not the case, if the return value is false, then the function caused an EVM exception, resulting in being terminated. However, the data returned cannot be accessed (to do this, we would need to know the size and encoding in advance). 

You can also adjust the available gas with the .gas() modifier

Example

regName.call.gas(1000000)("register", "name");

 

Try on Remix Try live on Hosting

Much alike, the Ether value can also be controlled:

Example

regName.call.value(1 ether)("register", "name");

 

Try on Remix Try live on Hosting

In addition to that, these modifiers may also be combined, no matter the order.

Example

regName.call.gas(1000000).value(1 ether)("register", "name");

 

Try on Remix Try live on Hosting

The delegatecall function can be used very similarly, although only the code of the provided address is used, and all other aspects being taken from the current contract.
The main goal of delegatecall is usage of library code located in another contract. When using delegatecall, it is important to make sure that the storage layout is suitable for using the function.
Before homestead, only a limited version of delegatecall was available, which did not provide access to values of msg.value and msg.sender .
All three callcallcode and delegatecall are low level functions, only meant for using as a last resort.
Another thing to note is that the .gas() method is available on all methods, while .value() is not available for delegatecall.
Note: all contracts inherit the members of the address, so it is possible to query them the current contract’s balance using this.balance
Note: using callcode is not recommended, as callcode is planned to be removed in the future.
Warning: Be careful when using these functions. An unknown contract can turn the call back to your contract, affecting your state variables.

Solidity Value Types Fixed Size Byte Array

Fixed size byte arrays are declared using the keyword byteX, the X being any number from 1 to 32.

The keyword byte is an alias for byte1.

Operators:

  • Comparison operators (evaluates to bool)
    • <= – less than or equal
    • < – less than
    • == – equal to
    • != – not equal to
    • >= – greater than or equal
    • > – greater than
  • Bit operators
    • & – bitwise AND
    • | – bitwise inclusive OR
    • ^ – bitwise XOR (exclusive OR)
    • ~ – bitwise NOT
    • << – left shift
    • >> – right shift
  • Index access
    • In case x is type bytesI, it means that x[k] for 0 <= k < I will return kth byte, which is read-only.

The operator for shift works with any integer type as the right operand (but return the type of the left operand), denoting the number of bits to shift by.

If you shift a negative amount, a runtime exception will be thrown.

Fixed size arrays have the member .length for yielding the fixed size of the byte array, additionally, it is read-only.


Solidity Value Types Dynamic Size Byte Array

bytes keyword declares an array with a dynamic size, not a value type.

string keyword declares UTF-8 enconded string with dynamic size, not a value type either.

As of the rule of thumb, it is recommended to use byte for arbitrary raw byte data, while string is used for arbitrary string, or UTF-8, data.

If it is possible to limit the size, use byte keywords from byte1 to byte32, as they are a lot cheaper and will save you gas.


Solidity Value Types Address Literals

Literals that are hexadecimal and pass the checksum test, for example 0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF, are of the type address.

To begin with, it should be noted that hexadecimal literals, which range in length from 39 to 41 digits and do not pass the checksum test bring up a warning and are, additionally, treated a regular rational number intervals.


Solidity Value Types Rational And Integer Literals

Integer literals are formed using a sequence of numbers from 0 to 9. These numbers are interpreted as decimals. For example 42 means forty two. In Solidity there are no octal literals and leading zeros would be invalid.

Decimal fraction literals would be formed by a . and at least a single number on one side. For example, 1..1 and 1.3 would be decimal fraction literals.

Scientific notation in Solidity also is supported, where the base itself may have fractions, meanwhile the exponent cannot. The example include 2e10, -2e10, 2e-10, 2.5e1.

Number literal expressions normally stay arbitrarilly precise, until being converted to a non-literal type (for example, by using them along with a non-literal expression), which means that computations will not overflow and divisions will not truncate in number literal expressions.

For example, (2**800 + 1) – 2**800 would result in the constant 1 (of type uint8), however, intermediate results would not even fit the word size of the machine. Moreover, .5 * 8 results in the integer 4 (however, non-integers were used in between).

Any operator that can be applied to integers can also be applied to number literal expressions as long as the operands are integers. If one of them is fractional, bit operations become forbidden along with exponentiation if the exponent is fractional (since this can result in a non-rational number).

Note: Solidity provides a number literal type for every rational number. Integer literals and rational number literals both belong to the category of number literal types. Additionally, all number literal expressions (such as the expressions which only contain number literals and operators) belong to number literal types. Therefore the number literal expressions like 1 + 2 and 2 + 1 both belong to a similar number literal type for the rational number three.

Warning: Dividing integer literals truncated in earlier versions, but now it converts into a rational number, for example: 10 / 4 is not equal to 2, but to 2.5.

Number literal expressions are converted into a non-literal type once they are used along with non-literal expressions. Although we know that the expression value assigned to b in the following example will evaluate to an integer, the partial expression 2.5 + a does not type check, resulting in that code does not compile:

Example

uint128 x = 1;
uint128 y = 2.5 + a + 0.5;

 

Try on Remix Try live on Hosting


Solidity Value Types String Literals

String literals are normally written with either single or double-quotes ( “foo” or ‘bar’). String literals do not imply trailing zeroes like in C; “foo” represents three bytes instead of four. Much like integer literals, their type may vary, as they are implicitly convertible to value types like bytes1,…, bytes32, if they fit, to bytes and to string.

String literals support escape characters, such as \n, \xNN and \uNNN.\xNN takes a hex value and inserts the appropriate byte, while \uNNN takes a Unicode codepoint and inserts an UTF-8 sequence.


Solidity Value Types Hexadecimal Literals

Hexademical Literals are prefixed with the keyword hex and are enclosed in single or double-quotes (hex”001122FF”). Their content has to be a hexadecimal string, the value of which will be the binary representation of those values.

Hexademical Literals behave a lot like String Literals and possess the same restrictions on convertibility.


Solidity Value Types Enums

In Solidity, enums are a way of creating a user-defined type. They are explicitly can be converted to and from any integer type but implicitly converting them is not allowed. The explicit conversions will check the value ranges at runtime, with a failure causing an exception. Enums need at least one member.

Example

enum moveChoice { moveLeft, moveRight, moveStraight, stayStill }
    moveChoice currentChoice;
    moveChoice constant defaultChoice = moveChoice.moveStraight;

    function moveGoStraight() {
        choice = moveChoice.moveStraight;
    }

    // Due to enum types not being a part of the ABI, the signature of "getChoice"
    // will be changed to "getChoice() returns (uint8)" automatically
    // for everything external to Solidity. The integer type that was used is simply
    // large enough for holding all enum values, for example, if you have multiple values,
    // uint16 is going to be used and so on.
    function getChoice() returns (moveChoice) {
        return currentChoice;
    }

    function getDefaultChoice() returns (uint) {
        return uint(defaultChoice);
    }

 

Try on Remix Try live on Hosting


Solidity Value Types Function Types

Function type variables may be assigned from certain functions and function parameters of the function type may be used for passing functions to and returning functions from function calls. Function types come in two ways – internal and external functions.

Internal functions may only be called in the current contract (particularly, in the current code unit, also including internal library functions as well as inherited functions) as they cannot be executed outside of the current contract’s context. Internal function call is realized by jumping to its entry label, just like when calling a function of the current contract internally.

External functions normally consist of an address and a function signature – they can be returned and passed via from external function calls.

Function types are notated as in the example:

Example

function (<parameter types>) {internal|external} [pure|constant|view|payable] [returns (<return types>)]

 

Try on Remix Try live on Hosting

Differently from the parameter types, the return types cannot be empty – in case the function type should not return anything, the whole returns(<return types>) part must be omitted.

Typically, that is, by default, function types are internal, therefore the internal keyword may be omitted. In contrast, the contract functions themselves by default are public, only when used as the type’s name, the default is internal.

There are two ways of accessing a function inside the current contract: directly by its name, or using this.functionName. The former would result in an internal function, the latter in an external function.

In the situation, when a function type variable has not been initialized, calling it would result in an exception. The same happens when you call a function after using delete on it.

If any external function types are used outside the context of Solidity, they will be treated as the function type, encoding the address followed by the function identifier along in a single bytes24 type.

Note that public functions of the current contract can be used both as an internal and as an external function. To use your function as an internal one, just use its name, if you want to use its external form, use this.functionName.

Additionally, public (or external) functions also have a special member called selector, which returns the ABI function selector:

Example

function funct() returns (bytes4) {
    return this.funct.selector;
  }

 

Try on Remix Try live on Hosting

Example which will show how internal function types can be used:

Example

library ArrayUtilities {
  // internal functions can be used in internal library functions because
  // they will be part of the same code context
  function map(uint[] memory self, function (uint) returns (uint) f)
    internal
    returns (uint[] memory r)
  {
    r = new uint[](self.length);
    for (uint i = 0; i < self.length; i++) {
      r[i] = f(self[i]);
    }
  }
  function reduction(
    uint[] memory self,
    function (uint, uint) returns (uint) f
  )
    internal
    returns (uint r)
  {
    r = self[0];
    for (uint i = 1; i < self.length; i++) {
      r = f(r, self[i]);
    }
  }
  function rangeTo(uint length) internal returns (uint[] memory r) {
    r = new uint[](length);
    for (uint i = 0; i < r.length; i++) {
      r[i] = i;
    }
  }
}

contract Pyramid {
  using ArrayUtilities for *;
  function pyramid(uint l) returns (uint) {
    return ArrayUtilities.range(l).map(square).reduce(sum);
  }
  function squareResult(uint a) internal returns (uint) {
    return a * a;
  }
  function sumResult(uint a, uint b) internal returns (uint) {
    return a + b;
  }
}

 

Try on Remix Try live on Hosting

Another example that will use external function types:

Example

 struct dataRequest {
    bytes data;
    function(bytes memory) external callback;
  }
  dataRequest[] dataRequests;
  event NewDataRequest(uint);
  function dataQuery(bytes data, function(bytes memory) external callback) {
    dataRequests.push(dataRequest(data, callback));
    NewDataRequest(requests.length - 1);
  }
  function reply(uint requestID, bytes response) {
    // Here goes the check that the reply comes from a trusted source
    dataRequests[requestID].callback(response);
  }
}

contract OracleUser {
  Oracle constant oracleConst = Oracle(0x1234567); // known contract
  function buySomething() {
    oracleConst.dataQuery("USD", this.oracleResponse);
  }
  function oracleResponse(bytes response) {
    require(msg.sender == address(oracleConst));
    // Use the data
  }

 

Try on Remix Try live on Hosting

Note: Lambda or inline functions are planned but not yet supported.

Read previous post:
HTML <comment> Tag

HTML comment Tag Main Tips The <!-- --> tag is known as comment tag. Anything put between <!-- -->is not interpreted by...

Close