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.
Related
I'm using web3j in Android studio to interact with smartcontracts.
In my SmartContract i've 2 functions getName() and getAge() and i'm setting age and name in constructor as below:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.9.0;
contract Identify {
string name;
uint age;
constructor() public {
name = "Shoaib Khalid";
age = 22;
}
function getName() view public returns(string memory){
return name;
}
function getAge() view public returns(uint){
return age;
}
}
But I'm not able to read the value returned by both functions. After deploying the smartcontract correctly, following is the method I'm trying to read the value returned by getName() function.
val identityContract = Identity_sol_Identify.load(
deployedContractAddress,
web3j,
getCredentialsFromPrivateKey(),
DefaultGasProvider.GAS_PRICE,
DefaultGasProvider.GAS_LIMIT
)
Log.d(TAG, "counter Result: ${identityContract.name.sendAsync().get()}")
Instead of getting the value Shoaib Khalid which I set in the constructor I'm getting a TranscriptReciept object the output screenshoot is attached below.
So I want to know can you read the exact value returned by the function getName() in smartcontract using web3j?
Please refer to the Web3j documentation:
Transactional calls do not return any values, regardless of the return type specified on the method. Hence, for all transactional
methods the Transaction Receipt associated with the transaction is
returned [1]
...
The transaction receipt is useful for two reasons:
It provides details of the mined block that the transaction resides in Solidity events that are called will be logged as part of
the transaction, which can then be extracted.
You are getting the transaction receipt. In order to query the values from your state variables you should refer to the section Querying the state of a smart contract and do something like this:
Function function = new Function<>(
"getName", // This is the name of the solidity function in your smart contract
Collections.emptyList(), // Solidity Types in smart contract functions, no input in the function so we set this to empty
Arrays.asList(new TypeReference<Utf8String>() {})); // result will be a string
String encodedFunction = FunctionEncoder.encode(function);
org.web3j.protocol.core.methods.response.EthCall response = web3j.ethCall(
Transaction.createEthCallTransaction(contractAddress, encodedFunction),
DefaultBlockParameterName.LATEST)
.sendAsync().get();
Iterator<Type> it = someType.iterator();
Type result = someType.get(0);
String a = result.toString();
Log.d("Name: ", a);
I've a smartcontract in which I've a function getLevels() that returns an array of struct type MLevel, defined at the beginning of smartcontract.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;
struct MLevel{
address id;
string levelPrize;
uint levelNo;
uint levelCriteria;
}
contract Test{
MLevel[] public mlevels;
function addLevel(uint _levelCriteria, string memory _levelPrize)public payable{
MLevel memory level;
level.id = msg.sender;
level.levelCriteria = _levelCriteria;
level.levelPrize = _levelPrize;
mlevels.push(level);
}
function getLevels() public view returns(MLevel [] memory){
return mlevels;
}
}
The above smartcontract works absolutely fine in Remix ide. But when I try to create its java wrapper class using web3j, wrapper class does not generate and web3j ends with a weird error part 'struct MLevel' is keyword as shown below in the image
I am creating an NFT collection, and am testing on ropsten. I have my NFT metadata on my website for example boredApes.com/tokens/3.json but the issue is the contract goes to boredApes.com/tokens/3 and not the .json part. How should I change to contract to load this metadata. The current solidity function is
function _baseURI() internal pure override returns (string memory) { return "https://boredApes.com/tokens/"; }
The _baseURI() function sets just the beginning of the URI. You need to override the tokenURI() function as well, appending the .json string at the end of the returned URI.
pragma solidity ^0.8;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol";
contract MyCollection is ERC721 {
constructor() ERC721("MyCollection", "MyC") {
_mint(msg.sender, 1);
}
function _baseURI() internal pure override returns (string memory) {
return "https://boredApes.com/tokens/";
}
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
// the `super` keyword references the parent function of the same name
string memory uri = super.tokenURI(tokenId);
return string(abi.encodePacked(uri, ".json"));
}
}
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
I've been doing some research around this issue, but I couldn't find a definite answer. I'm using solidity 0.4.24.
I have a contract like this:
contract {
struct FutureOperation is Ownable {
uint256 date;
uint256 price;
uint256 amount;
string name;
}
FutureOperation[] futureOperations;
// ...
function getAllFutureOperations() public onlyOwner returns (FutureOperation[]) {
return futureOperations;
}
}
When I compile this in Remix I get the following error:
browser/myfuturetoken.sol:53:64: TypeError: This type is only supported in the new experimental ABI encoder. Use "pragma experimental ABIEncoderV2;" to enable the feature.
I found some blog posts saying that I should de-structure the fields in the struct to return them as arrays of the primitive types. So, in this case, it would look something like this:
function getAllFutureOperations() public onlyOwner returns (uint256[] dates, uint256[] prices, uint256[] amounts, string[] names) {
return futureOperations;
}
Is there an alternative for that? Are the newer compilers capable of returning an array of structs?
Thanks.
As error stated, returning dynamic array is not supported by compiler yet. However, experiment feature supported it. To use experimental compiler, you need to make some changes as follows,
pragma experimental ABIEncoderV2;
contract myContract{
struct FutureOperation {
uint256 date;
uint256 price;
uint256 amount;
string name;
}
string[] futureOperations;
function getAllFutureOperations() public view returns (string[] memory) {
return futureOperations;
}
}
Note: make sure to not use experimental things in production version