Open In App

Bytes in Solidity

Improve
Improve
Like Article
Like
Save
Share
Report

In Solidity, the term “bytes” refers to a dynamically-sized byte array. Solidity provides two types of byte arrays: fixed-size arrays (called “bytesN”, where N is a number between 1 and 32) and dynamic arrays (simply called “bytes”).

Endianness and Bytes Data Layout in Solidity

Endianness refers to the order in which bytes are stored in memory. Solidity follows the little-endian format, where the least significant byte (LSB) is stored at the lowest address, and the most significant byte (MSB) is stored at the highest address. Solidity stores bytes in contiguous memory slots, with each slot being 32 bytes wide. This arrangement allows efficient memory access and alignment.

Suppose we want to store the number 0x12345678 in a bytes4 data type. In the little-endian format, the least significant byte (LSB) comes first, and the most significant byte (MSB) comes last. Thus, the byte representation of this number would be:

0x78 0x56 0x34 0x12

Example:

Solidity




pragma solidity ^0.8.0;
  
contract EndiannessExample {
    bytes4 public number;
  
    constructor() {
        number = 0x12345678;
    }
  
    function getIndividualBytes() public view returns (bytes1, bytes1, bytes1, bytes1) {
        bytes1 byte1 = bytes1(number[0]);
        bytes1 byte2 = bytes1(number[1]);
        bytes1 byte3 = bytes1(number[2]);
        bytes1 byte4 = bytes1(number[3]);
  
        return (byte1, byte2, byte3, byte4);
    }
}


Output:

In the EndiannessExample contract, we store the number 0x12345678 in a bytes4 data type called number. The getIndividualBytes function returns each byte of the number as a tuple.

Endianness and Bytes Data Layout in Solidity

 

Fixed-size Byte Arrays (bytesN)

Fixed-size byte arrays have a specified length, ranging from 1 to 32 bytes. The notation “bytesN” is used to represent these arrays, where “N” is an integer representing the length of the array. These arrays are useful when you know the exact size of the data you are working with.

Example:

Solidity




// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
  
contract FixedSizeBytesExample {
    // Store a fixed-size byte array
    bytes32 public fixedData;
  
    // Set the fixed-size byte array
    function setFixedData(bytes32 _data) public {
        fixedData = _data;
    }
  
    // Get the length of the fixed-size byte array
    function getFixedDataLength() public view returns (uint256) {
        return fixedData.length; // Always returns 32 for bytes32
    }
}
  
// Example usage:
// setFixedData input: 0x74657374696e67206461746120666f722062797465733332
// getFixedDataLength output: 32


Explanation:

  • The input for the setFixedData function is a hex-encoded bytes32 value: 0x74657374696e67206461746120666f722062797465733332. When converted to a human-readable string, it represents “testing data for bytes32”. Note that this string is 22 characters long, but it’s stored in a 32-byte space with extra padding (spaces or zeroes) at the end.
  • When you call the getFixedDataLength function, it returns the length of the bytes32 array, which is always 32 bytes.

Output:

Fixed-size Byte Arrays (bytesN)

 

Dynamically-size Byte Arrays

Dynamic byte arrays do not have a fixed length, and their size can be changed during runtime. These arrays are simply represented by the “bytes” keyword.

Example: 

Solidity




// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
  
contract DynamicBytesExample {
    // Store a dynamic byte array
    bytes public dynamicData;
  
    // Set the dynamic byte array
    function setDynamicData(bytes calldata _data) public {
        dynamicData = _data;
    }
  
    // Get the length of the dynamic byte array
    function getDynamicDataLength() public view returns (uint256) {
        return dynamicData.length;
    }
}
  
// Example usage:
// setDynamicData input: 0x74657374696e67206461746120666f722064796e616d6963206279746573
// dynamicData output: "testing data for dynamic bytes"
// getDynamicDataLength output: 30


Explanation:

  • The input for the setDynamicData function is a hex-encoded dynamic bytes value: 0x74657374696e67206461746120666f722064796e616d6963206279746573. When converted to a human-readable string, it represents “testing data for dynamic bytes”. This string is 30 characters long, and it’s stored in a dynamic byte array that adjusts its size accordingly.
  • When you query the dynamicData variable, the output is the stored data: “testing data for dynamic bytes”.
  • When you call the getDynamicDataLength function, it returns the length of the dynamic byte array, which in this case is 30 bytes (the length of the input string).

Output:

Dynamically-size Byte Arrays

 

Bitwise Operations in Solidity

Bitwise operations in Solidity are operations that manipulate individual bits in binary numbers. Solidity is a high-level programming language designed for implementing smart contracts on blockchain platforms like Ethereum. It supports various bitwise operations, including AND, OR, XOR, NOT, and bit shifts.

Here’s a brief overview of each operation:

1. Bitwise AND (&)

This operation compares each bit of the first number to the corresponding bit of the second number. If both bits are 1, the corresponding result bit is set to 1; otherwise, the result bit is set to 0.

uint256 a = 5; // binary: 101

uint256 b = 3; // binary: 011

uint256 result = a & b; // binary: 001, decimal: 1

2. Bitwise OR (|)

This operation compares each bit of the first number to the corresponding bit of the second number. If either bit is 1, the corresponding result bit is set to 1; otherwise, the result bit is set to 0.

uint256 a = 5; // binary: 101

uint256 b = 3; // binary: 011

uint256 result = a | b; // binary: 111, decimal: 7

3. Bitwise XOR (^)

This operation compares each bit of the first number to the corresponding bit of the second number. If the bits are different, the corresponding result bit is set to 1; otherwise, the result bit is set to 0.

uint256 a = 5; // binary: 101

uint256 b = 3; // binary: 011

uint256 result = a ^ b; // binary: 110, decimal: 6

4. Bitwise NOT (~)

This is a unary operation that inverts the bits of its operand. If the bit is 1, it is changed to 0; if it is 0, it is changed to 1.

uint256 a = 5; // binary: 000…101 (256 bits)

uint256 result = ~a; // binary: 111…010 (256 bits), decimal: (2^256 – 1) – 5

5. Left shift (<<)

This operation shifts the bits of a number to the left by a specified number of positions, filling the vacated positions with 0s.

uint256 a = 5; // binary: 101

uint256 result = a << 1; // binary: 1010, decimal: 10

6. Right shift (>>)

This operation shifts the bits of a number to the right by a specified number of positions, discarding the shifted bits.

uint256 a = 5; // binary: 101

uint256 result = a >> 1; // binary: 010, decimal: 2

Example:

Solidity




pragma solidity ^0.8.0;
  
contract BitwiseOperations {
    function andOperation(uint256 a, uint256 b) public pure returns (uint256) {
        uint256 result = a & b;
        return result;
    }
  
    function orOperation(uint256 a, uint256 b) public pure returns (uint256) {
        uint256 result = a | b;
        return result;
    }
  
    function xorOperation(uint256 a, uint256 b) public pure returns (uint256) {
        uint256 result = a ^ b;
        return result;
    }
  
    function notOperation(uint256 a) public pure returns (uint256) {
        uint256 result = ~a;
        return result;
    }
  
    function leftShift(uint256 a, uint256 shift) public pure returns (uint256) {
        uint256 result = a << shift;
        return result;
    }
  
    function rightShift(uint256 a, uint256 shift) public pure returns (uint256) {
        uint256 result = a >> shift;
        return result;
    }
}


Output:

Bitwise Operations

 

Array of Bytes = a little difference

In Solidity, an array of bytes is used to store a dynamic sequence of bytes. This can be useful for storing data that does not have a fixed size or structure, such as raw data or arbitrary messages.

An array of bytes is declared using the syntax bytes or bytesX, where X is a number indicating the size of the array. The maximum size of the array is 2^256-1.

Syntax:

bytes memory byteArray; // declare an empty array of bytes

bytes1 byte1Array; // declare an array of 1 byte

bytes2 byte2Array; // declare an array of 2 bytes

bytes32 byte32Array; // declare an array of 32 bytes

To initialize an array of bytes with some data, you can use the hex notation to represent the bytes in hexadecimal format.

Example:

bytes memory byteArray = hex”deadbeef”;

In the above example, an array of bytes is declared and initialized with the value 0xdeadbeef.

Solidity




pragma solidity ^0.8.0;
  
contract ByteArray {
    function concatenate(bytes memory a, bytes memory b) public pure returns (bytes memory) {
        bytes memory result = new bytes(a.length + b.length);
        uint i;
        uint j = 0;
        for (i = 0; i < a.length; i++) {
            result[j++] = a[i];
        }
        for (i = 0; i < b.length; i++) {
            result[j++] = b[i];
        }
        return result;
    }
}


Input:

a: 0x68656c6c6f (hex representation of “hello”)

b: 0x20776f726c64 (hex representation of ” world”)

Output:

result: 0x68656c6c6f20776f726c64 (hex representation of “hello world”)

Array of Bytes

 

Bytes as Function Arguments

You can use bytes as function arguments in Solidity. When you pass a bytes argument, you can manipulate and work with the byte data within the function.

Example:

Solidity




// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
  
contract BytesFunctionExample {
    // Prints the length of a bytes array
    function printBytesLength(bytes calldata data) public pure returns (uint) {
        return data.length;
    }
}


Output:

Bytes as Function Arguments

 

Conversion between addresses and bytes20

An Ethereum address is 20 bytes long, and you can convert it to a bytes20 type or vice versa. To convert an address to bytes20, you can use an explicit typecast.

Example: 

Solidity




// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
  
contract AddressBytes20Conversion {
    // Converts an Ethereum address to bytes20
    function addressToBytes20(address addr) public pure returns (bytes20) {
        return bytes20(addr);
    }
  
    // Converts a bytes20 value to an Ethereum address
    function bytes20ToAddress(bytes20 bytes20Addr) public pure returns (address) {
        return address(bytes20Addr);
    }
}


Output:

Conversion between addresses and bytes20

 

Advanced Operations with Bytes

Aside from the basic operations, you can perform more advanced operations with byte arrays. these are a few examples:

  • Concatenating two-byte arrays
  • Comparing two-byte arrays for equality

Example: 

Solidity




// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
  
contract AdvancedBytesOperations {
    // Concatenates two byte arrays
    function concat(bytes calldata a, bytes calldata b) public pure returns (bytes memory) {
        bytes memory result = new bytes(a.length + b.length);
        for (uint i = 0; i < a.length; i++) {
            result[i] = a[i];
        }
        for (uint j = 0; j < b.length; j++) {
            result[a.length + j] = b[j];
        }
        return result;
    }
  
    // Compares two byte arrays for equality
    function equalBytes(bytes calldata a, bytes calldata b) public pure returns (bool) {
        if (a.length != b.length) {
            return false;
        }
        for (uint i = 0; i < a.length; i++) {
            if (a[i] != b[i]) {
                return false;
            }
        }
        return true;
    }
}


Output:

Advanced Operations with Bytes

 



Last Updated : 01 Jun, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads