How can I delete an element from a nested mapping - solidity

I have a nested mapping that goes from address to planId to struct(subscription)
mapping(address => mapping(uint => subscription)) public subscriptions
I have a function to cancel a specific plan that has been created but when I trigger the function I got an error that says
The transaction ran out of gas. Please increase the Gas Limit.
When I debugged the error the debugger points the error inside the cancel function at the code line below
function cancel(uint planId) external {
Subscription storage subscription = subscriptions[msg.sender][planId];
require(
subscription.subscriber != address(0),
'this subscription does not exist'
);
delete subscriptions[msg.sender][planId]; // this one
emit SubscriptionCancelled(msg.sender, planId, block.timestamp);
}
How can I fix that error?
Thanks

I am facing a similar problem. Here's what I did, I realized that it's not possible to assign a default value to a nested mapping once you change it.
In your example, you can try assigning a struct with different values(which you can consider as a replacement for default) and then it wont throw error.
Following is my example:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract test{
mapping(address=> mapping(uint => uint)) public address_id_liked;
function register(uint id) external{
address_id_liked[msg.sender][id] = 1;
}
function test_(uint index) external view returns(uint) {
uint out = address_id_liked[msg.sender][index];
return(out);
}
function ops(uint id, uint num) external {
address_id_liked[msg.sender][id] = num;
}
}
In the ops function, I wanted my mapping to have a default value but it's not happening. So I tried inputting the value which I want to assign to nested mapping. And it's taking all uint values except 0(the default)
P.S- I was using mapping(address=> mapping(uint => bool)) public address_id_liked previously. But I am unable to delete/assign false, hence I tried with uint.
Hope this helps!

Related

How to create a time-based upkeep directly from my contract rather than use the GUI?

I want to create a time-based upkeep directly from my contract. I was able to register and fund the upkeep but for some reason the function is not getting executed automatically.
Here's the code
`
// Goerli network
address public cronFactoryAddress = 0x1af3cE8de065774B0EC08942FC5779930d1A9622;
address public keeperRegistrar = 0x57A4a13b35d25EE78e084168aBaC5ad360252467;
constructor(){
cronFactory = ICronFactory(cronFactoryAddress);
}
function createUpkeep(string memory _cronString) public{
address _target = address(this);
bytes memory functionToCall = bytes(abi.encodeWithSignature("sendSalary(string)", _cronString));
bytes memory job = cronFactory.encodeCronJob(_target, functionToCall, _cronString);
uint256 maxJobs = cronFactory.s_maxJobs();
address delegateAddress = cronFactory.cronDelegateAddress();
address newCronUpkeep = address(new CronUpkeep(msg.sender, delegateAddress, maxJobs, job));
allUpkeeps.push(newCronUpkeep);
}
function fundUpkeep(uint256 _linkAmount, address _upkeepAddress) public{
bytes4 reg = bytes4(keccak256("register(string,bytes,address,uint32,address,bytes,bytes,uint96,address)"));
bytes memory _data = abi.encode(
"TestV2",
"",
_upkeepAddress,
uint32(500000),
address(this),
"",
"",
_linkAmount,
address(this)
);
bytes memory combinedData = abi.encodePacked(reg, _data);
LinkContract.transferAndCall(keeperRegistrar, _linkAmount, combinedData);
}
sendSalary is the function in my contract that I want to be executed at regular intervals.
cronFactory is the cron factory contract.
cronUpkeep is the cronUpkeep.sol contract from the chainlink github repo.
To create these functions, I created a time-based upkeep manually and used the transaction logs to find what all function are being called and implemented the same here.
But, Once I execute both these functions nothing happens, however, I am able to find the upkeep registered on chainlink's website . And also it shows the trigger as custom trigger on upkeep page on chainlink:
chanlink upkeep
Please let me know how I can solve this? Any help would be appreciated. Thanks in advance
Contracts cannot execute themselves. Function needs to be called. While contract (function) is not called, contract is sleeping, because every time it makes operations, they should be payed (aka gas), so there is no way to throw an allways-active-timer inside of the contract (infinite gas). It means that you have to make calls manually or use automation services like ChainLink, Openzepplin Defender etc.
You can make a requirement by time-passed with
uint256 private lastTimeStamp;
uint256 private interval;
constructor() {
lastTimeStamp = block.timestamp;
interval = 7 days;
}
function isTimePassed() public view returns (bool timePassed) {
timePassed = ((block.timestamp - lastTimeStamp) > /*7 days */ interval);
return timePassed;
}
function smth() public {
(bool timePassed) = isTimePassed();
...
}
Something like this.

Solidity: Nested mapping access cannot be at return

i'm writing simple smart contract for learning.
I wrote a contract as
contract A {
mapping(address => mapping(uint32 => uint32)) data;
..
function getData(address addr, index id) public view {
return data[addr][id];
}
}
And it returns compile error
TypeError: Different number of arguments in return statement than in returns declaration
In right that line.
Maybe there is miswriting in my code data[addr][index] , which one should be fixed,
or just a bug of Solidity?
Weirdly, i found answer right after posting..
Simply added returns keywords like
function getData(address addr, index id) public view returns(uint32) {
...
then works fine.
For others, i'll leave this question as is.

Require() function is not working properly in below code

Deployed this contract on Remix IDE on InjectedWeb3 environment on Rinkeyby test network.
I tried removing the error msg statement in require, then it is not throwing error but still not working properly i.e the function is getting executed irrespective of any require condition.
pragma solidity >=0.4.22 <0.7.0;
contract RegisterLand{
struct land{
uint area;
string location;
uint floorsAllowed;
mapping(uint => address) owner;
uint count;
bool idExists;
}
mapping(uint => land) lands;
function Register(uint id,uint area, string memory location, uint
floorsAllowed) public
{
require(
!lands[id].idExists,
"ID already exists"
);
lands[id] = land(area, location, floorsAllowed,0,true);
lands[id].owner[lands[id].count] = msg.sender;
}
function ViewLand(uint id) public view returns(address currentOwner,
uint
landArea, string memory landLocation, uint landFloors )
{
require(lands[id].idExists,
"Id doesn't exist.");
currentOwner = lands[id].owner[lands[id].count];
landArea = lands[id].area;
landLocation = lands[id].location;
landFloors = lands[id].floorsAllowed;
}
}
error:
Failed to decode output: Error: overflow (operation="setValue",
fault="overflow", details="Number can only safely store up to 53
bits", version=4.0.32)
There is a known issue that require in view/pure functions don’t revert on public networks:
https://forum.openzeppelin.com/t/require-in-view-pure-functions-dont-revert-on-public-networks/1211
If you use Remix JavaScript VM then calling ViewLand with a non-existent id reverts as expected.
If you have questions on dapp development you can also ask in the OpenZeppelin Community Forum: https://forum.openzeppelin.com/
Disclosure: I am the Community Manager at OpenZeppelin

Example Oraclize files return 0: string: when called in Remix

I want to use Oraclize in Remix, to test it. I'm too stupid to use their examples.
How can I make this work?
From their Github I took the YouTube-Views code and copied it into Remix
pragma solidity >= 0.5.0 < 0.6.0;
import "github.com/oraclize/ethereum-api/oraclizeAPI.sol";
contract YoutubeViews is usingOraclize {
string public viewsCount;
event LogYoutubeViewCount(string views);
event LogNewOraclizeQuery(string description);
constructor()
public
{
update(); // Update views on contract creation...
}
function __callback(
bytes32 _myid,
string memory _result
)
public
{
require(msg.sender == oraclize_cbAddress());
viewsCount = _result;
emit LogYoutubeViewCount(viewsCount);
// Do something with viewsCount, like tipping the author if viewsCount > X?
}
function update()
public
payable
{
emit LogNewOraclizeQuery("Oraclize query was sent, standing by for the answer...");
oraclize_query("URL", 'html(https://www.youtube.com/watch?v=9bZkp7q19f0).xpath(//*[contains(#class, "watch-view-count")]/text())');
}
}
When I use the viewCount it returns:
0: string:
This happens with all the other examples aswell.
With WolframAlpha eg. I also get the following error:
transact to WolframAlpha.update errored: VM error: revert.
revert The transaction has been reverted to the initial state.
Note: The constructor should be payable if you send value. Debug the transaction to get more information.
Ok you don't see the answer like a normal result in Remix:
You have to go under settings and open the Oraclize plug in.
If you then deploy the contract and or click update, you get the result shown in the plug in.

solidity, set value to state Variables, the value not changed

I'm trying to change the state variable value of solidity, and test on the geth console, but the value of state variable is not changed.
The steps are as below:
1: Write a simple smart contract code by solidity as below
pragma solidity ^0.4.0;
contract SimpleStorage {
uint public storedData=99;
mapping(string => uint) balances;
function set(uint x) public returns (uint){
storedData = x;
return storedData;
}
function get() public constant returns (uint) {
return storedData;
}
function multiply(uint a) returns(uint d) {
return a * 7;
}
function setmapping(string key,uint value) returns(uint v)
{
balances[key] = value;
return balances[key];
}
function getmapping(string key) returns(uint v)
{
return balances[key];
}
function kill()
{
}
}
2: compile the code by the truffle, use command
truffle compile
3:start the geth, unlock account, and start the minner
4:deploy the smart contract
truffle migration --reset
and then I see the console output as below
Using network 'development'.
Running migration: 1_initial_migration.js
Replacing Migrations...
... 0x8ccf9e1599c2760ff3eed993be10929403e1faa05489a247a067f4f06536c74c
Migrations: 0xec08113a9e810e527d99a1aafa8376425c4a75ed
Saving successful migration to network...
... 0xedbf12715b736759e9d9297cbaaeb3151d95f478c2f1ee71bff4819d2dbb47e5
Saving artifacts...
Running migration: 2_deploy_contracts.js
Replacing SimpleStorage...
... 0xff5b00f9b14d8ecea4828c3ad8e9dbfa9d685bc0b81530fc346759d7998b060f
SimpleStorage: 0x96cf1e076f4d99a5d0823bd76c8de6a3a209d125
Saving successful migration to network...
... 0x3452a9e76b73e250de80874ebc3fd454724ebf6a15563bee0d5ba89b7b41909f
Saving artifacts...
which means the smart contract deployed to geth successfully
5: Then in the geth console, I set abi variable as below:
abi=[{"constant":true,"inputs":[],"name":"storedData","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"changeStorage","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"kill","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"getAll","outputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"key","type":"string"},{"name":"value","type":"uint256"}],"name":"setmapping","outputs":[{"name":"v","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"refrenceType","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"changeMemory","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"key","type":"string"}],"name":"getmapping","outputs":[{"name":"v","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"payable":false,"type":"function"}]
6: Get an contract instant as below:
test=eth.contract(abi).at("0x2f3970e8e4e2f5ed4ccb37b0f79fe5598700e2f0")
7: Run the set()
test.set.call(22);
The out put is 22, which I think the state variable storedData was successfully set to the new value 22, but when I run below code to read the storedData,
test4.get()
the return value is still 99, which mean the value of storedData was not changed, beside use the uint to do the testing, I also tried the mapping, but the answer is as same as uint, I don't know if I was wrong in somewhere or the state variable is not allowed to be modified, could anyone help me?
Thanks.
From the question, this is the call that is not changing state:
test.set.call(22)
In order to change state, you must issue a transaction. A call(...) only tells you what would happen if you were to send a transaction. For more background, see: What is the difference between a transaction and a call?
So you can replace the above line with:
test.set.sendTransaction(22)
Alternatively, web3.js will automatically attempt to decide whether to use a transaction or a call. So in this case you can use simply:
test.set(22)
For more background on how web3.js decides, see: How does web3.js decide to run a call() or sendTransaction() on a method call?