How to access private data from a smart contract

One of the biggest advantages of blockchains or at least public blockchains is that they are “public”. Nothing can be hidden and all the transactions are publicly visible. If you are a smart contract developer, you must have heard about the access modifier and particularly the “private” access modifier.

Access Modifiers in Solidity

We know there are four types: 

  • Public: Available to EOAs, external contracts along with the same contract.
  • External: Available to EOAs as well as external contracts but not to the same contract
  • Internal: Available to Contracts that inherit the contract with internal function
  • Private: Available to none. Only visible within the contract.

As stated above, “Private” variables are not accessible to the outside world. If you deploy a smart contract and try to access the private variable, it won’t allow you to. Then exactly how come blockchains are public? And if they are public, can we access this private data?

How to access private data

Smart contracts are accounts with code hash i.e. Ethereum account that can store bytecode. All the state data is stored in the chain’s world state which is inherently public.

The bytecode of a smart contract doesn’t allow you to read certain variables which are marked “private” if you send the message call to the smart contract address. But we have another way round. We can directly access this world state of the chain.

To understand how this works, recall from this blog post that variables in Solidity are stored in 32-byte (256-bits) storage slots, and that data is stored sequentially in these storage slots based on the order in which these variables are declared. 

So if you know the location or “slot” at which a particular variable is stored, you can directly read the contents at this location and then decode the bytes to read the required values.

Makes sense? Let’s demystify this with one example.

Accessing Private data of a smart contract using Ethers.js

Let’s take following contract as an example:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;
contract PrivateAccessDemo {
uint256 private privateVariable;
uint256 public publicVariable;
constructor(uint256 privateValue, uint256 publicValue) {
privateVariable = privateValue;
publicVariable = publicValue;
}
}

privateVariable is the private variable and publicVariable is the public variable. Now after deploying the contract, of course, we won’t be able to read privateVariable from the contract directly. However, we can use providers.getStorageAt() function provided by ethers.js

This function takes two params:

  1. Address of the contract
  2. Slot to read the bytes from

In our example, privateVariable is 256bit integer and takes the entire storage slot. Also, it’s the first declared variable in the contract so it is going to occupy slot 0.

So we can read the data at slot 0 using:

const slot0Bytes = await ethers.provider.getStorageAt(demo.address, 0);

This tutorial is available here. Clone the repo, install the dependencies and try: 

npx hardhat test

It will run the testcase which confirms that data read from slot 0 is nothing but integer 2.

So! Private data in a smart contract is not private as such since we are dealing with public blockchains. Nothing can be really hidden from the external world in blockchains. If we want to store sensitive information on-chain, there are some encryption techniques that must be used. Using encryption for storing data, encrypted hashes are visible to everyone but only the one with the required key can decrypt and read the information.

%d bloggers like this: