String array in solidity - ethereum

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;
}
}

Related

solidity: returns(string[] memory) won't permit return of string[5]?

I'm wondering why if my function is meant to return a string of indeterminate length, I can't return a string array of definite length.
This function, for example doesn't compile
function BindingTypeList() public pure returns(string[] memory) {
return ["DocumentTemplate", "Definition", "RepAndWarranty", "Restriction", "Entitlement"];
}
the error message is TypeError: Return argument type string memory[5] memory is not implicitly convertible to expected type (type of first return variable) string memory[] memory.
It seems like they are saying that string[5] is incompatible with returns(string[]). I don't get that at all. Is there a workaround?
You're trying to return a dynamic array (string[]), but in fact the return statement instantiates a fixed array (string[5]).
Quick fix: Return string[5] memory (instead of string[] memory).
Solidity is currently (v0.8) not able to resize an in-memory array. So you can't just define an empty dynamic array in memory and then push() into it. But there's a workaround to return a dynamic array containing the predefined list of items:
You can define a dynamic array with 5 empty items, and then re-assign their values.
function BindingTypeList() public pure returns(string[] memory) {
string[] memory arr = new string[](5); // 5 empty items
arr[0] = "DocumentTemplate";
arr[1] = "Definition";
arr[2] = "RepAndWarranty";
arr[3] = "Restriction";
arr[4] = "Entitlement";
return arr;
}

Solidity: Data location must be "memory" or "calldata" for return parameter in function

I am learning Ethereum dev in Solidity and trying to run a simple HelloWorld program but ran into the following error:
Data location must be "memory" or "calldata" for return parameter in function, but none was given.
My code:
pragma solidity ^0.8.5;
contract HelloWorld {
string private helloMessage = "Hello world";
function getHelloMessage() public view returns (string){
return helloMessage;
}
}
You need to return string memory instead of string.
Example:
function getHelloMessage() public view returns (string memory) {
return helloMessage;
}
The memory keyword is the variable data location.
For those reading this who have similar code, 'memory' may not necessarily be the correct word to use for you. You may need to use the words 'calldata' or 'storage' instead. Here is an explanation:
Memory, calldata (and storage) refer to how Solidity variables store values.
For example:
1. Memory: here is an example using the word 'memory':
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import 'hardhat/console.sol'; // to use console.log
contract MemoryExample {
uint[] public values;
function doSomething() public
{
values.push(5);
values.push(10);
console.log(values[0]); // logged as: 5
modifyArray(values);
}
function modifyArray(uint[] memory arrayToModify) pure private {
arrayToModify[0] = 8888;
console.log(arrayToModify[0]) // logged as: 8888
console.log(values[0]) // logged as: 5 (unchanged)
}
}
Notice how the 'values' array was not changed in the private function because 'arrayToModify' is a copy of the array and does not reference (or point to the array that was passed in to the private function.
2. Calldata is different and can be used to pass a variable as read-only:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
contract CallDataExample {
uint[] public values;
function doSomething() public
{
values.push(5);
values.push(10);
modifyArray(values);
}
function modifyArray(uint[] calldata arrayToModify) pure private {
arrayToModify[0] = 8888; // you will get an error saying the array is read only
}
}
3. Storage: a third option here is to use the 'storage' keyword:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import 'hardhat/console.sol'; // to use console.log
contract MemoryExample {
uint[] public values;
function doSomething() public
{
values.push(5);
values.push(10);
console.log(values[0]); // logged as: 5
modifyArray(values);
}
function modifyArray(uint[] storage arrayToModify) private {
arrayToModify[0] = 8888;
console.log(arrayToModify[0]) // logged as: 8888
console.log(values[0]) // logged as: 8888 (modifed)
}
}
Notice how by using the memory keyword, the arrayToModify variable references the array that was passed in and modifies it.
use string memory instead of string.
storage - variable is a state variable (store on blockchain)
memory - variable
List item - is in memory and it exists while a function is being called
calldata - special data location that contains function arguments, only available for external functions
example code : Data Locations

How to return "Null" or an "Empty" object in Solidity?

I am currently writing a Smart Contract in Solidity. The smart contract, amongst other information, stores an array of properties object at the general level. The property object Looks like this:
struct PropertyObj {
string id;
uint weiPrice;
address owner;
}
Now there is a specific function that iterates over the array, finds the property and returns it (code below)
function getPropertyByid(string memory _propertyId)private view returns(PropertyObj memory){
for(uint i = 0; i<PropertyArray.length; i++){
if (keccak256(bytes((PropertyArray[i].id))) == keccak256(bytes((_propertyId)))) {
return PropertyArray[i];
}
return null;
}
}
The "Problem" is that, unlike other programming languages, Solidity does not allow to return null (as far as I am concerned).
In other words, if throughout the iteration we do not find the property, then what we shall return if we specified that we need to return PropertyObj memory in the function signature?
Solidity does not have null value, as you're correctly stating.
Your function can throw an exception using the revert() function.
It also seems that your implementation has a logical error. Your example would "return null" if the hash was not found during the first iteration. Instead, you may want to throw the exception after the loop has ended.
for(uint i = 0; i<PropertyArray.length; i++){
if (keccak256(bytes((PropertyArray[i].id))) == keccak256(bytes((_propertyId)))) {
return PropertyArray[i];
}
}
revert('Not found');
Other option would be to return the empty object (with default values, i.e. zeros), if it fits your use case.
for(uint i = 0; i<PropertyArray.length; i++) {
// ...
}
// not found, return empty `PropertyObj`
PropertyObj memory emptyPropertyObj;
return emptyPropertyObj;

Returning Struct Array in solidity

This is my contract code. Here I'm trying to store the coordinates of a particular trip. While storing the information contract executing fine. But When I retrieve the data, It should give the array of coordinates. But it is throwing an error.
reason: 'insufficient data for uint256 type'
contract TripHistory {
struct Trip {
string lat;
string lon;
}
mapping(string => Trip[]) trips;
function getTrip(string _trip_id) public view returns (Trip[]) {
return trips[_trip_id];
}
function storeTrip(string _trip_id, string _lat, string _lon) public {
trips[_trip_id].push(Trip(_lat, _lon));
}
}
What I'm missing here. Is there any other way to achieve what I'm trying here?
P.S: I'm new to solidity.
First of returning structs is not supported in Solidity directly. Instead you need to return every individual element in the struct as below.
Function xyz(uint256 _value) returns(uint256 User.x, uint256 User.y)
public {}
But then there’s an experimental feature that will help you with returning struct. All that you need to do is add the following after your first pragma line
pragma experimental ABIEncoderV2;
then continue with your code. That should work with no changes to your code.
An example of abiencoderv2 returning struct can be found at this link
It is not possible in solidity to return struct array.
As jlo said in this link, after version 0.8.0, it is possible to return a struct. jlo describes how to set and return an element of array of struct. Here I describe how to set, reset, and return a struct type variable.
I tested it and my test environment is:
private Ethereum network
Geth version 1.10.9-stable (for private network)
Slocjs Compiler version 0.8.7
web3js version 1.5.1
Note, you have to first define the struct type outside of any function inside the contract.
A supper intuitive example code is as follows:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract testContract {
struct funcResultType {
uint[] var1;
string[] var2;
string message;
}
funcResultType private funcResult;
function testSetFunc(string memory inputVar) public payable {
funcResult.var1.push(123);
funcResult.var2.push(inputVar);
funcResult.message = "Done!";
}
function testResetFunc() public payable {
delete funcResult; // reset variales
}
function testGetFunc() public view returns (funcResultType memory){
return funcResult;
}
}
The result of the test with Web3js in the console is as follow:
As you can see the whole variables are accessible. I upload its web3js code in Github in this link.

How to store 32 input paramaeters in a single solidity function with data types being string and bytes32?

When I try to exceute a solidity smart conract function to store 32 input parameters, I am getting a stack too deep error.
To resolve that, at the middleware we have consolidated and created an array of bytes32 for 24 input parameters and the rest of the 8 parameters are of string datatype. But again we get the same stack too deep error.
How can I possibly exceute a function to store the 32 input parameters?
We are making use of solidity compiler version 0.4.25.
This is the basic skeletal of code
struct Sample {
bytes32 key;
string str1;
.
.
.
.
.
string str8;
byts32[] someArray; //23 elements
}
mapping(bytes32 => Sample) sampleMap;
function set(bytes32 key,string str1,string str2,......,string
str8,bytes32[] array) public returns(bool) {
//set the values using sampleMap mapping;
return true;
}
function get(bytes32 key) returns(bytes32 key,string str1,string
str2,......,string str8,bytes32[] array) {
//retrieve values from struct using sampleMap mapping
}