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've been trying to write a function that takes contractAddress and bytes functionSignature as input parameters and returns bytes data from the static call in inline assembly.
For some reason it keeps returning 0x0 instead of the data
im clearly doing something wrong
function getUintAnswer(address _contractAddress, bytes memory data) public view returns (bytes memory result) {
assembly {
// Call the target contract
let success := staticcall(64000, _contractAddress, data, 4/*input bytes*/, result, 160/*output bytes*/)
// failed
if iszero(success) {
revert(0, 0)
}
}
}
i expected the result to be:
0x00000000000000000000000000000000000000000000000200000000000027fa0000000000000000000000000000000000000000000000000000001bf2c11b5e00000000000000000000000000000000000000000000000000000000639ce2e300000000000000000000000000000000000000000000000000000000639ce2e300000000000000000000000000000000000000000000000200000000000027fa
and got a revert
im trying to extract the second int256 and cast it to a uint256 in the most gas efficient way
and i been stuck trying to figure out how to get the static call to successfully return a value
I want to declare a simple array (dynamic list), one set function to push a string in and one get function which returns all the strings saved in the dynamic array.
I search a lot but not able to find this simple stuff.
Here is my solution, you need experimental ABIEncoderV2 to return array of strings.
pragma solidity ^0.5.2;
pragma experimental ABIEncoderV2;
contract Test {
string[] array;
function push(string calldata _text) external {
array.push(_text);
}
function get() external view returns(string[] memory) {
return array;
}
}
If, finally, you want to interact with your smart contract with tools like web3j (for java) or web3js (javascript) in an application, working with dynamic arrays is not going to work because of some bugs in those libraries.
In this case you should serialize your output array. Same applies if you have an input array.
I'm learning Solidity Assembly and I'm confused about something. I'm looking at this library called Seriality. Specifically, this function: https://github.com/pouladzade/Seriality/blob/master/src/TypesToBytes.sol#L21
function bytes32ToBytes(uint _offst, bytes32 _input, bytes memory _output) internal pure {
assembly {
mstore(add(_output, _offst), _input)
mstore(add(add(_output, _offst),32), add(_input,32))
}
}
That function bytes32ToBytes takes a bytes32 variable and stores it in a dynamically sized bytes array, starting at the offset passed in.
The thing that confuses me is that it uses the mstore function twice. But the mstore function stores a word, which is 32 bytes, right? So why is it called twice, given that the input is 32 bytes? Wouldn't calling it twice store 2 words, which is 64 bytes?
Thanks!
Solidity arrays are stored by writing out the size of the array to the first storage slot then writing out the data to the subsequent slots.
Knowing that mstore has the following parameters: mstore(START_LOCATION, ITEM_TO_STORE), the first mstore statement is written as follows:
mstore(add(_output, _offst), _input)
Since the first slot of the array points to the size of the array, this statement is setting the size of _output. You should be able to get the same result by replacing it with mstore(add(_output, _offst), 32) (since the size is of _input is static).
The second statement (mstore(add(add(_output, _offst),32), add(_input,32))) is the one that writes the data itself. Here, we are shifting the position of both pointers by 32 bytes (as the first 32 bytes for both arrays are pointing to the size) and storing the value of _input to where the data is stored for _output.
Chances are, _output will already be initialized before calling this method (so the length will already be set), so it will usually be unnecessary. But, it doesn't hurt. Note that a similar implementation making this assumption would look like this:
function test() public pure returns (bytes) {
bytes32 i = "some message";
bytes memory o = new bytes(32); // Initializing this way sets the length to the location "o" points to. This replaces mstore(add(_output, _offst), _input).
bytes32ToBytes(0, i, o);
return o;
}
function bytes32ToBytes(uint _offst, bytes32 _input, bytes memory _output) internal pure {
assembly {
mstore(add(add(_output, _offst),32), add(_input,32))
}
}
Not sure about the intention of the function bytes32ToBytes
If it is turning a bytes32 into a bytes, I think the right implementation should be
pragma solidity ^0.7.0;
contract DecodeEncode {
function test() public pure returns (bytes memory) {
bytes32 i = "some message";
bytes memory o = new bytes(32); // Initializing this way sets the length to the location "o" points to. This replaces mstore(add(_output, _offst), _input).
bytes32ToBytes(0, i, o);
return o;
}
function bytes32ToBytes(uint _offst, bytes32 _input, bytes memory _output) internal pure {
assembly {
mstore(add(_output, _offst), 32) //lineA
mstore(add(add(_output, _offst), 32), _input) //lineB
}
}
}
lineA sets the length of the bytes as 32 bytes
lineB sets the content of the first slot of the bytes as _input
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.