7

I was trying to get keys of a mapping to star name and address of particular contract (not exactly) in solidity contract

mapping(string=>address) nameOfAccounts;

What i am expecting is a method like the Object.keys(nameOfAccounts) in Javascript. Is there any similar method ?. or should i use an additional array. The additional Array may cause extra gas cost(This is not an option that i prefer). Please share some insights

I wanted to know if there is another method that i can do if getting keys is not an option?

Mikko Ohtamaa
  • 82,057
  • 50
  • 264
  • 435
Mohamed Rashiq
  • 322
  • 1
  • 3
  • 12
  • 1
    you have tagged several items, however it may relate to those topics. In addition, you need to elaborate your question as if a javascript programmer may don't know what is blockchain. I guess your question should be in javascript, you may pass your logic in the filter/map method in javascript! – Ebrahim Jan 01 '21 at 17:44
  • 1
    I edited the question and removed JavaScript tag to protect the question – Mikko Ohtamaa Jan 02 '21 at 08:50
  • okay mikko, thanks – Mohamed Rashiq Jan 19 '21 at 07:00
  • Why not just keep an array to track the keys? Only append if not in map? – ystark Sep 03 '21 at 07:20

4 Answers4

8

Short answer: If the contract doesn't provide the list of map's object keys, you can't get it.

Long answer:

But since the blockchain is a public thing , you can get anything. To get all keys of the map, you would need to do this:

  • Setup archival node and enable tracing (you will need 9 terabytes disk, with SSD caching)
  • Get the traces of all the calls to your contract and search for SSTORE opcode (instruction)
  • The SSTORE instruction pops 2 parameters from the stack, Loc (location) and Val (Value) . The location is the key you are looking for.

If you don't have a budget for archival node, you can try Etherscan's API

There are answers on SO on how to read storage of a contract, might be useful.

There is another option, decompile the contract, and check its input, the input will have the key, then scan all transactions for that contract, and extract the keys by processing the input (the input is the tx.Data() field). This option is easier if you have the some knowledge about the contract, or if you have the sources, that's will be the easiest thing (process the Input)

There is also a function in StateDB object type called ForEachStorage(). It doesn't have a front-end from the RPC api, but with a little bit of effort you could implement your own RPC function to access it and put it on a Full node. This function accepts the address of the contract and a closure function , and it will iterate through the storage of the entire contract. Source: https://github.com/ethereum/go-ethereum/blob/0a3993c558616868e35f9730e92c704ac16ee437/core/state/statedb.go#L634 The only thing you would have to know is how the keys are built, and this can be only known from contract source.

Nulik
  • 6,748
  • 10
  • 60
  • 129
  • 4
    9 terabytes with ssd caching? you don't say.. let's all sell our livers to iterate over a mapping why don't we.. how about just doing mapping.keys() and then iterating over that? – Eight Rice Sep 06 '21 at 17:57
3

The best way to iterate over the keys is that when a key is added you also emit a Solidity event.

You get keys from the event logs. This can be done client-side (JavaScript, Python) but not within Solidity itself, as EVM does not have access to event data.

Solidity/EVM does not store iterable keys due to efficiency reasons in execution constrained environment.

More information here.

Pang
  • 9,564
  • 146
  • 81
  • 122
Mikko Ohtamaa
  • 82,057
  • 50
  • 264
  • 435
1

Try to use Goodmapping.sol instead of raw mapping in Solidity.

https://github.com/lidangzzz/GoodMapping.sol

Here is a quick example:

// Import map uint256 => address
import "goodmapping/contracts/Map_UA.sol";

// A mapping uint256 => address
Map_UA private my_map; 

// insert some key-value pare
my_map.set(0x123,1);
my_map.set(0x234,2);

// get all keys 
address[] memory keys = my_map.keys();

// iterate each key-value pare
for (uint256 index = 0; index < keys.length; index++)
{
  //get value 
  bool bExist = false;
  uint256 value = 0;
  (bExist, value) = my_map.get(keys[index]);
}
0

Well, the easiest way to get the keys for your mapping variable, you can use the tool SmartMuv. This tool provides the list of mapping keys with their values and you can use it anyway.

Maha Ayub
  • 1
  • 1