How to read Ethereum contract storage

Everybody is talking that data in contracts are public, but not everybody knows how to read it.

All contracts deployed to ethereum VM has dedicated storage where stores state. Here is an example how to read this storage with the web3.js library using eth.getStorageAt() method.

Contract example which we examine:

This contract is deployed in ropsten test net: at 0xf1f5896ace3a78c347eb7eab503450bc93bd0c3b

All parameters in the storage are indexed from the beginning. One index takes 256 bytes ant it fits 64 symbols. In this example, we have 10 parameters and we can iterate them:

let contractAddress = '0xf1f5896ace3a78c347eb7eab503450bc93bd0c3b'
for (index = 0; index < 10; index++){
console.log(`[${index}]` +
web3.eth.getStorageAt(contractAddress, index))
}
result:
[0] 0x000000000000000000000000000000000000000000000000000000000000000f
[1] 0x00000000000000000000000059b92d9a0000000000000000000000000000429f
[2] 0x0000000000000000000000000000000074657374310000000000000000000000
[3] 0x7465737431323336000000000000000000000000000000000000000000000000
[4] 0x6c65747320737472696e6720736f6d657468696e67000000000000000000002a
[5] 0x0000000000000000000000000000000000000000000000000000000000000000
[6] 0x0000000000000000000000000000000000000000000000000000000000000000
[7] 0x0000000000000000000000000000000000000000000000000000000000000002
[8] 0x0000000000000000000000000000000000000000000000000000000000000002
[9] 0x0000000000000000000000000000000000000000000000000000000000000000

Let`s have a deeper look into each parameter

Index 0 — storeduint1

let contractAddress = '0xf1f5896ace3a78c347eb7eab503450bc93bd0c3b'
let index = 0
console.log(web3.eth.getStorageAt(contractAddress, index))
console.log('DEC:' + web3.toDecimal(web3.eth.getStorageAt(contractAddress, index)))
result:
0x000000000000000000000000000000000000000000000000000000000000000f
DEC:15

constuint

Constants are not stored in a storage. Available only in code.

Index 1 — investmentsLimit, investmentsDeadlineTimeStamp

let index = 1
console.log(web3.eth.getStorageAt(contractAddress, index))
result:
0x00000000000000000000000059b92d9a0000000000000000000000000000429f
DEC: 1505308058 and 17055

In index 1 is merged 2 properties to optimize storage usage.

index 2 — string1

index = 2
console.log(web3.eth.getStorageAt(contractAddress, index))
console.log('ASCII: ' +
web3.toAscii(web3.eth.getStorageAt(contractAddress, index)))
result: 
0x0000000000000000000000000000000074657374310000000000000000000000
ASCII: test1

index 3 — string2

index = 3
console.log(web3.eth.getStorageAt(contractAddress, index))
console.log('ASCII: ' +
web3.toAscii(web3.eth.getStorageAt(contractAddress, index)))
result:
0x7465737431323336000000000000000000000000000000000000000000000000
ASCII: test1236

index 4— string3

index = 4
console.log(web3.eth.getStorageAt(contractAddress, index))
console.log('ASCII: ' +
web3.toAscii(web3.eth.getStorageAt(contractAddress, index)))
result:
0x6c65747320737472696e6720736f6d657468696e67000000000000000000002a
ASCII: lets string something * (42)

End symbol 2a (dec 42) is length of the stored string. (more details http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage)

index 5 — uints1

index = 5
console.log(web3.eth.getStorageAt(contractAddress, index))
result:
0x0000000000000000000000000000000000000000000000000000000000000000
PROBLEM!!!!

Mappings have a different indexation and should be read in other way. To read mapping value you should know the key value. Otherwise, read mapping value is impossible.

index = '0000000000000000000000000000000000000000000000000000000000000005'
key = '00000000000000000000000xbccc714d56bc0da0fd33d96d2a87b680dd6d0df6'
let newKey =  web3.sha3(key + index, {"encoding":"hex"})
console.log(web3.eth.getStorageAt(contractAddress, newKey))
console.log('DEC: ' + web3.toDecimal(web3.eth.getStorageAt(contractAddress, newKey)))
result:
0x0000000000000000000000000000000000000000000000000000000000000058
DEC: 88

index6 — structs1

index = "0000000000000000000000000000000000000000000000000000000000000006"
key = "00000000000000000000000xbccc714d56bc0da0fd33d96d2a87b680dd6d0df6"
let newKey =  web3.sha3(key + index, {"encoding":"hex"})
console.log(web3.eth.getStorageAt(contractAddress, newKey))
console.log('ASCII: ' +
web3.toAscii(web3.eth.getStorageAt(contractAddress, newKey)))
result:
0x6465766963654272616e64000000000000000000000000000000000000000016
ASCII: deviceBrand

To read second struct value you need to increase newKey value by 1

function increaseHexByOne(hex) {
let x = new BigNumber(hex)
let sum = x.add(1)
let result = '0x' + sum.toString(16)
return result
}
index = "0000000000000000000000000000000000000000000000000000000000000006"
key = "00000000000000000000000xbccc714d56bc0da0fd33d96d2a87b680dd6d0df6"
let newKey =  increaseHexByOne(
web3.sha3(key + index, {"encoding":"hex"}))
console.log(web3.eth.getStorageAt(contractAddress,newKey))
console.log('ASCII: ' +
web3.toAscii(web3.eth.getStorageAt(contractAddress, newKey)))
result:
0x6465766963655965617200000000000000000000000000000000000000000014
ASCII: deviceYear

If you want third struct value increase newKey once more.

index 7— uintarray

index = "7"
console.log(web3.eth.getStorageAt(contractAddress, index))
result:
0x0000000000000000000000000000000000000000000000000000000000000002

This array has 2 items

index = "0000000000000000000000000000000000000000000000000000000000000007"
let newKey = web3.sha3(index, {"encoding":"hex"})
console.log(web3.eth.getStorageAt(contractAddress, newKey))
console.log('DEC: ' +
web3.toDecimal(web3.eth.getStorageAt(contractAddress, newKey)))
result:
0x0000000000000000000000000000000000000000000000000000000000001f40
DEC: 8000

newKey = increaseHexByOne(web3.sha3(index, {"encoding":"hex"}))
console.log(web3.eth.getStorageAt(contractAddress, newKey))
console.log('DEC: ' +
web3.toDecimal(web3.eth.getStorageAt(contractAddress, newKey)))
result:
0x0000000000000000000000000000000000000000000000000000000000002328
DEC: 9000

index 8— deviceDataArray

index = "0000000000000000000000000000000000000000000000000000000000000008"
let newKey = web3.sha3(index, {"encoding":"hex"})
console.log(web3.eth.getStorageAt(contractAddress, newKey))
console.log('ASCII: ' +
web3.toAscii(web3.eth.getStorageAt(contractAddress, newKey)))
result:
0x6465766963654272616e64000000000000000000000000000000000000000016
ASCII: deviceBrand

index = "0000000000000000000000000000000000000000000000000000000000000008"
let newKey = increaseHexByOne(web3.sha3(index, {"encoding":"hex"}))
console.log(web3.eth.getStorageAt(contractAddress, newKey))
console.log('ASCII: ' +
web3.toAscii(web3.eth.getStorageAt(contractAddress, newKey)))
result:
0x6465766963655965617200000000000000000000000000000000000000000014
ASCII: deviceYear

Increase by 2 result:

0x776561724c6576656c0000000000000000000000000000000000000000000012

ASCII: wearLevel

Increase by 3 you enter to second item into array result:

0x6465766963654272616e64320000000000000000000000000000000000000018

ASCII: deviceBrand2

Sources:
https://github.com/ethereum/solidity/issues/1550
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getstorageat
https://ethereum.stackexchange.com/questions/13910/how-to-read-a-private-variable-from-a-contract
https://github.com/ethereum/web3.js/issues/445

Check out our testing ground — Aigang!

More about Aigang — http://bit.ly/AigangWebsite
Aigang Whitepaper — http://bit.ly/2hwAtkt
Aigang on Telegram — http://bit.ly/AigangTelegram
Get Aigang Latest Updates — http://bit.ly/AigangUpdates