There is Array.push function in solidity how it is return length of the array? - solidity

pragma solidity >=0.5.0 <0.6.0;
contract ZombieFactory {
event NewZombie(uint zombieId, string name, uint dna);
uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;
struct Zombie {
string name;
uint dna;
}
Zombie[] public zombies;
function _createZombie(string memory _name, uint _dna) private {
uint id = zombies.push(Zombie(_name, _dna)) - 1; //**here how it is return length of array**
emit NewZombie(id, _name, _dna);
}
function _generateRandomDna(string memory _str) private view returns (uint) {
uint rand = uint(keccak256(abi.encodePacked(_str)));
return rand % dnaModulus;
}
function createRandomZombie(string memory _name) public {
uint randDna = _generateRandomDna(_name);
_createZombie(_name, randDna);
}
}
I want to know how it is working i tired to searched in google but i cant get exact results try to explain how it returning length of array and basically it just add the element ...

before solidity v0.6.
Arrays have a member "push" define as :
Dynamic storage arrays and bytes (not string) have a member function called push that you can use to append an element at the end of the array. The element will be zero-initialised. The function returns the new length.
It's changed after v0.6.
Reference https://docs.soliditylang.org/en/v0.8.12/060-breaking-changes.html

Related

Failed to call solidity function with array argument

I'm trying to execute contract's function use abi encode and call function, but I'm getting an error. Don't know how to resolve this issue.
pragma solidity >=0.8.0;
contract AbiTest {
event A(uint);
event Out(bytes);
function test() public {
bytes4 func = bytes4(keccak256("callMe(uint[])"));
uint[] memory arr = new uint[](3);
arr[0] = 3;
arr[1] = 4;
(bool res, bytes memory data) = address(this).call(abi.encode(func, arr));
emit Out(data);
require(res);
}
function callMe(uint[] memory array) public {
emit A(array[0]);
emit A(array[1]);
}
}
This solution works well, if arguments sizes are known at compile time, but with dynamic sizes this doesn't work.
How can I resolve it?
I think this solution should work just fine. This is compatible with dynamic array lengths.
function test() public {
uint[] memory arr = new uint[](2);
arr[0] = 3;
arr[1] = 4;
(bool success, ) = address(this).call
(
abi.encodeWithSignature(
"callMe(uint[])",
arr
)
);
}
Need to use abi.encodePacked see Solidity doc instead of abi.encode because second one don't compress items and therefore uses 32 bytes for the function name.
pragma solidity >=0.8.0;
contract Abi2Test {
function getByte() public returns (bytes memory) {
bytes4 func = bytes4(keccak256("callMe(uint256[])"));
return abi.encode(func);
}
function getByte2() public returns (bytes memory) {
bytes4 func = bytes4(keccak256("callMe(uint256[])"));
return abi.encodePacked(func);
}
}
getByte output: 0x6600981c00000000000000000000000000000000000000000000000000000000
getByte2 output: 0x6600981c
Therefore, when you use encode with function signature additionally will be allocated 28 bytes and EVM can't correctly parse passed value.
So, to correctly call function with abi.encode and abi.encodePacked you need to pack arguments with encode at the first, and at the second group function's signature with encoded bytes.
pragma solidity >=0.8.0;
contract Abi2Test {
event A(uint256);
event Out(bytes);
event Out1(bytes);
function test() public {
bytes4 func = bytes4(keccak256("callMe(uint256[])"));
uint256[] memory arr = new uint256[](3);
arr[0] = 3;
arr[1] = 4;
(bool res, bytes memory data) = address(this).call(abi.encodePacked(func, abi.encode(arr)));
emit Out(data);
require(res);
}
function callMe(uint256[] memory array) public {
emit A(array.length);
emit Out1(msg.data);
}
}

Member "Push" not found... I tried to run my code the same way I did earlier with the Strings but this time with uints and it didn't work. New to Sol

I'm new to solidty so any new keyword is appreciated. I like the language so far, the syntax is close to Java and C#. I'm still a college student and the lack of info about solidity is a killer. I hope someone can help :/
pragma solidity ^0.8.7;
contract MyTestContarct{
Person[] public people;
uint256 public peopleCount;
struct Person {
string _firstName;
string _lastName;
}
function addPerson(string memory _firstName, string memory _lastName) public{
people.push(Person(_firstName, _lastName));
peopleCount +=1;
}
uint[] IntArrayTest;
uint256 public elementsCounter;
struct Numbers{
uint _number;
}
function addElements(uint _number) public{
IntArrayTest.push(Numbers(_number));
elementsCounter +=1;
} //It worked with string, why not with uint?
}
IntArrayTest is of type uint[] (array of unsigned integers), so you can only push uint items to this array.
However Numbers(_number) is not an uint - its type is Numbers.
You can create a Numbers variable in memory, and then pass its _number member (which has the uint type) to the IntArrayTest array.
uint[] IntArrayTest;
function addElements(uint _number) public{
Numbers memory numbers = Numbers(_number);
IntArrayTest.push(numbers._number);
elementsCounter +=1;
}
Or if you want to pass the Numbers type to an array, you'll need to delcare an array of this type.
struct Numbers{
uint _number;
}
Numbers[] NumbersArrayTest;
function addElements(uint _number) public{
NumbersArrayTest.push(Numbers(_number));
elementsCounter +=1;
}

Solidity variable definition: (bool sent, )

In Solidity, this sentence:
(bool sent, ) = msg.sender.call{value: _amount}("");
What is the sense of that "," after sent variable?
Any link to offcial documentation?
Many thanks.
It means you do not need to define both the variables to access the return values. You can access either of these variables as follows
(,uint256 alpha) = testFunction(); //access first returned variable
(uint256 beta,) = testFunction(); //access second returned variable
function testFunction() public pure returns(uint256,uint256){
return (1,2);
}
Solidity functions can return multiple variables of different types.
If you only want to keep one variable, then you can declare a variable and then use commas:
function multiValueFunction() public returns (bool, string memory, uint[] memory, uint {
//do something
return (true, "New String", [1,2], 21)
}
function differentFunction() public {
uint numberToKeep;
(,,,numberToKeep) = multiValueFunction();
}
Expected: numberToKeep = 21
Each comma represents the place of a returned variable (that is not kept)
It just means that the function will return two variables and you just want to store the first one.
This kind of syntax (variable type) is called Tuple In Python and other languages.
In Solidity,
they're not considered a proper type but can be used as a shorthand.
See the Official Docs: Destructuring Assignments and Returning Multiple Values
example:
uint256 amount = 10 ether;
require(
amount <= address(this).balance,
"Trying to withdraw more money than the contract has."
);
(bool success, ) = (msg.sender).call{value: amount}("");
require(success, "Failed to withdraw money from contract.");
above code, Roughly, Can be translated with basic if else statements like following:
uint256 amount = 10 ether;
bool success;
if(amount <= address(this).balance) {
success = true;
} else {
success = false;
return "Trying to withdraw more money than the contract has.";
}
if(success) {
(msg.sender).call{value: amount}("")
} else {
return "Failed to withdraw money from contract.";
}
here's another contract as example,
use this link https://remix.ethereum.org/#version=soljson-v0.6.9+commit.3e3065ac.js&optimize=false&gist=83bfbd85ef79387f760154999eb4f192&runs=200&evmVersion=null to play around in remix online
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.8.0;
contract myContract {
uint index;
function myFunction() public {
( , , uint256 standardDepositAmount, , ) = returnTuple();
require(standardDepositAmount == 3);
// do something...
}
function returnTuple() public pure returns (uint, uint, uint, uint, uint) {
return (1, 2, 3, 4, 5);
}
}

If statement in for loop not filtering out items in solidity

// #param physicalAddress - the actual address of the home a host wants to list (not the ethereum address)
// #return _id - list of ids for homes
function listHomesByAddress(string _physicalAddress) public returns(uint [] _id ) {
uint [] results;
for(uint i = 0 ; i<homes.length; i++) {
if(keccak256(homes[i].physicalAddress) == keccak256(_physicalAddress) && homes[i].available == true) {
results.push(homes[i].id);
}
}
return results;
}
The result is supposed to be a list of ids which match the physical address entered however it does not filter through but returns all the available homes.
When I change to using String utils nothing changes.
Here is the whole code:
pragma solidity ^0.4.0;
import "browser/StringUtils.sol";
// #title HomeListing
contract HomeListing {
struct Home {
uint id;
string physicalAddress;
bool available;
}
Home[] public homes;
mapping (address => Home) hostToHome;
event HomeEvent(uint _id);
event Test(uint length);
constructor() {
}
// #param physicalAddress - the actual address of the home a host wants to list (not the ethereum address)
function addHome(string _physicalAddress) public {
uint _id = uint(keccak256(_physicalAddress, msg.sender));
homes.push(Home(_id, _physicalAddress, true));
}
// #param physicalAddress - the actual address of the home a host wants to list (not the ethereum address)
// #return _id - list of ids for homes
function listHomesByAddress(string _physicalAddress) public returns(uint [] _id ) {
uint [] results;
for(uint i = 0 ; i<homes.length; i++) {
string location = homes[i].physicalAddress;
if(StringUtils.equal(location,_physicalAddress )) {
results.push(homes[i].id);
}
}
return results;
}
}
The part giving you trouble is the line uint[] results;. Arrays declared as local variables reference storage memory by default. From the "What is the memory keyword" section of the Solidity docs:
There are defaults for the storage location depending on which type of variable it concerns:
state variables are always in storage
function arguments are in memory by default
local variables of struct, array or mapping type reference storage by default
local variables of value type (i.e. neither array, nor struct nor mapping) are stored in the stack
The result is you're referencing the first storage slot of your contract, which happens to be Home[] public homes. That's why you're getting the entire array back.
To fix the problem, you need to use a memory array. However, you have an additional problem in that you can't use dynamic memory arrays in Solidity. A workaround is to decide on a result size limit and declare your array statically.
Example (limited to 10 results):
function listHomesByAddress(string _physicalAddress) public view returns(uint[10]) {
uint [10] memory results;
uint j = 0;
for(uint i = 0 ; i<homes.length && j < 10; i++) {
if(keccak256(homes[i].physicalAddress) == keccak256(_physicalAddress) && homes[i].available == true) {
results[j++] = homes[i].id;
}
}
return results;
}

return multiple array in solidity

I am trying to return multiple array from a function.
My code is looks like this:
struct Document{
bytes32 _documentNumber;
bytes32 _documentStatus;
uint _documentScore;
}
mapping(bytes32=>Document) public mapDocuments; // Holds Docno as key
mapping(address=>bytes32[]) public mapUserDocNos; // Holds User address as key with valuehaving array of all document nos
mapping(bytes32=>DocumentDetails) public mapDocumentDetails; // Holds Docno as key
//And i am storing data in map like this:
function addDocument(address _user, bytes32 _docNo,
bytes32 _documentStatus,uint _documentScore,
uint _createdDateStr) returns (bool status){
DocumentDebuggingLog(block.timestamp, "Step 1",_user);
Document memory document;
DocumentDebuggingLog(block.timestamp, "Step 2",_user);
document._customerAccountAddress= _user;
document._documentNumber= _docNo;
document._documentScore=_documentScore;
document._documentStatus=_documentStatus;
mapDocuments[_docNo]=document;
mapUserDocNos[_user].push(_docNo);
return true;
}
//trying to retrieve :
function getDocumentListByUser(address _user) returns (bytes32[] _docNumber,
bytes32[] _docStatus,uint[] _docScore){
bytes32[] _documentNumber;
bytes32[] _documentStatus;
uint[] _documentScore;
DocumentDebuggingLog(block.timestamp, "step 1 in getDocumentListByUser",_user);
for(uint i=0;i<mapUserDocNos[_user].length;i++){
//bytes32 dockey= mapUserDocNos[_user][i];
//DocumentDebuggingLog(block.timestamp, dockey,_user);
_documentNumber.push( mapDocuments[mapUserDocNos[_user][i]]._documentNumber);
_documentStatus.push( mapDocuments[mapUserDocNos[_user][i]]._documentStatus);
_documentScore.push( mapDocuments[mapUserDocNos[_user][i]]._documentScore);
}
return (_documentNumber,_documentStatus,_documentScore);
}
But i am not able to get any data from above function.Where i am doing wrong? is there any other way to return multiple array from a function in solidity?
Try to change below in function getDocumentListByUser
FROM:
bytes32[] _documentNumber;
bytes32[] _documentStatus;
uint[] _documentScore;
TO:
uint256 arrLength = mapUserDocNos[_user].length
bytes32[] memory _documentNumber = new bytes32[](arrLength);
bytes32[] memory _documentStatus = new bytes32[](arrLength);
uint[] memory _documentScore = new uint[](arrLength);
Try below:
function getDocumentListByUser(address _user) returns (bytes32[] _docNumber, bytes32[] _docStatus,uint[] _docScore) {
uint256 arrLength = mapUserDocNos[_user].length
bytes32[] memory _documentNumber = new bytes32[](arrLength);
bytes32[] memory _documentStatus = new bytes32[](arrLength);
uint[] memory _documentScore = new uint[](arrLength);
DocumentDebuggingLog(block.timestamp, "step 1 in getDocumentListByUser",_user);
for(uint i=0;i<mapUserDocNos[_user].length;i++){
//bytes32 dockey= mapUserDocNos[_user][i];
//DocumentDebuggingLog(block.timestamp, dockey,_user);
_documentNumber.push( mapDocuments[mapUserDocNos[_user][i]]._documentNumber);
_documentStatus.push( mapDocuments[mapUserDocNos[_user][i]]._documentStatus);
_documentScore.push( mapDocuments[mapUserDocNos[_user][i]]._documentScore);
}
return (_documentNumber,_documentStatus,_documentScore);
}
Due to the limitations of Ethereum Virtual Machine, you can currently return only a fixed sized array. Please look at the answer with code examples from the official solidity faq.