How to use an array of strings in a solidity mapping? - ethereum

I need to store a mapping between a string to items which are 128 bytes long in a solidity contract. The problem is that the longest bytes data type is bytes32, which is not long enough, and if I try to store my strings in a string array I get the following error:
This type is only supported in the experimental ABI encoder. Use "pragma experimental abiencoderv2;" to enable this feature
So I cannot use bytes32 because it's not big enough. I cannot use bytes because it's not supported. And I cannot use string[] because it's experimental and not recommended in production.
Any solution?
This is the contract I'm using:
pragma solidity ^0.4.24;
contract SomeData {
struct Data {
string id;
string[3] items;
}
mapping (string => Data) dataItems;
function addData(string id, string[3] items) public {
Data memory data = Data(id, items);
data.id = id;
data.items = items;
dataItems[id] = data;
}
function getDataItems(string id) public view returns (string[3]){
return dataItems[id].items;
}
}

Since you know the maximum length of each individual string, you can use a static 2 dimensional array:
contract SomeData {
struct Data {
string id;
byte[128][100] items;
}
mapping (string => Data) dataItems;
function addData(string id, byte[128][100] items) public {
Data memory data = Data(id, items);
data.id = id;
data.items = items;
dataItems[id] = data;
}
function getDataItems(string id) public view returns (byte[128][100]){
return (dataItems[id].items);
}
}
Note, only 2-dimensional dynamic arrays are not allowed. You could even use byte[128][] if you wanted to.

Related

I get a declaration error in solidity when I create a variable with "var"

I'm trying to create a variable "project" to store data from a mapping but I get "Decalration error, undefined identifier" on project = projects[addr]
function getProjectInfo(address addr) public view returns (string memory name, string memory url, uint funds){
var project = projects[addr];
}```
Use explicit variable type definition:
pragma solidity ^0.5.8;
contract Test
{
struct Project
{
bytes32 name ;
}
mapping (address => Project) projects ;
constructor () public {
}
function getProjectInfo(address addr) public view returns (string memory name, string memory url, uint funds)
{
Project memory project = projects[addr];
// ...
}
}

How to encode tuple as input parameter to function using web3j

I am attempting to call a solidity function that looks something like the following:
function fillOrder(
Order memory order,
uint256 takerAssetFillAmount,
bytes memory signature
)
Using web3j I would create the function similar to below, however I'm not quite sure how to represent the order which is represented as a struct in Solidity.
List<Type> inputParams = Arrays.asList(???, new
Uint256(takerAssetFillAmount), new Bytes32(signture));
new Function("fillOrder", inputParams, Collections.emptyList());
Any pointers on how I should represent the struct?
Thanks.
You can wrap parameters with square brackets.
For example, let's say I have a contract:
contract Test {
struct Foo {
uint a;
string b;
address c;
}
function bar (Foo memory foo) public {
c = foo.c;
}
}
I can call bar function with web3.js like this:
contract.methods.foo([123, "123", "0xABC...."]).send({ from: '0x...' })
here is the contract address https://goerli.etherscan.io/address/0xd5999bf0ce31a1d9d6a6de2bf03feaff1913cee5#writeContract
in the write function , createSwapOrder is asking nested Tuple . here is the solidity code to show the structure of tuple :
struct Side {
address user;
bytes signedRequiredOutput;
ERC20Component[] erc20s;
ERC721Component[] erc721s;
ERC1155Component[] erc1155s;
}
struct ERC20Component {
uint256 amount;
address underlying;
// A signed approval transaction giving `amount` transfer rights
// of token `underlying` to address(this).
// bytes signedApproval;
}
struct ERC721Component {
uint256 tokenId;
address collection;
// A signed approval transaction giving `tokenId` tranfer rights
// of token `collection` to address(this).
// bytes signedApproval;
}
struct ERC1155Component {
uint256 tokenId;
uint256 amount;
address collection;
// A signed approval transaction giving `tokenId` tranfer rights
// of token `collection` to address(this).
// bytes signedApproval;
}
struct Order {
Side side0;
Side side1;
uint256 expiry;
bytes32 hashlock;
bytes32 preimage;
bool completed;
}
event OrderCreated(address indexed user, bytes32 orderId);
uint256 public totalOrders;
mapping(bytes32 => Order) public orders;
function createSwapOrder(
Side calldata side0,
bytes32 hashlock,
uint256 timelock
) public {
...
}
and in first args side0 is asking a nested tuple and this tuple formet should be like this
["0x5B38Da6a701c568545dCfcB03FcB875f56beddC4","0x00",[["32","0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"]],[["32","0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"]],[["32","32","0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"]]],
i hope you can understand the structure how its provided !! and sure it working
Web3j offers such classes as StaticStruct and DynamicStruct where you define your struct object via primitives. Here is the sample from my project:
class Value: DynamicStruct {
private lateinit var offer: String
private lateinit var availableSince: BigInteger
private lateinit var availabilityEnd: BigInteger
private var isConsumed: Boolean = false
private lateinit var lockedUntil: BigInteger
constructor(
offer: String,
availableSince: BigInteger,
availabilityEnd: BigInteger,
isConsumed: Boolean,
lockedUntil: BigInteger
) : super(
Utf8String(offer), Uint256(availableSince),
Uint256(availabilityEnd), Bool(isConsumed),
Uint256(lockedUntil)
) {
this.offer = offer
this.availableSince = availableSince
this.availabilityEnd = availabilityEnd
this.isConsumed = isConsumed
this.lockedUntil = lockedUntil
}
constructor(
offer: Utf8String,
availableSince: Uint256,
availabilityEnd: Uint256,
isConsumed: Bool,
lockedUntil: Uint256
) : super(offer, availableSince, availabilityEnd, isConsumed, lockedUntil) {
this.offer = offer.value
this.availableSince = availableSince.value
this.availabilityEnd = availabilityEnd.value
this.isConsumed = isConsumed.value
this.lockedUntil = lockedUntil.value
}
}
Ideally you just need to pass this struct instance to you contract method as a parameter where contract is autogenerated over $web3j solidity generate -b /path/to/<smart-contract>.bin -a /path/to/<smart-contract>.abi -o /path/to/src/main/java -p com.your.organisation.name command.
However personally I faced with issue to make this console command working and have to implement required logic by my own. This is in the progress now.

How to return a list of user-defined struct in solidity?

I want to return a list of user-defined struct
function sortByEtherValues (string category) public view returns (JobStruct[]) {
...
}
I am getting the following error:
Failed to decode output: Error: Unsupported or invalid type: tuple
Solidity supports returning multiple values:
struct JobStruct {
uint a;
uint b;
uint c;
}
function sortByEtherValues (string category) public view returns (uint, uint, uint) {
JobStruct memory js = JobStruct(1, 2, 3);
return (js.a, js.b, js.c);
}
It'll be a little dirty to return an array of structs by decomposing your struct elements. If you can, you should try to return just a single JobStruct by getting the number of elements by category in a separate function, then adding the index to sortByEtherValues.

What is the best approach in Solidity for pagination (straightforward and based on some filter(s)) capabilities?

Let's suppose that we have following structures in Solidity contract :
struct EntityA {
string lessThen32ByteString1;
string moreThen32ByteString1;
string lessThen32ByteString2;
string moreThen32ByteString3;
bool flag;
uint var1;
uint var2;
uint var3;
uint var4;
ProposalStatus proposalStatus;}
// 100K entities EntityA[] public items;
We have now following implementation (with helper functions for string to byte32 conversion, splitting string to a few byte32 parts and so on) :
function getChunkOfPart1EntityADetails(uint filterAsUint, uint offset, uint limit) public constant
returns (bytes32[100] lessThen32ByteString1Arr, bytes32[100] moreThen32ByteString1PrefixArr, bytes32[100] moreThen32ByteString1SuffixArr) {
}
function getChunkOfPart2EntityADetails(uint filterAsUint, uint offset, uint limit) public constant
returns (bytes32[100] lessThen32ByteString2Arr, bytes32[100] moreThen32ByteString2PrefixArr, bytes32[100] moreThen32ByteString2SuffixArr) {
}
function getChunkOfPart3EntityADetails(uint filterAsUint, uint offset, uint limit) public constant
returns (bool[100] flagArr, uint[100] var1Arr, uint[100] var2Arr, uint[100] var3Arr, uint[100] var4Arr, ProposalStatus[100] proposalStatusArr,) {
}
Current implementation does not look good from design perspective, furthermore each additional filter support requires contract changes due to absence any query language support.
Use case example : retrieve 10 entities based on specified proposalStatus and offset 50 (6th page, page size is 10 entities (maybe 20, 50, 100))
I know I'm late but here you go. This will return an array of uints. You can then use this array on the client to resolve the id's in the getter for the array that stores your data.
struct Zombie {
//data
};
Zombie[] public zombies;
function paginateAllZombies(uint _resultsPerPage, uint _page) external view returns (uint[]) {
Zombie[] memory result = new Zombie[](_resultsPerPage);
for(uint i = _resultsPerPage * _page - _resultsPerPage; i < _resultsPerPage * _page; i++ ){
result[i] = i;
} //CONVERT TO SAFEMATH
return result;
}
So once you get this returned you can .map() the result in your client app and call zombies(uint _index).
If you need more details on this approach please check out cryptozombies.io
Not sure if this is the best approach though.
Hopefully in the future you can just do:
function paginateAllZombies(uint _resultsPerPage, uint _page) external view returns (Zombie[]) {
uint[] memory result = new uint[](_resultsPerPage);
for(uint i = _resultsPerPage * _page - _resultsPerPage; i < _resultsPerPage * _page; i++ ){
result[i] = zombies[i];
} //CONVERT TO SAFEMATH
return result;
}

String array in solidity

I came across quite a common problem that it seems I can't solve elegantly and efficiently in solidity.
I've to pass an arbitrary long array of arbitrary long strings to a solidity contract.
In my mind it should be something like
function setStrings(string [] row)
but it seems it can't be done.
How can I solve this problem?
This is a limitation of Solidity, and the reason is that string is basically an arbitrary-length byte array (i.e. byte[]), and so string[] is a two-dimensional byte array (i.e. byte[][]). According to Solidity references, two-dimensional arrays as parameters are not yet supported.
Can a contract function accept a two-dimensional array?
This is not yet implemented for external calls and dynamic arrays - you can only use one level of dynamic arrays.
One way you can solve this problem is if you know in advanced the max length of all of your strings (which in most cases are possible), then you can do this:
function setStrings(byte[MAX_LENGTH][] row) {...}
December 2021 Update
As of Solidity 0.8.0, ABIEncoderV2, which provides native support for dynamic string arrays, is used by default.
pragma solidity ^0.8.0;
contract Test {
string[] public row;
function getRow() public view returns (string[] memory) {
return row;
}
function pushToRow(string memory newValue) public {
row.push(newValue);
}
}
String arrays as parameters aren't supported yet in solidity.
You can convert the array elements to a byte string and then deserialize that byte string back to the array inside the function. Although this can prove to be quite expensive you can try it if you don't have a choice. You can follow this short article to serialize/deserialize any datatype in solidity.
all solutions you need:-
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.1;
contract HelloWorld {
string[] strings;
// push one string to array
function pushToStrings(string memory _data) public{
strings.push(_data);
}
//get all the strings in array form
function GetAllStrings() view public returns(string[] memory){
return strings;
}
//get nth string of strings array
function GetNthStrings(uint x) view public returns(string memory){
return strings[x];
}
//push array of strings in strings
function pushStringsArray(string[] memory someData) public{
for (uint i=0; i < someData.length; i++) {
strings.push(someData[i]);
}
}
//change whole strings, take array of strings as input
function changeWholeString(string[] memory someData) public{
strings=someData;
}
}
string array is not available in Solidity
because String is basically array of character
Nested dynamic arrays not implemented
There are two types arrays in solidity: static array and dynamic array.
declaration of array
static array: These has fixed size.
int[5] list_of_students;
list_of_students = ["Faisal","Asad","Naeem"];
we access the values using index number
Dynamic arrays: The size of these arrays dynamically increases or decreases.
int[] list_of_students;
list_of_students.push("Faisal");
list_of_students.push("Asad");
list_of_students.push("Smith");
we can access the value using index number.
push and pop function is used to insert and delete the values. length function is used to measure the length of the array.
It can be done by using
pragma experimental ABIEncoderV2;
at the top of your contract you may then use dynamic arrays of strings. Ex.
string[] memory myStrings;
This is an example contract to manage the array push, get, getAll,
and remove
pragma solidity ^0.8.4;
contract Array {
string[] private fruits = ["banana", "apple", "avocado", "pineapple", "grapes"];
function push(string memory item) public {
fruits.push(item);
}
function get(uint256 index) public view returns (string memory) {
return fruits[index];
}
function remove(uint256 index) public returns (bool) {
if (index >= 0 && index < fruits.length) {
fruits[index] = fruits[fruits.length - 1];
fruits.pop();
return true;
}
revert("index out of bounds");
}
function getAll() public view returns (string[] memory) {
return fruits;
}
}