# Solidity Data Types

### Solidity Data Types

Solidity is a statically typed language, which means that the type of each variable (state and local) needs to be specified. Solidity provides several elementary types which can be combined to form complex types.

There are two types of data types in solidity.

1. Value Type
2. Reference Type

## Value Types

The following types are also called value types because variables of these types will always be passed by value, i.e. they are always copied when they are used as function arguments or in assignments.

##### Booleans

Booleans of the Solidity value types can be either `true` or `false`. The boolean is defined with the `bool` keyword.

It works with these operators:

• `!` (logical negation)
• `&&` (logical conjunction, AND)
• `||` (logical disjunction, OR)
• `==` (equality)
• `!=` (inequality)
##### Integers

There are two main Solidity types of integers of differing 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 the following 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)

### Fixed Point Numbers

There are two types of 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` represents the amount of bits that the type takes, with `N` representing the number 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.

Note: fixed point numbers can be declared in Solidity, but they are not completely supported by this language.

The fixed point numbers function 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)

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 address Solidity value type has two similar kinds:

• `address` holds a 20-byte value (size of an Ethereum address).
• `address payable` is the same as address, but have `transfer` and `send` members.

Note: there are two distinctions between smart contract addresses – the address payable can receive Ether, while simple address cannot.

Implicit conversions of addresses:

• From `address payable` to `address`: allowed.
• From `address` to `address payable`: not allowed. This type of conversion is only possible with intermediate conversion to `uint160`.
• Address literals can be converted to `address payable`.

Explicit conversions to and from address are permitted for integers, integer literals, contact types and bytes20. However, Solidity prevents the conversions of `address payable(x)`.

The `address(x)` can be converted to `address payable` in cases when `x` is of integer, fixed bytes type, or a literal or a contract that has a `payable` fallback function. When `x` is a contract without the `payable` fallback function, the `address(x)` is of type address.

Note: it is possible to disregard the difference between address and address payable by using the address. You can call the transfer function on msg.sender which is an address payable.

#### Members of Address

The `balance` property queries the balance of an address, while Ether can be sent to addresses with the `transfer` function.

The `transfer` function queries the balance of an address by applying the property balance and sending Ether (in units of `wei`) to a payable address:

```				```
if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10);
```
```

Gain more direct control over encoding or interface with contracts (not adhere to the ABI) with `call``delegatecall` and `staticcall` functions.

They accept one `bytes memory` parameter, deliver the success condition as a boolean and the returned data. For encoding of structured data, use `abi.encode``abi.encodePacked``abi.encodeWithSelector` and `abi.encodeWithSignature`:

```				```
bytes memory payload = abi.encodeWithSignature("register(string)", "MyName");
(bool success, bytes memory returnData) = address(nameReg).call(payload);
require(success);
```
```

You can adjust the provided gas using the `.gas()` modifier:

```				```
```
```

You can manipulate the Ether value as well:

```				```
```
```

It is possible to combine these modifiers. Their order is not important:

```				```
```
```

### Fixed-size Byte Arrays

The value types `bytes1``bytes2``bytes3`, …, `bytes32` contain a sequence of bytes (from 1 to 32).

Operators that can be applied to this Solidity value type:

• Comparisons: `<=``<``==``!=``>=``>` (evaluate to bool)
• Bit operators: `&``|``^` (bitwise exclusive or), `~` (bitwise negation)
• Shift operators: `<<` (left shift), `>>` (right shift)
• Index access: If `x` is of type `bytesI`, then `x[k]` for `0 <= k < I` returns the `k` th byte (read-only).

Note: there is one possible parameter of .length. It provides the fixed length of the byte array (read-only).

### Enums

Enums generate user-defined Solidity types. The explicit conversion is possible to and from all integer types, but the implicit conversion is not.

Note: during the explicit conversion from integer, it is confirmed whether the value at runtime is inside the range of the enum. If this is not confirmed, a failing assert occurs.

```				```
pragma solidity >=0.4.16 <0.7.0;

contract test {
enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill }
ActionChoices choice;
ActionChoices constant defaultChoice = ActionChoices.GoStraight;

function setGoStraight() public {
choice = ActionChoices.GoStraight;
}

function getChoice() public view returns (ActionChoices) {
return choice;
}

function getDefaultChoice() public pure returns (uint) {
return uint(defaultChoice);
}
}
```
```

## Reference Types

Solidity reference types have to be handled with more caution than value types. It is crucial to clearly indicate the data area where the reference type is stored: `memory``storage` or `calldata`. Reference type values are manipulated via more than one different name.

### Data Location and Assignment Behavior

Every reference type contains information on where it is stored. There are three possible options: `memory``storage`, and `calldata`. The set location is important for semantics of assignments, not only for the persistence of data:

• Assignments between `storage` and `memory` (or from `calldata`) always generate an independent copy.
• Assignments from `memory` to `memory` only create references. This means that changes to one memory variable are also visible in all other memory variables that refer to the same data.
• Assignments from `storage` to a local storage variable also only assign a reference.
• All other assignments to `storage` always copy. Examples for this case are assignments to state variables or to members of local variables of storage struct type, even if the local variable itself is just a reference.

In the following example, the location of `x` is `storage`, while the location of `memoryArray` is in `memory`:

```				```
pragma solidity >=0.4.0 <0.7.0;

contract C {
uint[] x;

function f(uint[] memory memoryArray) public {
x = memoryArray; // works, copies the whole array to storage
uint[] storage y = x; // works, assigns a pointer, data location of y is storage
y; // fine, returns the 8th element
y.length = 2; // fine, modifies x through y
delete x; // fine, clears the array, also modifies y
// The following does not work; it would need to create a new temporary /
// unnamed array in storage, but storage is "statically" allocated:
// y = memoryArray;
// This does not work either, since it would "reset" the pointer, but there
// is no sensible location it could point to.
// delete y;
g(x); // calls g, handing over a reference to x
h(x); // calls h and creates an independent, temporary copy in memory
}

function g(uint[] storage) internal pure {}
function h(uint[] memory) public pure {}
}
```
```

### Arrays

Arrays can have fixed or dynamic sizes for the compiling process. The array with fixed size `k` and element type `T` is combined as `T[k]`. The dynamically-sized array is `T[]`.

An array consisting of 6 dynamic arrays of `uint` looks like this: `uint[] `. By default in Solidity, x array consists of three elements of type despite the fact that it can be an array.

Note: array elements can have any type. However, there are some limitations: mappings must be stored in the storage data location and public functions are to have ABI type parameters.

### Allocating Memory Arrays

The keyword `new` creates arrays with a runtime-dependent length in memory.

Remember: differently than storage arrays, size of memory arrays cannot be manipulated. Determine the size beforehand or generate a new array and transfer all elements to it.

```				```
pragma solidity >=0.4.16 <0.7.0;

contract C {
function f(uint len) public pure {
uint[] memory a = new uint[](7);
bytes memory b = new bytes(len);
assert(a.length == 7);
assert(b.length == len);
a = 8;
}
}
```
```

### Array Literals

A list of one or multiple expressions, separated by commas and placed in square brackets ([…]) is an array literal. It is a statically-sized memory array.

The following example shows that the type of `[1, 2, 3]` is `uint8 memory`. Since every constant is of `uint8` type, the first element must be converted to `uint` before it can be `uint memory`.

```				```
pragma solidity >=0.4.16 <0.7.0;

contract C {
function f() public pure {
g([uint(1), 2, 3]);
}
function g(uint memory) public pure {
// ...
}
}
```
```

The next example shows an impossible situation – fixed size memory arrays cannot be set to dynamically-sized memory arrays. Since it cannot compile, a type error appears (`unit` cannot be converted to `uint[] memory`):

```				```
pragma solidity >=0.4.0 <0.7.0;

contract C {
function f() public {
uint[] memory x = [uint(1), 3, 4];
}
}
```
```

### Array Members

Arrays have these members:

• `length` – indicates the number of elements. For memory arrays, the length is fixed after they are generated. However, it can be dynamic and can rely on runtime parameters. The length is set to dynamically-sized arrays to change their size.
• `push` – attaches an element at the end of the dynamic storage arrays and bytes (not string). The newly-added element is zero-initialized.
• `pop` removes an element at the end of the dynamic storage arrays and bytes (not string).
```				```
pragma solidity >=0.4.16 <0.7.0;

contract ArrayContract {
uint[2**20] m_aLotOfIntegers;
// Note that the following is not a pair of dynamic arrays but a
// dynamic array of pairs (i.e. of fixed size arrays of length two).
// Because of that, T[] is always a dynamic array of T, even if T
// itself is an array.
// Data location for all state variables is storage.
bool[] m_pairsOfFlags;

// newPairs is stored in memory - the only possibility
// for public contract function arguments
function setAllFlagPairs(bool[] memory newPairs) public {
// assignment to a storage array performs a copy of ``newPairs`` and
// replaces the complete array ``m_pairsOfFlags``.
m_pairsOfFlags = newPairs;
}

struct StructType {
uint[] contents;
uint moreInfo;
}
StructType s;

function f(uint[] memory c) public {
// stores a reference to ``s`` in ``g``
StructType storage g = s;
// also changes ``s.moreInfo``.
g.moreInfo = 2;
// assigns a copy because ``g.contents``
// is not a local variable, but a member of
// a local variable.
g.contents = c;
}

function setFlagPair(uint index, bool flagA, bool flagB) public {
// access to a non-existing index will throw an exception
m_pairsOfFlags[index] = flagA;
m_pairsOfFlags[index] = flagB;
}

function changeFlagArraySize(uint newSize) public {
// if the new size is smaller, removed array elements will be cleared
m_pairsOfFlags.length = newSize;
}

function clear() public {
// these clear the arrays completely
delete m_pairsOfFlags;
delete m_aLotOfIntegers;
// identical effect here
m_pairsOfFlags.length = 0;
}

bytes m_byteData;

function byteArrays(bytes memory data) public {
// byte arrays ("bytes") are different as they are stored without padding,
// but can be treated identical to "uint8[]"
m_byteData = data;
m_byteData.length += 7;
m_byteData = 0x08;
delete m_byteData;
}

function addFlag(bool memory flag) public returns (uint) {
return m_pairsOfFlags.push(flag);
}

function createMemoryArray(uint size) public pure returns (bytes memory) {
// Dynamic memory arrays are created using `new`:
uint[] memory arrayOfPairs = new uint[](size);

// Inline arrays are always statically-sized and if you only
// use literals, you have to provide at least one type.
arrayOfPairs = [uint(1), 2];

// Create a dynamic byte array:
bytes memory b = new bytes(200);
for (uint i = 0; i < b.length; i++)
b[i] = byte(uint8(i));
return b;
}
}
```
```

### Structs

Solidity allows to define new types in the form of structs. This process is shown in the example below:

```				```
pragma solidity >=0.4.11 <0.7.0;

contract CrowdFunding {
// Defines a new type with two fields.
struct Funder {
uint amount;
}

struct Campaign {
uint fundingGoal;
uint numFunders;
uint amount;
mapping (uint => Funder) funders;
}

uint numCampaigns;
mapping (uint => Campaign) campaigns;

function newCampaign(address payable beneficiary, uint goal) public returns (uint campaignID) {
campaignID = numCampaigns++; // campaignID is return variable
// Creates new struct in memory and copies it to storage.
// We leave out the mapping type, because it is not valid in memory.
// If structs are copied (even from storage to storage),
// types that are not valid outside of storage (ex. mappings and array of mappings)
// are always omitted, because they cannot be enumerated.
campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0);
}

function contribute(uint campaignID) public payable {
Campaign storage c = campaigns[campaignID];
// Creates a new temporary memory struct, initialised with the given values
// and copies it over to storage.
// Note that you can also use Funder(msg.sender, msg.value) to initialise.
c.funders[c.numFunders++] = Funder({addr: msg.sender, amount: msg.value});
c.amount += msg.value;
}

function checkGoalReached(uint campaignID) public returns (bool reached) {
Campaign storage c = campaigns[campaignID];
if (c.amount < c.fundingGoal)
return false;
uint amount = c.amount;
c.amount = 0;
c.beneficiary.transfer(amount);
return true;
}
}
```
```

The contract is not as efficient as a crowdfunding contract, but it has the necessary parts for you to learn structs. They can be applied or contain mappings and arrays.

Note: a struct cannot have a member of the same type as its own.

In all of the functions in the example, a struct type is assigned to a local variable with data location storage. Therefore, it does not copy a struct. Instead, a reference is stored so that assignments to members of the local variable write to the state.

Like in `campaigns[campaignID].amount = 0`, it is possible to directly access the struct members and not assign it to a local variable.

## Mapping Types

Syntax of `(_KeyType =>_ValueType)` defines the mapping types.

• The `_KeyType` can be any elementary type (plus bytes and string). However, contract types, enums, mappings, structs, and any array type apart are not permitted.
• _ValueType accepts any type, mappings as well.

Note: Solidity mappings can only have a data location of storage. Therefore, they are allowed for state variables.

• It is possible to set state variables of mapping Solidity type as public and the language generates a getter. Then, the `_KeyType` is a parameter for the getter.
• In cases when `_ValueType` is a value type or a struct, the getter returns `_ValueType`. When it is an array or mapping, the getter has one parameter for every `_KeyType`, recursively.

Take a look at the example of mapping Solidity:

```				```
pragma solidity >=0.4.0 <0.7.0;

contract MappingExample {
mapping(address => uint) public balances;

function update(uint newBalance) public {
balances[msg.sender] = newBalance;
}
}

contract MappingUser {
function f() public returns (uint) {
MappingExample m = new MappingExample();
m.update(100);
}
}
```
```

## Operators Involving LValues

The `a` is an LValue (for instance, a variable or something that can be assigned to). It has operators as shorthands:

• `a += e` is the same as `a= a + e`. The operators `-=``*=``/=``%=``|=``&=` and `^=` are defined accordingly.
• `a++` and `a--` is the same as `a += 1` / `a -= 1` but the expression still has the previous a value.
• `--a` and `++a` work the same on a but return the value after the change.

### delete

In the following example, `delete x` assigns `x` to 0 and does not affect data. `delete data` sets data to 0, does not affect `x`:

```				```
pragma solidity >=0.4.0 <0.7.0;

contract DeleteExample {
uint data;
uint[] dataArray;

function f() public {
uint x = data;
delete x;
delete data;
uint[] storage y = dataArray;
delete dataArray; // this sets dataArray.length to zero, but as uint[] is a complex object, also
// y is affected which is an alias to the storage object
// On the other hand: "delete y" is not valid, as assignments to local variables
// referencing storage objects can only be made from existing storage objects.
assert(y.length == 0);
}
}
```
```
• The `delete a` sets the initial value for the type to `a`. I.e. for integers it is equivalent to `a = 0`.
• It is possible to apply it on arrays. It will set a dynamic array of length zero or static array of the same length with all elements assigned their initial value.
• `delete a[x]` will remove an element at the specified array index (won’t change other items and length). This results in a gap in an array.
• It sets a struct with all members reset. The a value after `delete a` is the same as delete does not influence mappings. By deleting a struct, you reset all members that are not related to mappings.

## Conversions Between Elementary Types

### Implicit Conversions

When you use operators on different Solidity types, the compiler aims to implicitly convert one of the operands to the type of the other.

Therefore, the operations execute in the type of one of the operands. The implicit conversion takes place without any issues if it is semantically logic, and data is not lost.

For instance, you can convert `uint8` to `uint16` and `int128` to `int256`. However, `int8` cannot be converted to `uint256` (it can’t hold values like -1).

### Explicit Conversions

When you are confident that a conversion will take place correctly, attempt to perform the explicit type conversion.

Warning: be careful since such actions can render unpredictable results and disregard some security features.

The example below depicts the conversion of a negative `int` to a `uint`:

```				```
int  y = -3;
uint x = uint(y);
```
```

At the end of this conversion, the `x` will have the value of `0xfffff..fd` (64 hex characters). It is -3 in the two’s complement representation of 256 bits.

In cases when integers are converted into a smaller type, higher-order bits are removed. After the second line is compiled, `b` will be `0x5678`:

```				```
uint32 a = 0x12345678;
uint16 b = uint16(a);
```
```

The next example shows the opposite situation when an integer is converted to larger Solidity types. It will be padded at the higher order end. After the second line, `b` will be `0x00001234`. Comparison between equal and the original integer is the result:

```				```
uint16 a = 0x1234;
uint32 b = uint32(a);
assert(a == b);
```
```

The fixed-size bytes Solidity types act differently during conversions. After the second line, `b` will be `0x12`. If you try to convert them to smaller type, the sequence will be disturbed:

```				```
bytes2 a = 0x1234;
bytes1 b = bytes1(a);
```
```

In cases when fixed-size bytes type is converted to a larger type, it is padded on the right. After the second line, `b` will be `0x12340000`:

```				```
bytes2 a = 0x1234;
bytes4 b = bytes4(a);
assert(a == b);
assert(a == b);
```
```

During truncating or padding, integers and fixed-size byte types act differently. Therefore, explicit conversions between them are not permitted (unless their sizes are identical). To convert them from different sizes, intermediate conversions make the truncation and padding rules explicit:

```				```
bytes2 a = 0x1234;
uint32 b = uint16(a); // b will be 0x00001234
uint32 c = uint32(bytes4(a)); // c will be 0x12340000
uint8 d = uint8(uint16(a)); // d will be 0x34
uint8 e = uint8(bytes1(a)); // e will be 0x12
```
```