I have a contract where an ID of type uint can be owned by an address through mapping (uint => address) owner. I have another mapping that keeps tabs of which IDs a particular address might own: mapping(address=> uint[]). I'd like to create a function that returns all the IDs a certain address might own to an off-chain call. What is a good way I can achieve this? I considered creating a string that appends all the IDs that are separated by some delimiter, but that seems inefficient.

Thank you for any help in advance.

  • Not sure if I've understood this but why not just return an array? – Edmund Edgar Jul 25 '17 at 1:18

I'd like to create a function that returns all the IDs a certain address might own to an off-chain call.

If you want to track your assets on-chain then you can use constant functions to retrieve them. Such functions are not broadcasted to the network and do not cost you any gas. Therefore this is a good way to go forward without having to pay for gas.

Unfortunately, Solidity does not yet allow you to return arrays so you have to fetch the elements one by one. First get the number of elements in the array then get them all:

mapping (uint => address) public owner;
mapping (address=> uint[]) public ids;

function getLength(address idOwner) constant returns (uint len) {
  len = ids[idOwner].length;

Now where did I implement the function to get the actual IDs one-by-one? By simply declaring ids as public a getter function is generated for us.

If you really want to track more of the assets off-chain then you could go via solutions such as IPFS. IPFS addresses any asset by a unique hash. You can use this hash as an identifier on-chain but retrieve the actual asset off-chain, e.g. on IPFS. Typically, architectures use the blockchain as a pointer-layer that gives you the address in the data layer which could be IPFS.

| improve this answer | |

I found a good solution to my problem by using Events thanks to this blogpost by Consensys.

Here's the solution from the post:

The third use case is quite different from what’s been covered, and that is using events as a significantly cheaper form of storage. In the Ethereum Virtual Machine (EVM) and Ethereum Yellow Paper[2], events are referred to as logs (there are LOG opcodes). When speaking of storage, it would be technically more accurate to say that data can be stored in logs, as opposed to data being stored in events. However, when we go a level above the protocol, it is more accurate to say that contracts emit or trigger events which the frontend can react to. Whenever an event is emitted, the corresponding logs are written to the blockchain. The terminology between events and logs is another source of confusion, because the context dictates which term is more accurate.

Logs were designed to be a form of storage that costs significantly less gas than contract storage. Logs basically[3] cost 8 gas per byte, whereas contract storage costs 20,000 gas per 32 bytes. Although logs offer gargantuan gas savings, logs are not accessible from any contracts[4]. Nevertheless, there are use cases for using logs as cheap storage, instead of triggers for the frontend. A suitable example for logs is storing historical data that can be rendered by the frontend. A cryptocurrency exchange may want to show a user all the deposits that they have performed on the exchange. Instead of storing these deposit details in a contract, it is much cheaper to store them as logs. This is possible because an exchange needs the state of a user’s balance, which it stores in contract storage, but does not need to know about details of historical deposits. contract CryptoExchange { event Deposit(uint256 indexed _market, address indexed _sender, uint256 _amount, uint256 _time); function deposit(uint256 _amount, uint256 _market) returns (int256) { // perform deposit, update user’s balance, etc Deposit(_market, msg.sender, _amount, now); } Suppose we want to update a UI as the user makes deposits. Here is an example of using an event (Deposit) as an asynchronous trigger with data (_market, msg.sender, _amount, now). Assume cryptoExContract is an instance of CryptoExchange: var depositEvent = cryptoExContract.Deposit({_sender: userAddress}); depositEvent.watch(function(err, result) { if (err) { console.log(err) return; } // append details of result.args to UI }) Improving the efficiency of getting all events for a user is the reason why the _sender parameter to the event is indexed: event Deposit(uint256 indexed _market, address indexed _sender, uint256 _amount, uint256 _time) By default, listening for events only starts at the point when the event is instantiated. When the UI is first loading, there are no deposits to append to. So we want to retrieve the events since block 0 and that is done by adding a fromBlock parameter to the event. var depositEventAll = cryptoExContract.Deposit({_sender: userAddress}, {fromBlock: 0, toBlock: 'latest'}); depositEventAll.watch(function(err, result) { if (err) { console.log(err) return; } // append details of result.args to UI }) When the UI is rendered depositEventAll.stopWatching() should be called.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.