Calling contract function with bytes4 argument gives error - ethereum

This seems like a very small problem and I am new to contract development.
I have a function in contract prototype as follows:
function exampleContractFunction(
address somecontractaddress,
bytes4 someCurrencySymbol,
uint[] memory someDetails,
uint16 someTime,
uint8 data1,
bytes32 data2,
bytes32 data3
);
I am using ethers library version ^4.0.33 and truffle
On contract compilation following interface is generated.
exampleContractFunction: TypedFunctionDescription<{
encode([
somecontractaddress,
someCurrencySymbol,
someDetails,
someTime,
data1,
data2,
data3
]: [
string,
Arrayish,
BigNumberish[],
BigNumberish,
BigNumberish,
Arrayish,
Arrayish
]): string;
}>;
I try to call this for getting encoded data for contract interaction transaction that needs signing.
When i call this and pass
const hexCurrency = (web3.utils.toHex('DAI'));
as someCurrencySymbol in the following call:
const data = this.contracts.somecontract.interface.functions.exampleContractFunction.encode([
somecontractaddress, // Can be compound or dydx at the moment
hexCurrency,
someDetails,
someTime,
data1,
data2,
data3,
]);
I get this error with variation of whatever currencySymbol i pass
Error: invalid input argument (arg="someCurrencySymbol", reason="invalid bytes4 value", value="0x444149", version=4.0.42)
at Object.throwError (/packages/types/node_modules/ethers/errors.js:76:17)
How can I send this bytes4 argument a currencySymbol?

Have you tried with just:
web3.utils.toHex('DA')
Seems to me you're not passing just 4 bytes, indeed the value you're getting of value="0x444149" it's actually made of 6.

Related

Returning nested structs in solidity reverts with error

My class structure looks similar to this:
struct A {
uint8 foo;
address bar;
}
struct B {
A foo;
int64 bar;
}
struct C {
uint8 foo;
...
B[] bar;
}
I have a hardhat test suite which have contract X and contract Y, contract X contains the structures above, and contract Y attempts to read struct C using X's public return function.
When i comment out C.bar, everything works fine. But when i try to return C.bar, i get the error:
Error: Transaction reverted: function returned an unexpected amount of data
I suspect it's throwing the ABI encoder out of whack, but does anyone know if Solidity officially supports the returning of these types of structures?
I think you are recieving this error because you need pass the key to access one of the C.bar entries.
For example, to access the first item in C.bar array, you can use:
C.bar[0]

factory contract reverts

I am trying to create a factory contract, which I call DAG:
pragma solidity >=0.7.0 <0.9.0;
import "./Agent.sol";
contract DAG {
address[] agents;
function createDAG() public returns (address) {
// create root agent
address[] memory parentsOfRoot;
address rootAddress = createAgent(parentsOfRoot);
// create child
address[] memory parentsOfChild;
parentsOfChild[0] = rootAddress;
createAgent(parentsOfChild);
return rootAddress;
}
function createAgent(address[] memory _parents) public returns(address) {
Agent agent = new Agent(_parents);
agents[agents.length - 1] = address(agent);
return address(agent);
}
}
It is meant to make something like a connected list of Agents.
pragma solidity >=0.7.0 <0.9.0;
contract Agent {
address[] parents;
constructor(
address[] memory _parents
){
parents = _parents;
}
function getParents() public view returns (address[] memory) {
return parents;
}
}
For some reason, when I call createDAG in the RemixIDE, I get the following error:
transact to DAG.createDAG pending ... transact to DAG.createDAG
errored: VM error: revert.
revert The transaction has been reverted to the initial state. Note:
The called function should be payable if you send value and the value
you send should be less than your current balance. Debug the
transaction to get more information.
Can anyone help me understand why I am unable to call createDAG?
Your snippet is trying to assign into 0th index of the array but this index doesn't exist at that moment. Which throws an "out of bounds" exception.
address[] memory parentsOfChild; // 0 items
parentsOfChild[0] = rootAddress; // trying to assign the first item (index 0)
It's currently (v0.8) not possible to resize in-memory arrays, so you'll need to initialize the array with an already predefined length.
address[] memory parentsOfChild = new address[](1); // 1 empty item
parentsOfChild[0] = rootAddress;
Then you're going to run into another logical error. Function createAgent() is also trying to assign a value to an "out of bounds" index.
When the agents array is empty, this snippet is trying to assign into index -1.
agents[agents.length - 1] = address(agent);
If you want to add a new item to the agents array, you can use the .push() member function of the array.
// instead of `agents[agents.length - 1] = address(agent);`
agents.push(address(agent));

TypeError: Member "length" is read-only and cannot be used to resize arrays

I am using this version of solidity, pragma solidity >=0.4.22 <0.9.0;
Whenever I compile truffle I get this TypeError: Member "length". When I change the version to 0.4.0 the error goes away, but I can't use this version. I need to use this pragma solidity >=0.4.22 <0.9.0;
Here is the error:
TypeError: Member "length" is read-only and cannot be used to resize arrays.
--> project:/contracts/Ballot1.sol:23:9:
|
23 | proposals.length = _numProposal;
| ^^^^^^^^^^^^^^^^
Compilation failed. See above.
From Solidity v0.6.0 and onwards, you can use an array slice such as proposals[start:end] (note: it only supports calldata array).
Otherwise, I think you might need to build a copy of the array like so:
function slice(
uint256 start,
uint256 end,
uint256[] memory proposals
) public pure returns (uint256[] memory) {
uint256[] memory result;
uint256 idx = 0;
for (uint256 i = start; i < end; i++) {
result[idx] = proposals[i];
idx++;
}
return result;
}
Note this operation can be quite costly if you have a large array.

Solidity storage struct not compiling

I'm new to solidity and I'm just experiencing with some simple code.
Basically I just want to create a struct that will store data permenantly inside a contract. According to the doc I need storage for that.
But the following code is not compiling except if I use memory instead. Why?
pragma solidity ^0.4.11;
contract test {
struct Selling {
address addr;
string name;
uint price;
}
mapping(string => Selling) selling;
function sellName() constant returns (bool ok)
{
address a = 0x4c3032756d5884D4cAeb2F1eba52cDb79235C2CA;
Selling storage myStruct = Selling(a,"hey",12);
}
}
The error I get is this:
ERROR:
browser/test.sol:16:9: TypeError: Type struct test.Selling memory is not implicitly convertible to expected type struct test.Selling storage pointer.
Selling storage myStruct = Selling(a,"hey",12);
^--------------------------------------------^
When you first create the myStruct instance, it will be created in memory and then written out to storage (assuming you put the object into your selling map and don't declare your method constant) when the function returns. If you were to retrieve the item from your map in another function, then you would declare it as a storage variable.
See this explanation on Ethereum StackExchange for more details. The Solidity documentation also has a very good explanation of how variables are stored and when to use memory vs storage.
I had similiar situation, the fix for this was:
function sellName() constant returns (bool ok)
{
address a = 0x4c3032756d5884D4cAeb2F1eba52cDb79235C2CA;
Selling memory myStruct = Selling(a,"hey",12);
// insert 'myStruct' to the mapping:
selling[a] = myStruct;
}

How to return array of struct?

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.