Description:
Decentralized Finance (DeFi) protocol contract providing Swap functionality.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/// @notice Minimal ERC20 interface (only what we use)
interface IERC20 {
function approve(address spender, uint256 value) external returns (bool);
function transfer(address to, uint256 value) external returns (bool);
function balanceOf(address account) external view returns (uint256);
}
/// @notice Uniswap V2 router interface (only the functions we call)
interface IUniswapV2Router02 {
function getAmountsIn(uint256 amountOut, address[] calldata path)
external
view
returns (uint256[] memory amounts);
function swapTokensForExactTokens(
uint256 amountOut, // exact USDC we want to receive
uint256 amountInMax, // max TRUTH we'll spend (slippage buffer)
address[] calldata path, // [TRUTH, USDC]
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
}
/**
* @title TruthSequencedSeller24hProtected
* @notice Sells TRUTH -> USDC in a fixed 16-step schedule totaling $1,600 per rolling 24h window.
* Call `executeStep()` ~every 90 minutes via a private relay (e.g., Flashbots Protect).
* Owner can pause, change keeper, tweak slippage, and sweep tokens.
*/
contract TruthSequencedSeller24hProtected {
// ---- access control ----
address public owner;
address public keeper; // automation EOA or service
bool public paused;
// ---- core deps ----
IERC20 public TRUTH; // token being sold
IERC20 public USDC; // stablecoin received (assumed 6 decimals)
IUniswapV2Router02 public router;
// ---- config ----
uint256 public constant TARGET_PER_WINDOW_USD = 1_600_000; // $1,600 total cap per 24h window
// 16-step schedule that sums to exactly 1,600 USDC (6 decimals)
uint256[] public steps = [
117_000, 83_000, 111_000, 89_000,
102_000, 98_000, 123_000, 77_000,
109_000, 91_000, 140_000, 60_000,
101_000, 99_000, 125_000, 75_000
];
uint256 public slippageBps = 100; // 1% slippage
// ---- rolling window state ----
uint256 public windowStart; // unix timestamp when current window began
uint256 public soldInWindowUsd; // total USDC (6 decimals) sold in this window
uint256 public stepIndex; // next index in steps[]
uint256 private _locked;
modifier nonReentrant() {
require(_locked == 0, "Reentrancy blocked");
_locked = 1;
_;
_locked = 0;
}
// ---- events ----
event StepExecuted(uint256 stepUsd, uint256 truthIn, uint256 usdcOut, uint256 nextIndex);
event WindowReset(uint256 newStart);
event KeeperUpdated(address keeper);
event SlippageUpdated(uint256 bps);
event Paused(bool isPaused);
// ---- modifiers ----
modifier onlyOwner() { require(msg.sender == owner, "Not owner"); _; }
modifier onlyKeeperOrOwner(){ require(msg.sender == keeper || msg.sender == owner, "Not keeper/owner"); _; }
modifier notPaused() { require(!paused, "Paused"); _; }
// ---- constructor ----
constructor(
address _router,
address _truth,
address _usdc,
address _keeper
) {
owner = msg.sender;
router = IUniswapV2Router02(_router);
TRUTH = IERC20(_truth);
USDC = IERC20(_usdc);
keeper = _keeper;
// Approve router once for spending TRUTH held by this contract
TRUTH.approve(_router, type(uint256).max);
// Window begins on first execution
windowStart = 0;
}
// ---- admin ----
function setKeeper(address _k) external onlyOwner {
keeper = _k;
emit KeeperUpdated(_k);
}
function setSlippage(uint256 _bps) external onlyOwner {
require(_bps <= 1000, "Too high"); // Max 10%
slippageBps = _bps;
emit SlippageUpdated(_bps);
}
function setPaused(bool _p) external onlyOwner {
paused = _p;
emit Paused(_p);
}
function sweep(address token, uint256 amount, address to) external onlyOwner {
IERC20(token).transfer(to, amount);
}
// ---- views ----
function remainingUsd() public view returns (uint256) {
if (_isNewWindow(block.timestamp)) return TARGET_PER_WINDOW_USD;
if (soldInWindowUsd >= TARGET_PER_WINDOW_USD) return 0;
return TARGET_PER_WINDOW_USD - soldInWindowUsd;
}
function nextStepUsd() public view returns (uint256) {
return steps[stepIndex % steps.length];
}
// ---- main execution ----
function executeStep() external notPaused onlyKeeperOrOwner nonReentrant {
// Initialize or roll 24h window
if (windowStart == 0) {
windowStart = block.timestamp;
emit WindowReset(windowStart);
}
if (_isNewWindow(block.timestamp)) {
_resetWindow();
}
uint256 remaining = remainingUsd();
require(remaining > 0, "Daily target reached");
// Choose step but never exceed 24h cap
uint256 stepUsd = nextStepUsd();
if (stepUsd > remaining) stepUsd = remaining;
// Quote TRUTH needed for exact USDC out
address[] memory P = _path(address(TRUTH), address(USDC));
uint256[] memory amtsIn = router.getAmountsIn(stepUsd, P);
uint256 requiredTruth = amtsIn[0];
// Add slippage buffer
uint256 amountInMax = (requiredTruth * (10_000 + slippageBps)) / 10_000;
require(TRUTH.balanceOf(address(this)) >= amountInMax, "Insufficient TRUTH");
// Perform swap
uint256[] memory res = router.swapTokensForExactTokens(
stepUsd,
amountInMax,
P,
address(this),
block.timestamp + 300
);
soldInWindowUsd += stepUsd;
stepIndex = (stepIndex + 1) % steps.length;
emit StepExecuted(stepUsd, res[0], res[res.length - 1], stepIndex);
}
// ---- internal helpers ----
function _isNewWindow(uint256 ts) internal view returns (bool) {
return (windowStart != 0) && (ts >= windowStart + 1 days);
}
function _resetWindow() internal {
windowStart = block.timestamp;
soldInWindowUsd = 0;
stepIndex = 0;
emit WindowReset(windowStart);
}
function _path(address a, address b) internal pure returns (address[] memory P) {
P = new address[](2) ;
P[0] = a;
P[1] = b;
}
}
Submitted on: 2025-11-05 11:44:02
Comments
Log in to comment.
No comments yet.