文件 1 的 3:TokenTurner.sol
pragma solidity >=0.6.2;
import './Utilities.sol';
contract TokenTurner is Utilities {
uint256 constant FUNDING_EPOCHS = 12;
uint256 constant DECAY_PER_EPOCH = 4;
uint256 constant MAX_DECAY_RATE = 100;
uint256 constant FUNDING_PRICE = 25e6;
uint256 constant MAX_EPOCH = FUNDING_EPOCHS + (MAX_DECAY_RATE / DECAY_PER_EPOCH);
function INPUT_TOKEN () internal view virtual returns (address) {
}
function OUTPUT_TOKEN () internal view virtual returns (address) {
}
function COMMUNITY_FUND () internal view virtual returns (address) {
}
struct InflowOutflow {
uint128 inflow;
uint128 outflow;
}
uint256 activeEpoch;
mapping (uint256 => mapping (address => InflowOutflow)) public inflowOutflow;
event Buy (address indexed buyer, uint256 indexed epoch, uint256 amount);
event Sell (address indexed seller, uint256 indexed epoch, uint256 amount);
event Claim (uint256 epoch, uint256 amount);
function getCurrentEpoch () public view virtual returns (uint256 epoch) {
uint256 FUNDING_START_DATE = 1614899552;
uint256 EPOCH_SECONDS = 604800;
epoch = (block.timestamp - FUNDING_START_DATE) / EPOCH_SECONDS;
if (epoch > MAX_EPOCH) {
epoch = MAX_EPOCH;
}
}
function getDecayRateForEpoch (uint256 epoch) public view returns (uint256 rate) {
rate = (getCurrentEpoch() - epoch) * DECAY_PER_EPOCH;
if (rate > MAX_DECAY_RATE) {
rate = MAX_DECAY_RATE;
}
}
function updateEpoch () public {
require(msg.sender != address(this));
uint256 currentEpoch = getCurrentEpoch();
if (currentEpoch >= MAX_EPOCH) {
address receiver = COMMUNITY_FUND();
uint256 balance = Utilities._safeBalance(INPUT_TOKEN(), address(this));
if (balance > 0) {
Utilities._safeTransfer(INPUT_TOKEN(), receiver, balance);
}
balance = Utilities._safeBalance(OUTPUT_TOKEN(), address(this));
if (balance > 0) {
Utilities._safeTransfer(OUTPUT_TOKEN(), receiver, balance);
}
return;
}
if (currentEpoch > activeEpoch) {
activeEpoch = currentEpoch;
uint256 balance = Utilities._safeBalance(INPUT_TOKEN(), address(this));
uint256 claimableAmount = (balance / MAX_DECAY_RATE) * DECAY_PER_EPOCH;
if (claimableAmount > 0) {
emit Claim(currentEpoch, claimableAmount);
Utilities._safeTransfer(INPUT_TOKEN(), COMMUNITY_FUND(), claimableAmount);
}
}
}
function getQuote (uint256 amountIn, uint256[] memory path) public view returns (uint256 inflow, uint256 outflow) {
uint256[] memory amounts = UniswapV2Library.getAmountsOut(amountIn, path);
inflow = amounts[amounts.length - 1];
outflow = inflow / FUNDING_PRICE;
}
function swapIn (
address receiver,
uint256 inputAmount,
uint256[] memory swapRoute,
bytes memory permitData
) external payable {
updateEpoch();
address fromToken = address(swapRoute[0]);
Utilities._maybeRedeemPermit(fromToken, permitData);
uint256 inflowAmount = inputAmount;
if (fromToken == INPUT_TOKEN()) {
Utilities._safeTransferFrom(fromToken, msg.sender, address(this), inflowAmount);
} else {
uint256 oldBalance = Utilities._safeBalance(INPUT_TOKEN(), address(this));
if (msg.value == 0) {
Utilities._swapExactTokensForTokens(swapRoute, inputAmount, msg.sender, address(this));
} else {
Utilities._swapExactETHForTokens(swapRoute, msg.value, address(this));
}
uint256 newBalance = Utilities._safeBalance(INPUT_TOKEN(), address(this));
require(newBalance > oldBalance, 'BALANCE');
inflowAmount = newBalance - oldBalance;
}
uint256 currentEpoch = getCurrentEpoch();
require(currentEpoch < FUNDING_EPOCHS, 'PRESALE_OVER');
uint256 outflowAmount = inflowAmount / FUNDING_PRICE;
require(outflowAmount != 0, 'ZERO_AMOUNT');
emit Buy(msg.sender, currentEpoch, outflowAmount);
inflowOutflow[currentEpoch][msg.sender].inflow += uint128(inflowAmount);
Utilities._safeTransfer(OUTPUT_TOKEN(), receiver, outflowAmount);
}
function swapOut (
address receiver,
uint256 inputSellAmount,
uint256 epoch,
uint256[] memory swapRoute,
bytes memory permitData
) external {
updateEpoch();
uint256 currentEpoch = getCurrentEpoch();
require(epoch <= currentEpoch, 'EPOCH');
Utilities._maybeRedeemPermit(OUTPUT_TOKEN(), permitData);
uint128 sellAmount = uint128(inputSellAmount * FUNDING_PRICE);
{
InflowOutflow storage account = inflowOutflow[epoch][msg.sender];
uint128 swappableAmount = account.inflow;
uint128 oldOutflow = account.outflow;
uint128 newOutflow = sellAmount + oldOutflow;
require(newOutflow > oldOutflow);
if (epoch != currentEpoch) {
uint256 decay = getDecayRateForEpoch(epoch);
swappableAmount = uint128(swappableAmount - ((swappableAmount / MAX_DECAY_RATE) * decay));
}
require(newOutflow <= swappableAmount, 'AMOUNT');
account.outflow = newOutflow;
}
emit Sell(msg.sender, epoch, inputSellAmount);
Utilities._safeTransferFrom(OUTPUT_TOKEN(), msg.sender, address(this), inputSellAmount);
if (swapRoute.length == 1) {
Utilities._safeTransfer(INPUT_TOKEN(), receiver, sellAmount);
} else {
address wethIfNotZero = address(swapRoute[0]);
swapRoute[0] = uint256(INPUT_TOKEN());
if (wethIfNotZero == address(0)) {
Utilities._swapExactTokensForTokens(swapRoute, sellAmount, address(this), receiver);
} else {
Utilities._swapExactTokensForETH(swapRoute, sellAmount, address(this), receiver, wethIfNotZero);
}
}
}
function recoverLostTokens (address token) external {
require(token != INPUT_TOKEN() && token != OUTPUT_TOKEN());
Utilities._safeTransfer(token, COMMUNITY_FUND(), Utilities._safeBalance(token, address(this)));
}
fallback () external payable {
assembly {
if eq(caller(), origin()) {
revert(0, 0)
}
}
}
}
文件 3 的 3:Utilities.sol
interface IWETH {
function deposit() external payable;
function transfer(address to, uint value) external returns (bool);
function withdraw(uint) external;
}
interface IUniswapV2Pair {
function factory() external view returns (address);
function token0() external view returns (address);
function token1() external view returns (address);
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
function price0CumulativeLast() external view returns (uint);
function price1CumulativeLast() external view returns (uint);
function kLast() external view returns (uint);
function mint(address to) external returns (uint liquidity);
function burn(address to) external returns (uint amount0, uint amount1);
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
function skim(address to) external;
function sync() external;
}
interface IDroplet {
function drip () external;
}
library SafeMath {
function add(uint x, uint y) internal pure returns (uint z) {
require((z = x + y) >= x, 'ADD_OVERFLOW');
}
function sub(uint x, uint y) internal pure returns (uint z) {
require((z = x - y) <= x, 'SUB_OVERFLOW');
}
function mul(uint x, uint y) internal pure returns (uint z) {
require(y == 0 || (z = x * y) / y == x, 'MUL_OVERFLOW');
}
}
library UniswapV2Library {
using SafeMath for uint;
function getReserves (uint256 pair) internal view returns (uint reserveA, uint reserveB) {
address addr = address(pair >> 1);
uint direction = pair & 1;
(uint reserve0, uint reserve1,) = IUniswapV2Pair(addr).getReserves();
(reserveA, reserveB) = direction == 0 ? (reserve0, reserve1) : (reserve1, reserve0);
}
function getAmountOut (uint amountIn, uint reserveIn, uint reserveOut) internal pure returns (uint amountOut) {
require(amountIn > 0, 'INSUFFICIENT_INPUT_AMOUNT');
require(reserveIn > 0 && reserveOut > 0, 'INSUFFICIENT_LIQUIDITY');
uint amountInWithFee = amountIn.mul(997);
uint numerator = amountInWithFee.mul(reserveOut);
uint denominator = reserveIn.mul(1000).add(amountInWithFee);
amountOut = numerator / denominator;
}
function getAmountsOut (uint amountIn, uint256[] memory pairs) internal view returns (uint[] memory amounts) {
amounts = new uint[](pairs.length);
amounts[0] = amountIn;
for (uint i = 1; i < pairs.length; i++) {
(uint reserveIn, uint reserveOut) = getReserves(pairs[i]);
amounts[i] = getAmountOut(amounts[i - 1], reserveIn, reserveOut);
}
}
}
contract Utilities {
bytes4 private constant SIG_TRANSFER = 0xa9059cbb;
bytes4 private constant SIG_TRANSFER_FROM = 0x23b872dd;
bytes4 private constant SIG_APPROVE = 0x095ea7b3;
bytes4 private constant SIG_BALANCE = 0x70a08231;
function _safeTransfer (address token, address to, uint256 amount) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(SIG_TRANSFER, to, amount));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'TRANSFER');
}
function _safeTransferFrom (address token, address from, address to, uint256 amount) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(SIG_TRANSFER_FROM, from, to, amount));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'TRANSFER_FROM');
}
function _safeTransferETH (address to, uint256 amount) internal {
(bool success,) = to.call{value:amount}("");
require(success, 'TRANSFER_ETH');
}
function _safeApprove (address token, address spender, uint256 amount) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(SIG_APPROVE, spender, amount));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'APPROVE');
}
function _safeBalance (address token, address account) internal returns (uint256) {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(SIG_BALANCE, account));
require(success, 'BALANCE');
return abi.decode(data, (uint256));
}
function _safeTransferWrapper (address token, address from, address to, uint256 amount) internal {
if (from == address(this)) {
_safeTransfer(token, to, amount);
} else {
_safeTransferFrom(token, from, to, amount);
}
}
function _maybeRedeemPermit (address token, bytes memory permitData) internal {
if (permitData.length > 0) {
bool success;
assembly {
let dataPtr := add(permitData, 32)
let functionSig := shr(224, mload(dataPtr))
{
let _owner := shr(96, mload(add(dataPtr, 16)))
if eq(_owner, address()) {
revert(0, 0)
}
}
if or( eq(functionSig, 0xd505accf), eq(functionSig, 0x8fcbaf0c) ) {
let size := mload(permitData)
success := call(gas(), token, 0, dataPtr, size, 0, 0)
}
}
require(success, 'PERMIT');
}
}
function _swap (uint[] memory amounts, uint256[] memory path, address _to) internal {
for (uint i = 1; i < path.length; i++) {
uint amountOut = amounts[i];
address pair = address(path[i] >> 1);
uint direction = path[i] & 1;
(uint amount0Out, uint amount1Out) = direction == 0 ? (uint(0), amountOut) : (amountOut, uint(0));
address to = i < path.length - 1 ? pair : _to;
IUniswapV2Pair(pair).swap(
amount0Out, amount1Out, to, ""
);
}
}
function _swapExactTokensForTokens (
uint256[] memory path,
uint amountIn,
address from,
address to
) internal returns (uint[] memory amounts)
{
amounts = UniswapV2Library.getAmountsOut(amountIn, path);
_safeTransferWrapper(address(path[0]), from, address(path[1] >> 1), amounts[0]);
_swap(amounts, path, to);
}
function _swapExactETHForTokens (
uint256[] memory path,
uint amountIn,
address to
) internal returns (uint[] memory amounts)
{
amounts = UniswapV2Library.getAmountsOut(amountIn, path);
IWETH(path[0]).deposit{value: amounts[0]}();
_safeTransferWrapper(address(path[0]), address(this), address(path[1] >> 1), amounts[0]);
_swap(amounts, path, to);
}
function _swapExactTokensForETH (
uint256[] memory path,
uint amountIn,
address from,
address to,
address weth
) internal returns (uint[] memory amounts)
{
amounts = UniswapV2Library.getAmountsOut(amountIn, path);
_safeTransferWrapper(address(path[0]), from, address(path[1] >> 1), amounts[0]);
_swap(amounts, path, address(this));
uint256 finalOutputAmount = amounts[amounts.length - 1];
IWETH(weth).withdraw(finalOutputAmount);
_safeTransferETH(to, finalOutputAmount);
}
}