Is it possible somehow to get a uint256 hash from solidity array types such a strings or bytes32[]?
I want to create mapping (uint256 => address) where uint256 is a hash of some string what user entered earlier.
Thanx!
You can use keccak256() on any Solidity type (including strings and arrays) to generate a bytes32 hash. From there, you can either just use the bytes32 result as your key or cast it to a uint.
Solidity documentation on keccak
Related
In my smart contract, I have a function that does something (not relevant), but there are two ways I could potentially accept arguments to the function: using a struct, or by encoding the values and simply passing a bytes value like so:
// Only 3 fields for simplicity, but in theory, there could be any arbitrary num. of fields
struct Data {
address user;
uint256 amount;
address receiver;
}
function executeSomething(Data memory data) external returns(bool) {
address user = data.user;
_;
return true;
}
vs
function executeSomething(bytes memory data) external returns(bool) {
address user = (data >> (bytes.length - 160)); // since address is of 160 bytes
_;
return true;
}
In case 1, I'm simply passing a struct, whereas in the second case, I'm passing an argument encoded as bytes memory for which I can extract the argument values by using bitshift tricks.
My question is this:
What are the potential gas savings of these?
Is it more recommended to use a struct (ease of use + sanity purposes)?
Since bytes memory is not of fixed length like bytes32, what is the tipping point in which using a struct makes better sense (gas-savings-wise) than using bytes memory.
I have this code in my solidity contract:
uint256 constant maxNum = 10000;
function mintNewFull(uint16 tokenId) public {
require (0 <= tokenId && tokenId < maxNum;
// do other stuff
}
And I called it using this code in ethers which worked:
contractWithSigner.mintNewFull(3);
But then later when I changed it to trying to mint with tokenID 11:
contractWithSigner.mintNewFull(11);
It didn't work. And I tried and every number under 10 seems to work and numbers greater than that don't.
Is it some uint16 uint256 problem? Should my constant maxNum be changed to uint16, is it impossible to call using ethers a function with a uint16 parameter? I have no idea how to pass in a uimt16 instead of a uint256 because I couldn't find how to declare parameter types in the ethers docs. It seems like everyone just uses numbers or strings so that's confusing (especially when I will later have to pass in an array.)
I figured it out!!! Turns out it was something in the
// do other stuff
part where I was causing an integer overflow.
Yayyy I'm so happy :):)
After including a mapping inside a struct, when trying to pass the struct into a function, it has to be "storage" instead of "memory". (the "checkBalance" function is just an example here)
Is it a good practice to store mapping inside struct?
Any alternatives to the mapping type inside the struct? Thought of using array but it has to do the iteration to find the item.
library MappingLib {
struct Balance {
uint256 value;
mapping(string => uint256) positions;
}
function checkBalance(Balance storage balance, string memory key) public returns (uint256) {
return balance.positions[key];
}
}
First of all mappings and any other data structures that need to be persisted in-between contract calls are always stored in storage, whether they are part of any struct or not. Now answering your questions:
Yes, it's perfectly fine to have mappings inside structs
In your particular case mapping seems to be the most natural option as you want to access values by keys
You could put the mapping outside the struct
`mapping(string => uint256) positions;`
Pass in the string and get the value or you could just create the key in the mapping
`mapping(uint256 => Balance) positions;`
In order to change the state of the smart contract from front end inputs, wanted to submit string array to a smart contract , key value pair or objects.
Is it possible to use string array as parameter?
No solidity doesn't support arrays of strings as parameter. You would have to serialize and deserialize it in a string yourself to have the desired result but that would be expensive to do in solidity. You can test that on remix if you want. However, on remix the error message says that this function is supported in the experimental ABI encoder but I have never tested that, or how well it works with other libraries, and it is experimental after all.
As seen in below example from solidity document we can send bytes array to constructor
constructor(bytes32[] memory proposalNames) public {
chairperson = msg.sender;
voters[chairperson].weight = 1;
// For each of the provided proposal names,
// create a new proposal object and add it
// to the end of the array.
for (uint i = 0; i < proposalNames.length; i++) {
// `Proposal({...})` creates a temporary
// Proposal object and `proposals.push(...)`
// appends it to the end of `proposals`.
proposals.push(Proposal({
name: proposalNames[i],
voteCount: 0
}));
}
}
If you are trying to send string/Objects data specifically then it's better to separate out the methods and call each methods separately or within each other as currently solidity does not support that (using ABIencodere v2 is exceptional as it is only recommended for development purpose- as per on the date of this answer written)
struct A{
uint date,
B[] b
}
You can separate this out to
struct A{
uint date
}
struct B{
string goods,
uint quantity
}
so now for 1 A you can call N B from your service. Use mapping for binding both(if dependent).
In current situation it's better to design a contract which does not take bulk inputs or give out bulk outputs. However contracts are not for storage of huge data it's for storage of related data which fulfills agreement between parties
Function getBets() gives me the error: error: Failed to decode output: Error: Unsupported or invalid type: tuple. What am I missing?
pragma solidity ^0.4.11;
contract Casino {
struct Bet {
address by;
uint number;
}
address owner;
Bet[] bets;
event BetPlaced(Bet bet);
function Casino() {
owner = msg.sender;
}
function bet(uint number) {
Bet memory bet;
bet.by = msg.sender;
bet.number = number;
bets.push(bet);
BetPlaced(bet);
}
function getBets() constant returns (Bet[]) {
return bets;
}
function getCount() constant returns (uint length) {
return bets.length;
}
}
At the moment if I'm correct you can't return anything except an array of integers as there is no support for returning multi-dimensional data storages;
You can use an experimental library using:
pragma experimental ABIEncoderV2;
in the place of:
pragma solidity ^0.4.11;
This isn't available on Remix if you're using that at the moment and it's experimental so it may never be part of Solidity source: https://github.com/ethereum/solidity/issues/2948
If you did want to return an array of structs you could convert the whole array to bytes and return the bytes. This would be a bit of an extreme case and I wouldn't advise it.
If you only need to access the method internally and not externally you can pass by storage e.g.
function getBets() internal returns (Bet[] storage _r) {
_v = bets;
}
You may want to switch your struct to another contract. This way you can return an array of addresses. I have found using structs is only useful in storing and retrieving "that" data. Contracts are better to return sets of addresses. Also, I have not used the experimental encoder, so just going off of the current solidity version.