how to test a function call with dynamic calldata array? - unit-testing

I have a contract function like the following
function updateDeps(uint16 parent, uint16[] calldata deps) public
And I am trying to create unit tests in remix with the following
contract TestContract is MyContract {
uint16[] deps;
function testUpdateDeps() {
deps.push(uint16(2));
deps.push(uint16(3));
deps.push(uint16(4));
(bool success, bytes memory result) = address(this).delegatecall(abi.encodeWithSignature("updateDeps(uint16, uint16[])", 1, deps));
Assert.ok(success, "should succeed");
}
}
I wrote the test like this because of the difference between fixed size array and dynamic array and that calldata cannot be a contract variable and can only be created in function call, thus the delegatecall. However, the transaction failed.
How should I test function that takes a calldata dynamic array as param?

It is with the abi encoding string, there cannot be space between the params in the string, i.e. updateDeps(uint16,uint16[]) works but not updateDeps(uint16, uint16[])
P.S. an additional fyi, if you are using uint in your code, which is alias for uint256, in the abi signature string you have to specify uint256 but not uint.

Related

what type of variable should i use to store contracts in soldity

so, First, I imported the simpleStorage contract and then wrote this function createNeewContracts() to push the contract address into an array
everything was going smooth and I got an idea
"why not just use a struct of two variables 1. string (name of the contract owner) and 2. address (of the contract the owner owns) and i can then have name of owner and his contract as a struct stored in an array named contractAndThereArray"
I could have used mapping but I used struct and array to do the work
I ran into a problem it say
"TypeError: Invalid type for argument in function call. Invalid implicit conversion from contract SimpleStorage1 to address requested."
here is the code:-
contract StorageFactory{
// SimpleStorage public simpleStorage;
struct ownerAndThereContracts {
string name;
address nf;
}
ownerAndThereContracts[] public saveYourContracts;
SimpleStorage1[] public x;
// mapping(string => address) y;
uint256 counter;
function createNewContracts() public {
SimpleStorage1 simpleStorage = new SimpleStorage1();
x.push(simpleStorage);
// address j = simpleStorage;
}
function refering(string memory _name) public {
counter++;
SimpleStorage1 simpleStorage2 = SimpleStorage1(x[counter]);
saveYourContracts.push(ownerAndThereContracts(_name,simpleStorage2)); // the error occurs here it says simpleStorage2 is not address but contract
}
}
The problem is with the function referring where i tried to refer the address of a contract with the owners name but seems there is a problem
Please,if someone can explain it. Please explain I need your help guys
i've been tried to solve it for hours now but nothing seems to fit
I tried to get into your logic and fixed your contract and solve your mistake. Specifically, your error states that you cannot insert an instance of a contract into a field with datatype address. In order to solve this problem you need to cast the contract instance in address datatype. You can see the contract in the following lines, I have also inserted some notes to let you understand what I have changed:
// SDPX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "./SimpleStorage1.sol";
contract StorageFactory{
uint256 counter;
struct ownerAndThereContracts{
string name;
address nf; // NOTE: I assume that it's the SimpleStorage address
}
ownerAndThereContracts[] public saveYourContracts;
SimpleStorage1[] public x;
function createNewContracts() public {
SimpleStorage1 simpleStorage = new SimpleStorage1();
x.push(simpleStorage);
}
function refering(string memory _name) public {
// NOTE: I assume that when creating new contract, then you must refering it directly to an owner.
// In this case, I retrieve the length of array in details the last element index.
counter = x.length-1;
SimpleStorage1 simpleStorage2 = SimpleStorage1(x[counter]);
// NOTE: You must cast your contract instance to an address for put it into your struct (because the datatype is 'address')
saveYourContracts.push(ownerAndThereContracts(_name,address(simpleStorage2)));
}
}
Ok first i fixed your code, you are missing 2 casting, first to address and then to a paybale address.
Here:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.15;
contract SimpleStorage1{
}
contract StorageFactory{
// SimpleStorage public simpleStorage;
struct ownerAndThereContracts {
string name;
address nf;
}
ownerAndThereContracts[] public saveYourContracts;
SimpleStorage1[] public x;
// mapping(string => address) y;
uint256 counter;
function createNewContracts() public {
SimpleStorage1 simpleStorage = new SimpleStorage1();
x.push(simpleStorage);
// address j = simpleStorage;
}
function refering(string memory _name) public {
counter++;
SimpleStorage1 simpleStorage2 = SimpleStorage1(x[counter]);
address simpleStorage2Address = address(simpleStorage2); //first cast to address
address payable _finalCast = payable(simpleStorage2Address); // then cast to payable address
saveYourContracts.push(ownerAndThereContracts(_name, _finalCast));
}
But you should know that this is a very costy contract, you should take out all the process of saving the contract to a environment like c or java and only create the contract through solidity so the objects on memory wont cost you.
Anyway good luck!
From docs: contract-types
Explicit conversion to and from the address payable type is only
possible if the contract type has a receive or payable fallback
function. The conversion is still performed using address(x). If the
contract type does not have a receive or payable fallback function,
the conversion to address payable can be done using
payable(address(x))
I explained here contract-type vs address type
In Solidity a contract variable is really just an address under the
hood.

Can I write it in remix ide?

I made a dynamic array variable of address type, i.e.,
address payable[] public participant;
which one is the correct way to write in the following and why,
uint payable[] public participant;
or
uint[] payable public participant;
enter code here
There's address and its extension address payable, which allows you to use the native transfer() method to send ETH to this address.
Since the type is called address payable, you can make an array of this type by appending the [] expression after the type name.
There's no payable extension to uint. If your aim is to define an amount to be sent, that can be stored in a regular uint.
pragma solidity ^0.8;
contract MyContract {
address payable[] public participants;
function foo() public {
uint amount = 1; // 1 wei
for (uint i = 0; i < participants.length; i++) {
participants[i].transfer(amount);
}
}
}

Call function of smart contract after deploying smart contract in Solidity

I have simple smart contract in Solidity. I want to call the function getHello() after I deployed the smart contract (to see variable in deployed contract without calling it by myself). Can I do this?
pragma solidity 0.5.12;
contract Hello {
string public helloStr = "Hello, world 2!";
uint[] public nums = [10, 20, 30];
function getHello() public view returns (string memory) {
return helloStr;
}
function pushNewElement(uint newElement) public returns (uint) {
nums.push(newElement);
}
function popLastElement () public returns (uint) {
nums.pop();
}
function setHello(string memory newHello) public {
helloStr = newHello;
}
}
For getting public variable the compiler automatically formed function.
In you case you can get hello string with function with hash c660ab0e
Or use your function getHello().
For calling function(example, helloStr()) you should use:
{"jsonrpc":"2.0","method":"eth_call", "params":[{"to":"address your smart contract", "data":"0xc660ab0e"}, "latest"],"id":1}
Or use web3 with call:
https://web3js.readthedocs.io/en/v1.2.0/web3-eth.html#call
Yes, you will see "hello world 2" after deployed the contract but you will never be able to see "newHello" output. because it sets the empty string whenever you setHello() function is using.

What is the use of callback addresses in a Chainlink Consumer contract?

I have a question about the RSK integration (specifically about how callback addresses are used)
https://github.com/smartcontractkit/chainlink-RSK/blob/master/test-runner/src/contracts/Consumer.sol
function requestRIFPriceByCallback(uint256 _payment, address _callback) public {
Chainlink.Request memory req = buildChainlinkRequest(specId, _callback, this.fulfill.selector);
req.add("get", "https://api.liquid.com/products/580");
req.add("path", "last_traded_price");
req.addInt("times", 100000000);
sendChainlinkRequest(req, _payment);
}
https://github.com/smartcontractkit/chainlink/blob/develop/evm-contracts/src/v0.4/tests/Consumer.sol
function requestEthereumPrice(string _currency) public {
Chainlink.Request memory req = buildChainlinkRequest(specId, this, this.fulfill.selector);
req.add("get", "https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD,EUR,JPY");
string[] memory path = new string[](1);
path[0] = _currency;
req.addStringArray("path", path);
sendChainlinkRequest(req, ORACLE_PAYMENT);
}
These are from Consumer.sol (above from RSK and below from original)
Why does the RSK consumer require a callback address and what does that do/how does it work?
Both of these functions have callback addresses. You’ll see in the second method they just use ‘this’. The callback address is a parameter in the ‘buildChainlinkRequest’ function. It specifies which contract to return the data to. That combined with the function selector lets you pick which contract and function you want to return your data to.
The 2nd method doesn’t have a callback address because it’s set to ‘this’. The first function lets you pick.

How to improve smart contact design in order to distinguish data and their manipulation functions for the same domain object?

We have a problem with contract redeploying. Each time when some logic is changed during new contract version deployment we are loosing all contract related data (which are stored in arrays, mappings). Then we need to execute data load procedures in order to restore environment to desired state which is time consuming action. I tried to split contract to tow ones (AbcDataContract, AbcActionsContract) but faced with problem of accessing to the mappings : Error: Indexed expression has to be a type, mapping or array (is function (bytes32) view external returns (uint256))
Initial contract :
contract AbcContract {
EntityA[] public entities;
mapping (bytes32 => uint) public mapping1;
mapping (bytes32 => uint[]) public mapping2;
mapping (bytes32 => uint[]) public mapping3;
/* Events */
event Event1(uint id);
event Event2(uint id);
/* Structures */
struct EntityA {
string field1;
string field2;
bool field3;
uint field4;
Status field5;
}
enum Status {PROPOSED, VOTED, CONFIRMED}
function function1(...) returns (...)
function function2(...) returns (...)
function function3(...) returns (...)
function function4(...) returns (...)
function function5(...) returns (...)
}
Refactored contracts :
contract AbcDataContract {
EntityA[] public items;
mapping (bytes32 => uint) public mapping1;
mapping (bytes32 => uint[]) public mapping2;
mapping (bytes32 => uint[]) public mapping3;
/* Events */
event Event1(uint id);
event Event2(uint id);
/* Structures */
struct EntityA {
string field1;
string field2;
bool field3;
uint field4;
Status proposalStatus;
}
enum Status {PROPOSED, VOTED, CONFIRMED}
}
contract AbcActionsContract {
AbcDataContract abcDataContract;
/* constructor */
function AbcActionsContract(address _AbcDataContract) {
abcDataContract = AbcDataContract(_AbcDataContract);
}
/* accessing to the mapping like abcDataContract.mapping1[someId] will raise Solidity compile error */
function function1(...) returns (...)
/* accessing to the mapping like abcDataContract.mapping2[someId] will raise Solidity compile error */
function function2(...) returns (...)
/* accessing to the mapping like abcDataContract.mapping3[someId] will raise Solidity compile error */
function function3(...) returns (...)
function function4(...) returns (...)
function function5(...) returns (...)
}
We would like to implement approach like we have in DB development when logic changes in stored procedures/views/other not data objects usually does not affect data itself. What is the best design solution for this problem ?
The first part of your question is fairly easy. To access a public mapping in another contract, simply use ():
abcDataContract.mapping1(someId)
Of course, you can also provide your own access methods to AbcDataContract instead of using the public mapping as well. If you go down this path, I'd recommend going through an interface to access your data contract
As for the design part of your question, it looks like you're on the right track. Separating your data store into its own contract has huge benefits. Not only is it much easier to deploy since you don't have to worry about migrating your data, but it's also much cheaper to deploy the new contract.
That being said, there's a couple things I want to point out with the refactored version you posted.
It's hard to tell what you're planning on doing with Struct1. There's no reference to it in your pseudocode. You can't return structs from functions in Solidity unless they are internal calls (or you explicitly decompose the struct).
Similarly, you can't return strings between contracts. If you plan on using Struct1.field1/2 in AbcActionsContract, you'll need to convert them to bytes32.
You'll probably want to move your event definitions into your business logic contract.
Separating your data store from your business logic is a key component to upgrading contracts. Using interfaces and libraries help with this. There are several blog posts out there addressing this issue. I personally recommend starting with this one along with a follow up here.
Here is approximate design of contracts which should resolve issue with losing data due to deployment some changes in contract's business logic :
contract DomainObjectDataContract {
struct DomainObject {
string field1;
string field2;
bool field3;
uint field4;
Status field5;
}
enum Status {PROPOSED, VOTED, CONFIRMED}
//primitives
//getters/setters for primitives
/arrays
DomainObject[] public entities;
//getters(element by id)/setters(via push function)/counting functions
mapping (bytes32 => uint) public mapping1;
mapping (bytes32 => uint[]) public mapping2;
mapping (bytes32 => uint[]) public mapping3;
//getters(element by id/ids)/setters(depends from the mapping structure)/counting functions
}
contract DomainObjectActionsContract {
DomainObjectDataContract domainObjectDataContract;
/*constructor*/
function DomainObjectActionsContract(address _DomainObjectDataContract) {
domainObjectDataContract = DomainObjectDataContract(_DomainObjectDataContract);
}
/* functions which contain business logic and access/change data via domainObjectDataContract.* Redeploying of this contract will not affect data*/
function function1(...) returns (...)
function function2(...) returns (...)
function function3(...) returns (...)
function function4(...) returns (...)
function function5(...) returns (...)
}
One of the pending design issues is application pagination capabilities. Let's suppose we have following structure :
struct EntityA {
string lessThen32ByteString1;
string moreThen32ByteString1;
string lessThen32ByteString2;
string moreThen32ByteString3;
bool flag;
uint var1;
uint var2;
uint var3;
uint var4;
ProposalStatus proposalStatus;
}
// 100K entities
EntityA[] public items;
And we need to return subset of data based on offset and limit to our UI per one contract's function invocation. Due to different limits/errors of Solidity our current functions (with helper functions for string to byte32 conversion, splitting string to a few byte32 parts and so on) looks like this :
function getChunkOfPart1EntityADetails(uint filterAsUint, uint offset, uint limit) public constant
returns (bytes32[100] lessThen32ByteString1Arr, bytes32[100] moreThen32ByteString1PrefixArr, bytes32[100] moreThen32ByteString1SuffixArr) {
}
function getChunkOfPart2EntityADetails(uint filterAsUint, uint offset, uint limit) public constant
returns (bytes32[100] lessThen32ByteString2Arr, bytes32[100] moreThen32ByteString2PrefixArr, bytes32[100] moreThen32ByteString2SuffixArr) {
}
function getChunkOfPart3EntityADetails(uint filterAsUint, uint offset, uint limit) public constant
returns (bool[100] flagArr, uint[100] var1Arr, uint[100] var2Arr, uint[100] var3Arr, uint[100] var4Arr, ProposalStatus[100] proposalStatusArr,) {
}
Definitely they looks like awful from design perspective but we still have no better solution for pagination. I am not even saying that there is no any query language support, even basic filtering by some field require manual implementation.