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

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.

Related

Solidity, swap on uniswapV3

I want to implement a swap on UniV3 but the follow error appears:
Gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending?
execution reverted
I have approved the Weth via etherscan goerli so I don't know why is sending me this error.Any advice??
Here is the code:
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
pragma abicoder v2;
import '#uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol';
import '#uniswap/v3-periphery/contracts/libraries/TransferHelper.sol';
contract SwapExamples {
// For the scope of these swap examples,
// we will detail the design considerations when using `exactInput`, `exactInputSingle`, `exactOutput`, and `exactOutputSingle`.
// It should be noted that for the sake of these examples we pass in the swap router as a constructor argument instead of inheriting it.
// More advanced example contracts will detail how to inherit the swap router safely.
// This example swaps DAI/WETH9 for single path swaps and DAI/USDC/WETH9 for multi path swaps.
ISwapRouter public immutable swapRouter;
// Router = 0xE592427A0AEce92De3Edee1F18E0157C05861564
address public constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
address public constant WETH = 0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6; //0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address public constant USDC = 0xEEa85fdf0b05D1E0107A61b4b4DB1f345854B952;//0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
// For this example, we will set the pool fee to 0.3%.
uint24 public constant poolFee = 3000;
uint256 public UsdOut;
constructor(ISwapRouter _swapRouter) {
swapRouter = _swapRouter;
}
/// #notice swapExactInputSingle swaps a fixed amount of DAI for a maximum possible amount of WETH9
/// using the DAI/WETH9 0.3% pool by calling `exactInputSingle` in the swap router.
/// #dev The calling address must approve this contract to spend at least `amountIn` worth of its DAI for this function to succeed.
/// #param amountIn The exact amount of DAI that will be swapped for WETH9.
/// #return amountOut The amount of WETH9 received.
function swapExactInputSingle(uint256 amountIn) external returns (uint256 amountOut) {
// msg.sender must approve this contract
// TransferHelper.safeApprove(WETH, address(this), amountIn);
// Transfer the specified amount of DAI to this contract.
TransferHelper.safeTransferFrom(WETH, msg.sender, address(this), amountIn); //Envia de la wallet a aquest contract, no necessito.
// Approve the router to spend DAI.
TransferHelper.safeApprove(WETH, address(swapRouter), amountIn);
// Naively set amountOutMinimum to 0. In production, use an oracle or other data source to choose a safer value for amountOutMinimum.
// We also set the sqrtPriceLimitx96 to be 0 to ensure we swap our exact input amount.
ISwapRouter.ExactInputSingleParams memory params =
ISwapRouter.ExactInputSingleParams({
tokenIn: WETH,
tokenOut: USDC,
fee: poolFee,
recipient: msg.sender,
deadline: block.timestamp,
amountIn: amountIn,
amountOutMinimum: 0,
sqrtPriceLimitX96: 0
});
// The call to `exactInputSingle` executes the swap.
amountOut = swapRouter.exactInputSingle(params);
UsdOut = amountOut;
}
}

Ethereum lottery smart contract reverts due to insufficient funds. Where does my code consumes that much gas?

I am experimenting with solidity and I faced an issue for what I could not find a solution.
The program should let addresses buy ticket at a preset price, and the owner can start the "roll the dice" function which randomly selects the winner and transfer the funds to that address.
I beleive that this program would be easier with mapping instead of array, but getting experience with array was the main purpose of this program.
The error happens when the user calls buyTicket function. Based on the response I beleive the contract comsumes too much gas. Can someone tell me why it doesnt work? I appreciate any other helping comment regarding the rest of the code:)
Thanks in advance!
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract Lottery {
constructor () {
owner = msg.sender;
}
address[] public Players;
address private owner;
uint public ticketPrice;
uint public price;
uint public nonce;
uint public variations;
address payable winner;
bool hasTicketAnswer;
event Winner(address _winner);
event PriceSet(uint _setPrice);
event TicketBought();
function setTicketPrice(uint _ticketPrice) public {
require(msg.sender == owner, "Only Owner...");
ticketPrice = _ticketPrice;
emit PriceSet(_ticketPrice);
}
function hasTicket(address _sender) private returns(bool) {
hasTicketAnswer = false;
for (uint i = 0; i < Players.length; i++) {
if (Players[i] == _sender) hasTicketAnswer = true;
}
return hasTicketAnswer;
}
function buyTicket() external payable {
require(ticketPrice > 0, "Price did not set, be patient...");
require(hasTicket(msg.sender) == false, "You cannot have two tickets...");
require(msg.sender.balance <= ticketPrice, "Insufficient funds...");
payable(address(this)).transfer(ticketPrice);
Players.push(address(msg.sender));
price += msg.value;
emit TicketBought();
}
function checkBalance() public view returns(uint) {
return address(this).balance;
}
function rollTheDice() public payable {
variations = Players.length;
winner = payable(Players[uint(keccak256(abi.encodePacked(msg.sender, nonce, block.timestamp))) % variations]);
winner.transfer(price);
emit Winner(winner);
}
receive () external payable {
}
}
Besides probably finding the problem, I've read some things that I'd like to comment on.
Your problem
The reason why you're getting the "Insufficient funds" error is because the condition is returning false. You're asking the msg.sender balance to be less than or equal (<=) to ticketPrice, when it should be more than or equal (>=).
Let's say Alice has a balance of 0.05 ETH and interacts with the contract whose ticket price is 0.001 ETH...
require(0.05 ETH <= 0.001 ETH) // Reverting...
Observations
I'm curious if you're intentionally coding the buyTicket function in that way. What it actually does is checking if the msg.sender has enough ETH to buy a ticket in its wallet, which doesn't mean that the user is effectively sending ETH in the transaction (the amount of wei sent in the transaction can be checked with msg.value, you can read more about it here).
My last observation is the payable(address(this)).transfer(ticketPrice) line of code, because it's not necessary to do so, once a payable function receives ETH, it is saved into the contract... In that line you're just making the Bob's contract to send ETH to the Bob's contract, which just wastes gas without reason
I hope I've helped with you and if I wasn't completely clear in any thing I've said don't doubt in asking me

When is function reverted?

so because of reentrancy attacks I'm structuring my function to not get hacked. So first updating mappings and so on and then sending the payment.
My question is what if the payment fails to go through. Will entire function be reverted of just the payment?
Because if only the payment than that would mean my mappings would be updated as if the payment went through.
Is this the case?
Thanks for answers!
function withdraw(uint256 _amount) external {
balances[msg.sender] -= _amount;
(bool success, ) = payable(msg.sender).call{value: _amount}("");
}
If the low-level .call() is unsuccessful, the value of success is set to false but that doesn't revert the whole transaction. So the balances value would be reflected even though the payment didn't go through.
You can add a require() condition to check the result of the payment. This will make the parent transaction fail if the internal transaction fails.
(bool success, ) = payable(msg.sender).call{value: _amount}("");
require(success);
Or you can use the .transfer() function (member of address payable, do not confuse with token transfers) that also fails the parent transaction if the internal transaction fals:
function withdraw(uint256 _amount) external {
balances[msg.sender] -= _amount;
payable(msg.sender).transfer(_amount);
}
If you're preventing the reentrancy attacks, you might probably use the modifiers. Therefore, when the Reentrancy is detected, the function would be reverted and even not allowed to enter the function. That is, there would be no other parameters updated.
Besides, I can show you some demo code to answer your question.
contract test {
uint public a = 0;
// a will still be a
function addRevert() public{
a += 1;
goRevert();
}
// a = a + 1
function addNoRevert() public{
a += 1;
}
function goRevert() pure public{
revert();
}
}

Burning Deployed ERC Tokens In an NFT Mint Function - Compiles, but Transaction Fails

I am very new to Solidity, and have recently been working on trying to learn the ropes. For reference, I have been using code from this video (https://www.youtube.com/watch?v=tBMk1iZa85Y) as a primer after having gone through the basic crypto zombies tutorial series.
I have been attempting to adapt the Solidity contract code presented in this video (which I had functioning just fine!) to require a Burn of a specified amount of an ERC-20 token before minting an NFT as an exercise for myself. I thought I had what should be a valid implementation which compiled in Remix, and then deployed to Rinkeby. I call the allowAccess function in Remix after deploying to Rinkeby, and that succeeds. But, when I call the mint function with the two parameters, I get: "gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending? execution reverted."
If I still send the transaction, metamask yields "Transaction xx failed! Transaction encountered an error.".
I'm positive it has to do with "require(paymentToken.transfer(burnwallet, amounttopay),"transfer Failed");", though I'm not sure what's wrong. Below is my entire contract code. I'm currently just interacting with the Chainlink contract on Rinkeby as my example, since they have a convenient token faucet.
pragma solidity ^0.8.0;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Counters.sol";
contract myNFTwithBurn is ERC721, Ownable {
address externalTokenAddress = 0x01BE23585060835E02B77ef475b0Cc51aA1e0709; //Token Type to burn on minting
uint256 amounttopay = 5; //number of these tokens to burn
IERC20 paymentToken = IERC20(externalTokenAddress); //my code: create an interface of the external token
address burnwallet = 0x000000000000000000000000000000000000dEaD; //burn wallet
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
using Strings for uint256;
// Optional mapping for token URIs
mapping (uint256 => string) private _tokenURIs;
// Base URI
string private _baseURIextended;
constructor() ERC721("NFTsWithBurn","NWB") {
}
function setBaseURI(string memory baseURI_) external onlyOwner() {
_baseURIextended = baseURI_;
}
function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
_tokenURIs[tokenId] = _tokenURI;
}
function _baseURI() internal view virtual override returns (string memory) {
return _baseURIextended;
}
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
string memory _tokenURI = _tokenURIs[tokenId];
string memory base = _baseURI();
// If there is no base URI, return the token URI.
if (bytes(base).length == 0) {
return _tokenURI;
}
// If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
if (bytes(_tokenURI).length > 0) {
return string(abi.encodePacked(base, _tokenURI));
}
// If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI.
return string(abi.encodePacked(base, tokenId.toString()));
}
function allowAccess() public
{
paymentToken.approve(address(this), 5000000); //This is my attempt to allow the contract access to the user's external tokens, in this case Chainlink (paymentToken)
}
function mintItem(address to, string memory tokenURI)
public
onlyOwner
returns (uint256)
{
require(paymentToken.transfer(burnwallet, amounttopay),"transfer Failed"); //Try to transfer 5 chainlink to the burn wallet
_tokenIds.increment();
uint256 id = _tokenIds.current();
_mint(to, id);
_setTokenURI(id, tokenURI);
return id;
}
}
If anybody can at least point me to what I'm doing completely wrong in the code that I've added, please do! TIA!
I'm not sure why are you trying to burn link in order to mint and nft but first check if the link code does not have a require that check if the destination address is the burn address if it has then burn the link is not possible and you should use any other erc20 maybe your own erc20, also your contract probably does not have any link and if you want to transfer the link from the user you should do this in the contract paymentToken.transferFrom(msg.sender,destinationAddress,amount) and if the user previously approve your contract you will able to send the tokens, and i suppose that the purpose of the allowAccess function is to make the user approve the contract to move the tokens that will never work, the approve function let's anyone that call it approve any address to move an amount of tokens, the thing is that to know who is approving to let other to move the tokens the function use msg.sender to explain how this work take a look at this example
let's say that your contract is the contract A and the link contract is the contract B
now a user call allowAccess in the contract A, so here the msg.sender is the user because they call the function
now internally this function call approve on contract B, here the contract A is the msg.sender, because the contract is who call the function
so what allowAccess is really doing is making the contract approving itself to move their own tokens that I assume it doesn't have

Requesting data with Chainlink, response not what expected

I've been trying to integrate Chainlink into my contract, managed to get the random number thingy working, but the API call doesn't work for me. Here's what I got:
contract ValorantCards is Ownable, ERC1155, VRFConsumerBase, ChainlinkClient {
using Chainlink for Chainlink.Request;
address private linkToken;
// Chainlink VRF
bytes32 private keyHash;
uint256 private vrfFee;
uint256 public randomResult;
// Chainlink API calls
address private oracle;
bytes32 private jobId;
uint256 private oracleFee;
uint256 public playerLevel;
constructor(
address _vrfCoordinator,
address _linkToken,
bytes32 _keyHash,
address _oracle,
bytes32 _jobId,
uint256 _oracleFee
) ERC1155("") VRFConsumerBase(_vrfCoordinator, _linkToken) {
setPublicChainlinkToken();
linkToken = _linkToken;
keyHash = _keyHash;
vrfFee = 0.1 * 10**18;
oracle = _oracle;
jobId = _jobId;
oracleFee = _oracleFee;
}
function requestUserLevel() public returns (bytes32 requestId) {
Chainlink.Request memory request = buildChainlinkRequest(
jobId,
address(this),
this.fulfill.selector
);
request.add(
"get",
"https://api.henrikdev.xyz/valorant/v1/account/draven/2023"
);
request.add("path", "data.account_level");
return sendChainlinkRequestTo(oracle, request, oracleFee);
}
function fulfill(bytes32 _requestId, uint256 _level)
public
recordChainlinkFulfillment(_requestId)
{
playerLevel = _level;
}
I'm deploying from hardhat, with the following parameters (ignoring the ones for VRF since that's working):
Oracle: 0x9C0383DE842A3A0f403b0021F6F85756524d5599
JobId: 0x3766623533366265383635623433333662323766633130313437633139336337
OracleFee: 0.1 * 10**18
The function runs fine, the transaction doesn't revert or anything, but when I check "playerLevel", it's always just 0
Looking at the Etherscan activity, it looks like the node you are using may be inactive. Try this node and jobId:
Oracle = 0xc57B33452b4F7BB189bB5AfaE9cc4aBa1f7a4FD8;
JobId = "d5270d1c311941d0b08bead21fea7747";
These were taken from the Chainlink Official Docs.
To check to see if a node may be inactive or not, check out the oracle address in a block explorer. You can see here that the original node you tried to use hasn't posted a transaction in awhile.
If a node is inactive you will need to find a new one or host one yourself. To find more nodes and jobs, you can check market.link or use the one found in the docs as mentioned earlier.