You need to break the interface down into smaller chunks. In practice, you'll want to extend the internal storage pattern a little, I think.
Iterable mapping comes to mind. You keep lists of keys to mappings in arrays. Something like this:
struct VoterStruct {
address voterAddress;
uint256 tokensBought;
mapping (bytes32 => uint256) tokensUsed;
bytes32[] tokenIndex; // a list of mapping keys that exist for the voter
}
mapping (address => VoterStruct) public voterStructs;
Then you can expose functions that allow iterating over lists while the contract returns one set of values at a time. Very sketchy to inspire some ideas:
// client can discover how may tokens are in the list for one voter
function getVoterTokenCount(address voter) returns(uint tokenCount) {
return voterStructs[voter].tokenIndex.length;}
// client can discover the tokenId for a voter in a given row
function getVoterTokenAtIndex(address voter, uint index) returns(bytes32 tokenId) {
return voterStructs[voter].tokenIndex[index];}
// client can discover token used by voter and tokenId
function getVoterTokenUsed(address voter, bytes32 tokenId) returns(uint tokensUsed) {
return voterStructs[voter].tokensUsed[tokenId];}
// append keys to the list as you go
function insertVoterToken(address voter, bytes32 tokenId) returns(bool success) {
voterStructs[voter].tokenIndex.push(tokenId);
return true;}
Hope I didn't flub the syntax. It might be possible to sketch out the contract a little more. Would need a general description of what it's going to do. When I use this pattern, I always add some rules to ensure there are no duplicate keys, and so on.
Anyway, possibly the hints will inspire some ideas.
Hope it helps.
The main issue with your code is that you are trying to return a uint[]
which is an array
, however you defined your variable as a mapping of mappings, and mapping
and array
are different types.
You could get your code to compile like this:
pragma solidity ^0.4.24;
contract test {
mapping (address => uint[]) public transactions;
constructor() public {
transactions[msg.sender].push(123456);
transactions[msg.sender].push(789101);
transactions[msg.sender].push(865436);
}
function getTransactions() public view returns (uint[]) {
return transactions[msg.sender];
}
}
However in general, I would not recommend a single function which returns all the values. While this may be useable in a web front-end, it will not scale as a normal contract function, since eventually you will reach a number of objects in the array where the gas cost to return them all will be too large for the evm.
Instead, I suggest you break it into two functions:
pragma solidity ^0.4.24;
contract test {
mapping (address => uint[]) public transactions;
constructor() public {
transactions[msg.sender].push(123456);
transactions[msg.sender].push(789101);
transactions[msg.sender].push(865436);
}
function getTransactionsLength() public view returns (uint) {
return transactions[msg.sender].length;
}
function getTransactionsValue(uint index) public view returns (uint) {
return transactions[msg.sender][index];
}
}
One is getTransactionsLength()
, and the other is getTransactionsValue()
. Both of these functions return just a single uint, which means you will have predictable output size and gas costs, which means it can be used in other parts of your contract.
Furthermore, you will also create better user experiences by controlling the number of values you are showing. If you need to show all the values, you could first get the length
, and then loop the value call from 0 to length-1
.
But even better, you may only want to show the last 10 transactions in the UI, where then you can loop from length-1 to length-11
. These may seem like complexities at first, but in reality will be simplifications to your dApp is it grows; things you will really come to appreciate from immutable programs :)
Best Answer
According to the Solidity docs you cannot return a mapping:
Taken from https://docs.soliditylang.org/en/v0.8.11/types.html#mapping-types
The error you mentioned is because it is necessary to define the data location for the return parameter(s) (see https://docs.soliditylang.org/en/v0.8.11/types.html?highlight=mapping#data-location), which needs to be
memory
orcalldata
(another reason that you cannot use a mapping, as it only exists instorage
)