Address
0xe3d5de07962ca32a7c19385d38014e5bb57dfd87Current Holdings
$0.00
TXs sent
0
First Active
2026-02-26
block 25,891,725
Last Active
today
block 27,506,579
Funded By
not identified
Net worth historyi
No net-worth snapshots recorded yet
partial matchTWAPOraclesolc 0.8.28+commit.7893614aruntime partial · creation partial
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// --- Interfaces ---
interface IUniswapV2Pair {
function price0CumulativeLast() external view returns (uint);
function price1CumulativeLast() external view returns (uint);
function getReserves()
external
view
returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
function token0() external view returns (address);
function token1() external view returns (address);
}
interface IERC20Decimals {
function decimals() external view returns (uint8);
}
interface ITWAPToken {
function totalSupply() external view returns (uint);
function balanceOf(address account) external view returns (uint);
function mint(address to, uint amount) external;
}
// --- Libraries ---
library FixedPoint {
struct uq112x112 {
uint224 _x;
}
struct uq144x112 {
uint _x;
}
uint8 private constant RESOLUTION = 112;
function encode(uint112 x) internal pure returns (uq112x112 memory) {
return uq112x112(uint224(x) << RESOLUTION);
}
function mul(
uq112x112 memory self,
uint y
) internal pure returns (uq144x112 memory) {
return uq144x112(self._x * y);
}
function fraction(
uint112 numerator,
uint112 denominator
) internal pure returns (uq112x112 memory) {
if (denominator == 0) revert("FixedPoint: DIV_BY_ZERO");
return uq112x112((uint224(numerator) << RESOLUTION) / denominator);
}
function decode144(uq144x112 memory self) internal pure returns (uint144) {
return uint144(self._x >> RESOLUTION);
}
function rpow(uint x, uint n) internal pure returns (uint z) {
z = n % 2 != 0 ? x : 1e18;
for (n /= 2; n != 0; n /= 2) {
x = (x * x) / 1e18;
if (n % 2 != 0) z = (z * x) / 1e18;
}
}
}
library UniswapV2OracleLibrary {
using FixedPoint for *;
function currentBlockTimestamp() internal view returns (uint) {
return block.timestamp;
}
function currentCumulativePrices(
address pair
)
internal
view
returns (
uint price0Cumulative,
uint price1Cumulative,
uint blockTimestamp
)
{
blockTimestamp = currentBlockTimestamp();
price0Cumulative = IUniswapV2Pair(pair).price0CumulativeLast();
price1Cumulative = IUniswapV2Pair(pair).price1CumulativeLast();
(
uint112 reserve0,
uint112 reserve1,
uint32 blockTimestampLast
) = IUniswapV2Pair(pair).getReserves();
if (blockTimestampLast != blockTimestamp) {
uint32 timeElapsed = uint32(blockTimestamp - blockTimestampLast);
unchecked {
price0Cumulative +=
uint(FixedPoint.fraction(reserve1, reserve0)._x) *
timeElapsed;
price1Cumulative +=
uint(FixedPoint.fraction(reserve0, reserve1)._x) *
timeElapsed;
}
}
}
struct PriceData {
uint price0Cumulative;
uint price1Cumulative;
}
function batchCumulativePrices(
address[] memory pairs
) internal view returns (PriceData[] memory prices, uint blockTimestamp) {
blockTimestamp = currentBlockTimestamp();
prices = new PriceData[](pairs.length);
for (uint i; i < pairs.length; ) {
IUniswapV2Pair pair = IUniswapV2Pair(pairs[i]);
uint price0Cumulative = pair.price0CumulativeLast();
uint price1Cumulative = pair.price1CumulativeLast();
(
uint112 reserve0,
uint112 reserve1,
uint32 blockTimestampLast
) = pair.getReserves();
if (blockTimestampLast != blockTimestamp) {
uint32 timeElapsed = uint32(
blockTimestamp - blockTimestampLast
);
unchecked {
price0Cumulative +=
uint(FixedPoint.fraction(reserve1, reserve0)._x) *
timeElapsed;
price1Cumulative +=
uint(FixedPoint.fraction(reserve0, reserve1)._x) *
timeElapsed;
}
}
prices[i] = PriceData(price0Cumulative, price1Cumulative);
unchecked {
++i;
}
}
}
}
// --- Oracle Contract ---
contract TWAPOracle {
using FixedPoint for *;
struct Observation {
uint timestamp;
uint price0Cumulative;
uint price1Cumulative;
}
struct ReferenceStable {
address stable;
address pair;
bool isWplsToken0;
uint8 stableDecimals;
bool active;
}
struct PairInfo {
address pair;
bool isToken0;
}
// Errors
error UnknownPair();
error PairAlreadyAdded();
error InvalidPair();
error InvalidTWAPWindow();
error Unauthorized();
error PeriodNotElapsed();
error RewardsNotEnabled();
error InsufficientBalanceForReward();
error AlreadyLaunched();
error NoValidReferences();
error StalePrice();
error InsufficientObservations();
error InvalidIndex();
address public immutable wpls;
ITWAPToken public immutable twapToken; // The separated token
mapping(address => uint) public wards;
function rely(address usr) external auth {
wards[usr] = 1;
emit Rely(usr);
}
function deny(address usr) external auth {
wards[usr] = 0;
emit Deny(usr);
}
modifier auth() {
if (wards[msg.sender] != 1) revert Unauthorized();
_;
}
bool public rewardsEnabled;
// Constants (Using the responsive 15m settings)
uint public constant PERIOD_SIZE = 900;
uint public constant MIN_WINDOW = 900;
uint public constant MAX_WINDOW = 7200;
uint public constant REWARD_WINDOW = 1350;
uint public constant DECAY_RATE = 500000000000000000; // 0.5
uint public constant DECAY_PERIOD = 225;
// Reward Config
uint public constant BASE_REWARD_RATE = 13888888888888888;
uint public constant INITIAL_SUPPLY = 1_000_000 * 10 ** 18;
uint public constant MIN_HOLDING_DIVISOR = 555;
// State
mapping(address => Observation[5]) public observations;
mapping(address => uint8) public observationHeads;
address[] public livePairs;
mapping(address => PairInfo) public tokenToPairInfo;
address[] public pricedTokens;
ReferenceStable[] public referenceStables;
uint public lastSuperUpdateTimestamp;
// Events
event PriceUpdated(
address indexed token,
address indexed pair,
uint price0Cumulative,
uint price1Cumulative,
uint timestamp
);
event Reward(address indexed updater, uint amount);
event RewardsLaunched();
event Rely(address indexed usr);
event Deny(address indexed usr);
event ReferenceAdded(
address indexed stable,
address indexed pair,
bool isWplsToken0,
uint8 stableDecimals
);
event ReferenceUpdated(uint indexed index, bool active);
event ReferenceRemoved(uint indexed index, address indexed pair);
event DirectPairAdded(
address indexed token,
address indexed pair,
bool isToken0
);
event DirectPairRemoved(address indexed token);
event SuperUpdate(uint timestamp, uint updatedPairs);
constructor(address _wpls, address _twapToken) {
wpls = _wpls;
twapToken = ITWAPToken(_twapToken);
wards[msg.sender] = 1;
emit Rely(msg.sender);
}
// --- Views ---
function getMinHoldingForReward() public view returns (uint) {
return twapToken.totalSupply() / MIN_HOLDING_DIVISOR;
}
function launch() external auth {
if (rewardsEnabled) revert AlreadyLaunched();
rewardsEnabled = true;
emit RewardsLaunched();
}
// --- Admin Logic ---
function _isPairLive(address pair) internal view returns (bool) {
for (uint i = 0; i < livePairs.length; i++) {
if (livePairs[i] == pair) return true;
}
return false;
}
function addReferenceStable(
address stable,
address pair,
bool isWplsToken0
) external auth {
address token0 = IUniswapV2Pair(pair).token0();
address token1 = IUniswapV2Pair(pair).token1();
if (
(isWplsToken0 && token0 != wpls) ||
(!isWplsToken0 && token1 != wpls)
) revert InvalidPair();
if (stable != token0 && stable != token1) revert InvalidPair();
uint8 stableDecimals = IERC20Decimals(stable).decimals();
if (stableDecimals > 18) revert("Stable decimals >18");
referenceStables.push(
ReferenceStable(stable, pair, isWplsToken0, stableDecimals, true)
);
if (!_isPairLive(pair)) {
if (observationHeads[pair] == 0) {
(
uint price0Cumulative,
uint price1Cumulative,
) = UniswapV2OracleLibrary.currentCumulativePrices(pair);
observations[pair][0] = Observation(
block.timestamp,
price0Cumulative,
price1Cumulative
);
observationHeads[pair] = 1;
}
livePairs.push(pair);
}
emit ReferenceAdded(stable, pair, isWplsToken0, stableDecimals);
}
function updateReference(uint index, bool active) external auth {
if (index >= referenceStables.length) revert("Invalid index");
referenceStables[index].active = active;
emit ReferenceUpdated(index, active);
}
function addDirectPair(
address token,
address pair,
bool isToken0
) external auth {
if (tokenToPairInfo[token].pair != address(0))
revert PairAlreadyAdded();
address token0 = IUniswapV2Pair(pair).token0();
address token1 = IUniswapV2Pair(pair).token1();
if ((isToken0 && token0 != token) || (!isToken0 && token1 != token))
revert InvalidPair();
tokenToPairInfo[token] = PairInfo(pair, isToken0);
pricedTokens.push(token);
if (!_isPairLive(pair)) {
if (observationHeads[pair] == 0) {
(
uint price0Cumulative,
uint price1Cumulative,
) = UniswapV2OracleLibrary.currentCumulativePrices(pair);
observations[pair][0] = Observation(
block.timestamp,
price0Cumulative,
price1Cumulative
);
observationHeads[pair] = 1;
}
livePairs.push(pair);
}
emit DirectPairAdded(token, pair, isToken0);
}
function removeDirectPair(address token) external auth {
PairInfo memory info = tokenToPairInfo[token];
if (info.pair == address(0)) revert UnknownPair();
delete tokenToPairInfo[token];
for (uint i = 0; i < pricedTokens.length; i++) {
if (pricedTokens[i] == token) {
pricedTokens[i] = pricedTokens[pricedTokens.length - 1];
pricedTokens.pop();
break;
}
}
address pair = info.pair;
bool isStillReference = false;
for (uint i = 0; i < referenceStables.length; i++) {
if (referenceStables[i].pair == pair) {
isStillReference = true;
break;
}
}
if (!isStillReference) {
_removeLivePair(pair);
delete observations[pair];
delete observationHeads[pair];
}
emit DirectPairRemoved(token);
}
function removeReferenceStable(uint index) external auth {
if (index >= referenceStables.length) revert InvalidIndex();
address pair = referenceStables[index].pair;
referenceStables[index] = referenceStables[referenceStables.length - 1];
referenceStables.pop();
bool isStillUsed = false;
for (uint i = 0; i < pricedTokens.length; i++) {
if (tokenToPairInfo[pricedTokens[i]].pair == pair) {
isStillUsed = true;
break;
}
}
if (!isStillUsed) {
for (uint i = 0; i < referenceStables.length; i++) {
if (referenceStables[i].pair == pair) {
isStillUsed = true;
break;
}
}
}
if (!isStillUsed) {
_removeLivePair(pair);
delete observations[pair];
delete observationHeads[pair];
}
emit ReferenceRemoved(index, pair);
}
function _removeLivePair(address pair) internal {
for (uint i = 0; i < livePairs.length; i++) {
if (livePairs[i] == pair) {
livePairs[i] = livePairs[livePairs.length - 1];
livePairs.pop();
break;
}
}
}
// --- Core Logic (FIXED with correct CEI + reward timing) ---
function superUpdate() public {
uint currentTimestamp = block.timestamp;
if (currentTimestamp - lastSuperUpdateTimestamp < MIN_WINDOW)
revert PeriodNotElapsed();
// === CHECKS DONE ===
uint totalPairs = livePairs.length;
address[] memory pairsToUpdate = new address[](totalPairs);
uint updateCount = 0;
for (uint i = 0; i < totalPairs; ) {
address pair = livePairs[i];
uint8 head = observationHeads[pair];
uint8 lastIdx = (head == 0) ? 4 : head - 1;
if (
currentTimestamp - observations[pair][lastIdx].timestamp >=
PERIOD_SIZE
) {
pairsToUpdate[updateCount] = pair;
unchecked {
++updateCount;
}
}
unchecked {
++i;
}
}
// === EFFECTS START (price updates + timestamp) ===
if (updateCount > 0) {
address[] memory activeBatch = new address[](updateCount);
for (uint i = 0; i < updateCount; i++) {
activeBatch[i] = pairsToUpdate[i];
}
(
UniswapV2OracleLibrary.PriceData[] memory prices,
) = UniswapV2OracleLibrary.batchCumulativePrices(activeBatch);
for (uint i = 0; i < updateCount; i++) {
_applyUpdate(activeBatch[i], prices[i], currentTimestamp);
}
}
// Calculate reward BEFORE updating timestamp (this is the key fix)
uint reward = 0;
if (
rewardsEnabled &&
twapToken.balanceOf(msg.sender) >= getMinHoldingForReward()
) {
reward = calculateReward(currentTimestamp); // still uses OLD lastSuperUpdateTimestamp
}
// Now update timestamp (state change)
lastSuperUpdateTimestamp = currentTimestamp;
emit SuperUpdate(currentTimestamp, updateCount);
// === INTERACTIONS (external call last) ===
if (reward > 0) {
_mintReward(msg.sender, reward);
}
}
function _applyUpdate(
address pair,
UniswapV2OracleLibrary.PriceData memory price,
uint blockTimestamp
) private {
uint8 head = observationHeads[pair];
observations[pair][head] = Observation(
blockTimestamp,
price.price0Cumulative,
price.price1Cumulative
);
observationHeads[pair] = (head + 1) % 5;
emit PriceUpdated(
address(0),
pair,
price.price0Cumulative,
price.price1Cumulative,
blockTimestamp
);
}
function getMedianPrice(
address pair,
bool needPrice0,
uint currentCumulative,
uint currentTimestamp
) internal view returns (uint224 medianPrice) {
Observation[5] memory obsArray;
for (uint i = 0; i < 5; i++) {
obsArray[i] = observations[pair][i];
}
for (uint i = 1; i < 5; ) {
Observation memory key = obsArray[i];
uint j = i;
while (j > 0 && obsArray[j - 1].timestamp < key.timestamp) {
obsArray[j] = obsArray[j - 1];
j--;
}
obsArray[j] = key;
unchecked {
++i;
}
}
uint224[] memory segmentPrices = new uint224[](5);
uint count = 0;
uint prevCum = currentCumulative;
uint prevTs = currentTimestamp;
for (uint i = 0; i < 5; i++) {
if (obsArray[i].timestamp == 0) continue;
uint timeElapsed = prevTs - obsArray[i].timestamp;
if (timeElapsed < MIN_WINDOW || timeElapsed > MAX_WINDOW) continue;
uint obsCum = needPrice0
? obsArray[i].price0Cumulative
: obsArray[i].price1Cumulative;
uint segmentTWAP = (prevCum - obsCum) / timeElapsed;
segmentPrices[count] = uint224(segmentTWAP);
count++;
prevCum = obsCum;
prevTs = obsArray[i].timestamp;
if (count >= 3) break; // Rolling Window
}
if (count < 3) revert InsufficientObservations();
for (uint i = 1; i < count; ) {
uint224 key = segmentPrices[i];
uint j = i;
while (j > 0 && segmentPrices[j - 1] > key) {
segmentPrices[j] = segmentPrices[j - 1];
j--;
}
segmentPrices[j] = key;
unchecked {
++i;
}
}
if (count % 2 == 1) {
medianPrice = segmentPrices[count / 2];
} else {
medianPrice =
(segmentPrices[count / 2 - 1] + segmentPrices[count / 2]) /
2;
}
}
function _getNormalizedPriceFromRef(
ReferenceStable memory ref
) internal view returns (uint priceNormalized) {
(uint112 reserve0, uint112 reserve1, ) = IUniswapV2Pair(ref.pair)
.getReserves();
uint wplsReserve = ref.isWplsToken0 ? uint(reserve0) : uint(reserve1);
if (wplsReserve == 0) return 0;
(
uint price0Cumulative,
uint price1Cumulative,
uint blockTimestamp
) = UniswapV2OracleLibrary.currentCumulativePrices(ref.pair);
bool needPrice0 = ref.isWplsToken0;
uint224 medianPrice = getMedianPrice(
ref.pair,
needPrice0,
needPrice0 ? price0Cumulative : price1Cumulative,
blockTimestamp
);
uint scalar = 1e18 * (10 ** (18 - ref.stableDecimals));
FixedPoint.uq144x112 memory fullPrice = FixedPoint
.uq112x112(medianPrice)
.mul(scalar);
priceNormalized = fullPrice._x >> 112;
}
function getWplsPriceInUsd() public view returns (uint priceWad) {
uint validCount = 0;
uint[] memory prices = new uint[](referenceStables.length);
for (uint i = 0; i < referenceStables.length; i++) {
ReferenceStable memory ref = referenceStables[i];
if (!ref.active) continue;
uint price = _getNormalizedPriceFromRef(ref);
if (price == 0) continue;
prices[validCount] = price;
validCount++;
}
if (validCount == 0) revert NoValidReferences();
for (uint i = 0; i < validCount; i++) {
for (uint j = i + 1; j < validCount; j++) {
if (prices[i] > prices[j]) {
uint temp = prices[i];
prices[i] = prices[j];
prices[j] = temp;
}
}
}
if (validCount % 2 == 1) {
priceWad = prices[validCount / 2];
} else {
priceWad =
(prices[validCount / 2 - 1] + prices[validCount / 2]) /
2;
}
}
function getPriceInUsd(
address token,
uint amountIn
) public view returns (uint amountOutUsd) {
if (token == wpls) {
return (amountIn * getWplsPriceInUsd()) / 1e18;
}
PairInfo memory info = tokenToPairInfo[token];
if (info.pair != address(0)) {
(
uint price0Cumulative,
uint price1Cumulative,
uint blockTimestamp
) = UniswapV2OracleLibrary.currentCumulativePrices(info.pair);
uint224 medianPrice = getMedianPrice(
info.pair,
info.isToken0,
info.isToken0 ? price0Cumulative : price1Cumulative,
blockTimestamp
);
FixedPoint.uq144x112 memory temp = FixedPoint
.uq112x112(medianPrice)
.mul(amountIn);
uint amountInWpls = temp._x >> 112;
amountOutUsd = (amountInWpls * getWplsPriceInUsd()) / 1e18;
} else {
revert UnknownPair();
}
}
// --- Rewards ---
function _mintReward(address updater, uint reward) private {
if (!rewardsEnabled) revert RewardsNotEnabled();
if (reward == 0) return;
twapToken.mint(updater, reward); // Calls the external token contract
emit Reward(updater, reward);
}
function calculateReward(uint currentTime) internal view returns (uint) {
if (!rewardsEnabled) return 0;
uint timeSinceLast = currentTime - lastSuperUpdateTimestamp;
if (timeSinceLast < MIN_WINDOW) return 0;
uint effectiveTime = timeSinceLast - MIN_WINDOW;
uint peakEffectiveTime = REWARD_WINDOW - MIN_WINDOW;
uint timeBasedReward;
if (effectiveTime <= peakEffectiveTime) {
timeBasedReward = BASE_REWARD_RATE * effectiveTime;
} else {
uint cappedExtra = effectiveTime - peakEffectiveTime;
uint decayIntervals = cappedExtra / DECAY_PERIOD;
uint decayFactor = FixedPoint.rpow(DECAY_RATE, decayIntervals);
uint peakReward = BASE_REWARD_RATE * peakEffectiveTime;
timeBasedReward = (peakReward * decayFactor) / 1e18;
}
uint supplyFactor = 1e18;
uint currentSupply = twapToken.totalSupply(); // Read from external token
if (currentSupply > INITIAL_SUPPLY) {
uint a = INITIAL_SUPPLY;
uint b = currentSupply;
supplyFactor = (2 * a * 1e18) / (a + b);
}
return (timeBasedReward * supplyFactor) / 1e18;
}
function getTokens() external view returns (address[] memory) {
return pricedTokens;
}
function getReferenceCount() external view returns (uint) {
return referenceStables.length;
}
function getPricedTokenCount() external view returns (uint) {
return pricedTokens.length;
}
function getCurrentReward() external view returns (uint) {
return calculateReward(block.timestamp);
}
}