'Timed Advertisements Solidity Contract
Basically, I'm trying to make the smart contract be able to take exactly 0.05 eth ONLY when the cooldown timer is set to 0. The Dapp I'm creating is a first come first serve eth advertising service, where the user can upload an image or gif within the dapp, then pay 0.05 eth to trigger the advertisement to run for x amount of time. When the time runs out, the next user can purchase their ad slot.
The timer seems to work, but I cant get the timer to start upon payment.
I would really appreciate the help, here is what I have so far:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract AdEth {
//This sets up the name of the ad and if it is running, set to false by default
//Variables for start, end, cooldown (cool down can be changed here)
address payable public owner;
uint _start;
uint _end;
uint cooldownTime = 1 minutes;
constructor() {
owner = payable(msg.sender);
}
modifier timerOver {
require(block.timestamp <= _end, "The Cooldown is over");
_;
}
modifier onlyWhileOpen {
require(block.timestamp >= _start && block.timestamp <= _end);
_;
}
function start() public {
_start = block.timestamp;
end(cooldownTime);
}
function end(uint totalTime) public {
_end = totalTime + _start;
}
function getTimeLeft() public view returns(uint) {
return _end - block.timestamp;
}
receive() external payable {
}
function receiveAdPayment() payable public returns (bool) {
require(msg.value == 0.05 ether, "Not enough ether. 0.05 Needed.");
require(cooldownTime == 0, "There is currently an add running. Please wait until the cooldown is finished.");
msg.sender.transfer(0.05 ether);
start();
return true;
}
function withdrawAll(uint _amount) external {
require(msg.sender == owner, "Caller is not the owner.");
payable(msg.sender).transfer(_amount);
}
function getBalance() external view returns (uint) {
return address(this).balance;
}
}
------------EDITS 5.11.21--------------
I have swapped out all of the timestamps for block.number and everything is working as intended. However, I want the smart contract to automatically call the setRunning() function when the cooldownTime expires. Is this possible? or is this the best its going to get?
Appreciate any and all help!
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract AdEth {
//This sets up the name of the ad and if it is running, set to false by default
//Variables for start, end, cooldown (cool down can be changed here)
address payable public owner;
uint _start;
uint _end;
//The number is in BLOCKS (~15 sec each, rinkeby approx ~1 min)
uint cooldownTime = 4;
bool running;
constructor() {
owner = payable(msg.sender);
}
function start() internal {
_start = block.number;
_end = block.number + cooldownTime;
running = true;
}
function getTimeLeft() public view returns(uint) {
return _end - block.number;
}
//This allows the owner to set "running" to false only if the required amount of cooldown blocks is reached.
function setRunning() public {
require(msg.sender == owner, "You are not the owner.");
require(block.number > _end, "Wait for the cooldown to expire before you can reset running to false.");
running = false;
}
function isRunning() public view returns (bool) {
return running;
}
function receiveAdPayment() payable public {
require(msg.value >= 0.05 ether, "At Least 0.05 ETH Needed.");
require(block.number > _end, "There is currently an ad running. Please wait until the cooldown is finished.");
require(running != true, "The ad time may have run out, but has not been reset by Admin.");
start();
}
function withdraw(uint _amount) external {
require(msg.sender == owner, "Caller is not the owner.");
payable(msg.sender).transfer(_amount);
}
function getBalance() external view returns (uint) {
return address(this).balance;
}
}
----EDITS 5.18.22------
Ok here is the final product. Got pushed in a bunch of directions and chose the below code. It is working well in remix.
Thanks everyone!
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import '@openzeppelin/contracts/access/Ownable.sol';
contract AdEth is Ownable {
//This sets up the name of the ad and if it is running, set to false by default
//Variables for start, end, cooldown (cool down can be changed here)
uint _start;
uint _end;
uint cooldownTime = 3 minutes;
uint runNumber = 0;
function start() internal {
_start = block.timestamp;
_end = block.timestamp + cooldownTime;
runNumber++;
}
function getTimeLeft() public view returns(uint) {
return _end - block.timestamp;
}
function adRunNumber() public view returns (uint) {
return runNumber;
}
function receiveAdPayment() payable public {
require(msg.value >= 0.05 ether, "At Least 0.05 ETH Needed.");
require(block.timestamp > _end, "There is currently an ad running. Please wait until the cooldown is finished.");
start();
}
function withdraw(uint _amount) external onlyOwner {
payable(msg.sender).transfer(_amount);
}
function getBalance() external view returns (uint) {
return address(this).balance;
}
}
Solution 1:[1]
First thing: NEVER use block.timestamp
. It's not validated and can be abused. Use the block number as a form of timestamp instead.
Second thing: start()
should be internal. Currently, anyone can call this, and this does not seem to be intentional behavior.
Finally, your issue: It appears you have two payable methods. The first one is the one that will be called. Remove the receive()
function.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Pandapip1 |