OptiBuys
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts/utils/Context.sol";
import "@openzeppelin/contracts/interfaces/IERC20.sol";
interface IOptiSwap {
function buyToken(address token, uint amountOutMin, uint deadline) external payable;
}
contract OptiBuy is OwnableUpgradeable {
IERC20 public token;
mapping(address => uint256) public contributionOf;
uint public threshold;
uint public deadline;
uint public unlock;
uint public totalShares;
uint public currentShares;
enum campaignStatus { FUNDING, FAILED, FINALIZED }
campaignStatus public status;
receive() external payable {}
function initialize(address set_token, uint set_threshold, uint set_deadline, uint set_unlock) public initializer {
token = IERC20(set_token);
threshold = set_threshold;
deadline = set_deadline;
unlock = set_unlock;
__Ownable_init(_msgSender());
status = campaignStatus.FUNDING;
}
function contribute() public payable {
//Add to buy pot
require (status == campaignStatus.FUNDING, "OptiBuy: not funding.");
require (block.timestamp < deadline, "OptiBuy: Deadline passed.");
contributionOf[_msgSender()] += msg.value;
currentShares += msg.value;
}
function finalize(uint amountOutMin, uint buy_deadline) public onlyOwner() {
//End OptiBuy with market purchase
require (status == campaignStatus.FUNDING, "OptiBuy: not funding.");
require (block.timestamp > deadline, "OptiBuy: deadline not reached.");
totalShares = currentShares;
if (threshold > 0) {
require (address(this).balance >= threshold, "OptiBuy: Threshold not met!");
}
IOptiSwap(0x293be20db3e4110670aFBcAE916393e40BC9B42b).buyToken
{value: address(this).balance}
(address(token), amountOutMin, buy_deadline);
status = campaignStatus.FINALIZED;
}
function fail() public {
require (status == campaignStatus.FUNDING, "OptiBuy: not funding.");
require (block.timestamp > deadline, "OptiBuy: Campaign can still succeed.");
if (address(this).balance >= threshold) {
require (block.timestamp > (deadline + 1 days), "OptiBuy: Owner can still finalize.");
}
status = campaignStatus.FAILED;
}
function recover() public {
//Release funds from failed campaigns
require(status == campaignStatus.FAILED, "OptiBuy: Campaign has not failed.");
payable(_msgSender()).transfer(contributionOf[_msgSender()]);
contributionOf[_msgSender()] = 0;
}
function tokenBalanceOf(address account) public view returns (uint) {
return contributionOf[account] * token.balanceOf(address(this)) / currentShares;
}
function earlyWithdraw() public {
require(status == campaignStatus.FINALIZED, "OptiBuy: Not finalized.");
require(block.timestamp < unlock, "OptiBuy: Tokens unlocked (try normal withdraw).");
uint amount = tokenBalanceOf(_msgSender()) * 80 / 100;
if (contributionOf[_msgSender()] == currentShares) {
//If user is last to withdraw, waive fee.
amount = tokenBalanceOf(_msgSender());
}
token.transfer(_msgSender(), amount);
currentShares -= contributionOf[_msgSender()];
contributionOf[_msgSender()] = 0;
}
function withdraw() public {
require(status == campaignStatus.FINALIZED, "OptiBuy: Not finalized.");
require(block.timestamp > unlock, "OptiBuy: Tokens still soft-locked (try early withdraw).");
token.transfer(_msgSender(), tokenBalanceOf(_msgSender()));
currentShares -= contributionOf[_msgSender()];
contributionOf[_msgSender()] = 0;
}
}Last updated