pragma solidity ^0.8.7;
// ::::::::: ::: ::: :::::::: ::::::::::: :::::::: ::: ::: :::::::::: :::: :::
// :+: :+: :+: :+: :+: :+: :+: :+: :+: :+: :+: :+: :+:+: :+:
// +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ :+:+:+ +:+
// +#++:++#: +#+ +:+ :#: +#+ +#+ +:+ +#++:++ +#++:++# +#+ +:+ +#+
// +#+ +#+ +#+ +#+ +#+ +#+# +#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+#+#
// #+# #+# #+# #+# #+# #+# #+# #+# #+# #+# #+# #+# #+# #+#+#
// ### ### ######## ######## ### ######## ### ### ########## ### ####
import "SafeMath.sol";
contract Rug {
using SafeMath for uint256;
string public symbol = "RUG";
string public name = "Rug Token";
uint256 public decimals = 18;
uint256 public floatingSupply = 10 ** 7;
uint256 public rugSupply;
uint256 public minEligibleTokens;
uint256 public totalSupply = 10 ** 9;
uint256 public chanceRugPerMillionPerTx;
uint256 public lastProbIncreaseTimestamp;
bool public hasRugged = false;
mapping(address => uint256) balances;
mapping(address => mapping(address => uint256)) allowed;
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
struct EligibleSet {
address[] addresses;
mapping (address => bool) everEligible;
mapping (address => bool) currentlyEligible;
}
EligibleSet eligibleSet;
// eligible addresses get collected here before picking winners
address[] candidates;
constructor(
uint256 _startingChanceOfRugging,
uint256 _minEligibleTokens) public {
// good default values for the constructor might be something like:
// _startingChanceOfRugging = 8 (if we expect low initial volume)
// _minEligibleTokens = 10 ** 4 (0.1% of floating token supply)
balances[msg.sender] = floatingSupply;
emit Transfer(address(0), msg.sender, floatingSupply);
rugSupply = totalSupply.sub(floatingSupply);
balances[address(this)] = rugSupply;
emit Transfer(address(0), address(this), rugSupply);
minEligibleTokens = _minEligibleTokens;
chanceRugPerMillionPerTx = _startingChanceOfRugging;
lastProbIncreaseTimestamp = block.timestamp;
}
function balanceOf(address _owner) public view returns (uint256) {
return balances[_owner];
}
function allowance(address _owner, address _spender) public view returns (uint256) {
return allowed[_owner][_spender];
}
function approve(address _spender, uint256 _value) public returns (bool) {
allowed[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
uint256 randNonce = 0;
function random() internal returns (uint256) {
randNonce += 1;
return uint256(keccak256(abi.encodePacked(
msg.sender,
randNonce,
block.timestamp,
block.difficulty
)));
}
function randomModulo(uint256 m) internal returns (uint256) {
return random() % m;
}
function shouldWeRugNow() internal returns (bool) {
// rug if the chance per million exceeds the current random
// number
// e.g. chance starts at 1 so random number needs to be 0
// to start a rug pull (whereas the other 10**6-1 values wouldn't)
return randomModulo(10**6) < chanceRugPerMillionPerTx;
}
function rugPull() internal {
// pick 9 winning addresses with at least 0.1% of the floating
// supply and send them each 11% of the total supply
hasRugged = true;
for (uint i; i < eligibleSet.addresses.length; i++) {
address addr = eligibleSet.addresses[i];
if (eligibleSet.currentlyEligible[addr]) {
candidates.push(addr);
}
}
// select 9 winning addresses and send them 1/9th
// of the remaining token supply
uint n = candidates.length;
uint tokensPerWinner = rugSupply.div(9);
for (uint i; i < 9; ++i) {
address winner = candidates[randomModulo(n)];
_transfer(address(this), winner, tokensPerWinner);
}
}
function updateEligibility(address addr) internal {
if (balances[addr] < minEligibleTokens) {
if (eligibleSet.everEligible[addr]) {
eligibleSet.currentlyEligible[addr] = false;
}
} else {
if (!eligibleSet.everEligible[addr]) {
eligibleSet.addresses.push(addr);
eligibleSet.everEligible[addr] = true;
}
eligibleSet.currentlyEligible[addr] = true;
}
}
function isEligible(address addr) public view returns (bool) {
return eligibleSet.currentlyEligible[addr];
}
function secondsSinceLastProbIncrease() public view returns (uint256) {
require(block.timestamp >= lastProbIncreaseTimestamp, "Time travel!");
return (block.timestamp - lastProbIncreaseTimestamp);
}
function daysSinceLastProbIncrease() public view returns (uint256) {
uint256 secondsPerDay = 24 * 60 * 60;
return secondsSinceLastProbIncrease() / secondsPerDay;
}
function _transfer(address _from, address _to, uint256 _value) internal {
require(balances[_from] >= _value, "Insufficient balance");
balances[_from] = balances[_from].sub(_value);
updateEligibility(_from);
balances[_to] = balances[_to].add(_value);
updateEligibility(_to);
emit Transfer(_from, _to, _value);
if (!hasRugged) {
if (shouldWeRugNow()) {
rugPull();
} else if (daysSinceLastProbIncrease() >= 1) {
lastProbIncreaseTimestamp = block.timestamp;
chanceRugPerMillionPerTx *= 2;
}
}
}
function transfer(address _to, uint256 _value) public returns (bool) {
_transfer(msg.sender, _to, _value);
return true;
}
function transferFrom(address _from, address _to, uint256 _value) public returns (bool)
{
require(allowed[_from][msg.sender] >= _value, "Insufficient allowance");
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
_transfer(_from, _to, _value);
return true;
}
}
pragma solidity ^0.8.7;
library SafeMath {
function add(uint a, uint b) internal pure returns (uint c) {
c = a + b;
require(c >= a);
return c;
}
function sub(uint a, uint b) internal pure returns (uint c) {
require(b <= a);
c = a - b;
return c;
}
function mul(uint a, uint b) internal pure returns (uint c) {
c = a * b;
require(a == 0 || c / a == b);
return c;
}
function div(uint a, uint b) internal pure returns (uint c) {
require(b > 0);
c = a / b;
return c;
}
}
{
"compilationTarget": {
"Rug.sol": "Rug"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"uint256","name":"_startingChanceOfRugging","type":"uint256"},{"internalType":"uint256","name":"_minEligibleTokens","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"chanceRugPerMillionPerTx","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"daysSinceLastProbIncrease","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"floatingSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hasRugged","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"isEligible","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastProbIncreaseTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minEligibleTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rugSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"secondsSinceLastProbIncrease","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]