Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- BuybackRouter
- Optimization enabled
- false
- Compiler version
- v0.8.26+commit.8a97fa7a
- EVM Version
- Verified at
- 2026-10-11T05:29:06.727130Z
Constructor Arguments
0000000000000000000000008feb03a1590c041a5ae7d09ff326919129b6f785000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f01058000000000000000000000000b7d7d4cc36ff190d2dca2cd618a71f3574688dd0000000000000000000000000e1dedf16b298e3532fb551c21710e23d3b0a28b7000000000000000000000000dae9dd3d1a52cfce9d5f2fac7fde164d500e50f70000000000000000000000005c2598630691e3c51bc17d94119ea990603f4561
Arg [0] (address) : 0x8feb03a1590c041a5ae7d09ff326919129b6f785
Arg [1] (address) : 0xdd99adf15d84d425f02983e730b0db8e27f01058
Arg [2] (address) : 0xb7d7d4cc36ff190d2dca2cd618a71f3574688dd0
Arg [3] (address) : 0xe1dedf16b298e3532fb551c21710e23d3b0a28b7
Arg [4] (address) : 0xdae9dd3d1a52cfce9d5f2fac7fde164d500e50f7
Arg [5] (address) : 0x5c2598630691e3c51bc17d94119ea990603f4561
contracts/router/BuybackRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {IPulseXRouter, IPulseXFactory, IPulseXPair, ITreasury, ITSI, IEpoch, IBurnable} from "../interfaces/IProtocol.sol";
import {TwapOracle} from "../oracle/TwapOracle.sol";
/// @title BuybackRouter
/// @notice Receives PBURN fee inflows and allocates each chunk across four actions:
/// burn PBURN, buy-and-burn pFLY, add protocol-owned liquidity, and fund the
/// treasury.
/// @dev THE CORE INVARIANT: a single `execute` can never spend more than the chunk it
/// committed to at the start of the call. `chunk` is snapshotted, the four legs
/// are computed from it with the remainder folded into the last leg, and the sum
/// of the legs is asserted to equal the chunk exactly before anything moves.
/// There is no path that reads `balanceOf(this)` again mid-execution, so the
/// router cannot spend fees that arrive during the call.
contract BuybackRouter is AccessControl, ReentrancyGuard {
using SafeERC20 for IERC20;
bytes32 public constant PARAMETER_ROLE = keccak256("PARAMETER_ROLE");
bytes32 public constant KEEPER_ROLE = keccak256("KEEPER_ROLE");
uint16 public constant BPS_DENOMINATOR = 10_000;
address public constant DEAD = 0x000000000000000000000000000000000000dEaD;
struct Allocation {
uint16 pburnBurnBps;
uint16 pflyBurnBps;
/// @dev Never set directly. Carved out of pflyBurnBps at execution time, and
/// only when the pCOCK pool can actually absorb the trade.
uint16 pcockBurnBps;
uint16 polBps;
uint16 treasuryBps;
}
struct Split {
uint256 pburnBurn;
uint256 pflyBurn;
uint256 pcockBurn;
uint256 pol;
uint256 treasury;
}
IERC20 public immutable pburn;
address public immutable pfly;
address public immutable treasury;
IPulseXRouter public immutable dexRouter;
TwapOracle public immutable oracle;
ITSI public tsi;
IEpoch public epoch;
/// @notice Second buyback target. Settable rather than immutable so the leg can ship
/// dormant and be pointed at pCOCK once that token and its pool exist.
address public pcock;
/// @notice Fallback split used when no Epoch is wired. Whitepaper default 40/40/20.
Allocation public defaultAllocation =
Allocation({pburnBurnBps: 4_000, pflyBurnBps: 4_000, pcockBurnBps: 0, polBps: 1_000, treasuryBps: 1_000});
/// @notice Minimum PBURN balance before `execute` will do anything.
uint256 public minExecutionAmount = 1_000e18;
/// @notice Maximum PBURN processed in a single execution, to bound price impact.
uint256 public maxExecutionAmount = 100_000e18;
/// @notice Slippage tolerance on router swaps.
uint16 public maxSlippageBps = 500; // 5%
/// @notice How far below the TWAP-implied value a fill may land before the trade is
/// refused. Covers the 0.29% pool fee plus the trade's own price impact at the
/// capped size, with headroom.
///
/// @dev THIS IS THE SAFETY PROPERTY. `maxSlippageBps` is not: it is applied to
/// `getAmountsOut`, which reads the reserves of the very pool being traded, so
/// after a front-run it is measured against the attacker's own manipulation and
/// binds on nothing. The floor below is measured against a time-average the
/// attacker cannot move within a block.
uint16 public twapFloorBps = 200; // 2%
/// @notice Loosest tolerance governance may set. Ceiling, not a default.
///
/// @dev The acceptance criterion for the whole fix is that loosening the floor breaks a
/// test. Without a ceiling that criterion held only against a code change:
/// PARAMETER_ROLE could have widened `twapFloorBps` until it bound on nothing and
/// the protection would be gone with no diff to review. 1000 is 5x the default,
/// which is room to react to a genuinely volatile market and not much more.
///
/// Raising the floor invalidates the depth table in `docs/PCOCK_ACTIVATION.md`
/// proportionally - that table is computed at 200 bps, and the attacker's maximum
/// gain scales linearly with this number.
uint16 public constant MAX_TWAP_FLOOR_BPS = 1_000;
/// @notice Largest swap cap governance may set, as a fraction of the PBURN reserve.
uint16 public constant MAX_SWAP_CAP_BPS = 500;
/// @notice The PulseX fee, in bps. 0.29%, not Uniswap's 0.30%.
uint16 public constant POOL_FEE_BPS = 29;
/// @dev Gas each swap is given, exactly - see `_trySwap`. A PulseX V2 swap costs ~120k; pFLY's
/// own transfer path can add a fee swap-back of its own. 1M leaves several times that.
uint256 public constant SWAP_GAS = 1_000_000;
/// @dev Covers the opcodes between the gas check and the call, chiefly ABI-encoding the path.
uint256 private constant SWAP_GAS_HEADROOM = 20_000;
/// @notice Governance's ceiling on the router's flow into the pFLY pool in one run, as a
/// fraction of that pool's PBURN side at the TWAP price.
///
/// @dev An impact bound, so `twapFloorBps` can be tight. It is no longer the binding one:
/// `maxFlowInto` caps the flow at half a sandwich's break-even - 0.295% of the pool
/// at today's fees - and governance cannot raise that. The remainder is not redistributed - the
/// chunk itself shrinks, so unspent fees stay in the router and are picked up by the
/// next `execute()`. A backlog from a period of refusals therefore still drains.
uint16 public maxSwapBps = 100; // 1% of the pool's PBURN side
/// @notice TSI below this biases allocation toward treasury and POL over burning.
uint256 public tsiStressThreshold = 4_000;
/// @notice Bps of the pFLY leg redirected to PBURN burn while TSI is healthy and
/// pFLY is expensive relative to its TWAP.
uint16 public pflySpikeRedirectBps = 5_000;
/// @notice Share of the pFLY buyback leg redirected to pCOCK, when the pCOCK pool can
/// take it. Default 25%, so an epoch preset of 40/40/10/10 executes as
/// 40/30/10/10/10 - the pFLY-primary shape.
/// @dev Carved from pFLY rather than added as a fifth top-level slot on purpose.
/// `IEpoch.allocation()` returns four values and Epoch is already deployed;
/// widening that interface would drag Epoch into a redeploy it does not
/// otherwise need. Carving also makes the economics explicit: pCOCK competes
/// with pFLY for the buyback budget, it does not dilute PBURN burn, POL, or
/// treasury.
uint16 public pcockShareBps = 2_500;
/// @notice Largest pCOCK trade, as bps of the pool's PBURN-side reserve.
/// @dev Constant-product loss is monotonic in amountIn/reserveIn, so bounding the
/// ratio bounds price impact directly without needing a quote. At the default
/// 200 bps the leg costs roughly 2.2% against a 0.29% PulseX fee; beyond that
/// the pool is too thin to be worth routing through and the leg stands down.
uint16 public maxPcockTradeBps = 200;
uint256 public totalPcockBurned;
uint256 public totalPburnBurned;
uint256 public totalPflyBurned;
uint256 public totalPolAdded;
uint256 public totalTreasuryFunded;
/// @param tsiSignalAvailable False when no TSI module is wired.
/// @param spikeSignalAvailable False when the pFLY spike check could not read the
/// oracle. Watch this: a long run of false means a protective mechanism is
/// off, not that it is finding nothing.
/// @param pcockLegAvailable False when the pCOCK leg stood down - no token wired, no
/// pool, an empty pool, or a pool too thin for this chunk. Same warning
/// applies: a long run of false is a leg that is off, not a leg finding
/// nothing to do.
/// @param pflyPoolAvailable False when the PBURN/pFLY pool is missing or empty, which
/// stands down BOTH the pFLY buyback leg and the POL leg - POL pairs against
/// pFLY, so one drained pool takes out both. Before the pool guard existed this
/// state reverted `execute()` outright instead of being reported.
event Executed(
uint256 chunk,
Split split,
uint256 tsiScore,
bool tsiSignalAvailable,
bool spikeSignalAvailable,
bool pcockLegAvailable,
bool pflyPoolAvailable,
address indexed caller
);
event PburnBurned(uint256 amount, uint256 newTotalSupply);
event PflyBurned(uint256 pburnSpent, uint256 pflyReceived);
event PcockBurned(uint256 pburnSpent, uint256 pcockReceived);
event PolAdded(uint256 pburnSpent, uint256 pairedSpent, uint256 liquidity);
event TreasuryFunded(address indexed asset, uint256 amount);
event AllocationUpdated(Allocation allocation);
event ExecutionBoundsUpdated(uint256 minAmount, uint256 maxAmount, uint16 maxSlippageBps);
event ModulesUpdated(address tsi, address epoch);
event SignalParamsUpdated(uint256 tsiStressThreshold, uint16 pflySpikeRedirectBps);
event PcockTargetUpdated(address indexed previousToken, address indexed newToken);
event PcockParamsUpdated(uint16 pcockShareBps, uint16 maxPcockTradeBps);
event TwapGuardsUpdated(uint16 twapFloorBps, uint16 maxSwapBps);
/// @notice A trading leg's swap could not fill at or above its floor, so the leg did not
/// trade. Its PBURN stays in the router and the next `execute()` picks it up.
/// @dev `minOut` is the floor the fill had to clear. The usual cause on chain 369 is a
/// transfer tax on the OUTPUT token - see `_trySwap`.
event LegStoodDown(address indexed token, uint256 pburnAmount, uint256 minOut);
error ZeroAddress();
error BelowMinimum(uint256 balance, uint256 minimum);
error AllocationMustTotalDenominator(uint256 provided);
error InvalidBounds();
error SplitDoesNotMatchChunk(uint256 sum, uint256 chunk);
error SwapProducedNothing();
/// @dev No usable time-average for this pair, so no trade into it can be bounded.
error TwapUnavailable(address token);
/// @dev Too little gas left to give a swap its full `SWAP_GAS`. Reverted rather than
/// attempted short - see `_trySwap`.
error InsufficientGasForLeg();
constructor(address admin, address pburn_, address pfly_, address treasury_, address dexRouter_, address oracle_) {
if (
admin == address(0) || pburn_ == address(0) || pfly_ == address(0) || treasury_ == address(0)
|| dexRouter_ == address(0) || oracle_ == address(0)
) revert ZeroAddress();
_grantRole(DEFAULT_ADMIN_ROLE, admin);
pburn = IERC20(pburn_);
pfly = pfly_;
treasury = treasury_;
dexRouter = IPulseXRouter(dexRouter_);
oracle = TwapOracle(oracle_);
}
// --- Execution ----------------------------------------------------------
/// @notice Process accrued fees. Permissioned to KEEPER_ROLE so that execution
/// timing cannot be used to sandwich the protocol's own swaps.
function execute() external onlyRole(KEEPER_ROLE) nonReentrant returns (Split memory split) {
// Snapshot the chunk once. Nothing below re-reads the balance, so fees that
// land mid-execution are simply left for the next run.
uint256 balance = pburn.balanceOf(address(this));
if (balance < minExecutionAmount) revert BelowMinimum(balance, minExecutionAmount);
(uint256 chunk, Allocation memory alloc, uint256 tsiScore, bool hasSignal) = _plan(balance);
split = _splitChunk(chunk, alloc);
// Hard invariant: the five legs must account for exactly the chunk, no more.
uint256 sum = split.pburnBurn + split.pflyBurn + split.pcockBurn + split.pol + split.treasury;
if (sum != chunk) revert SplitDoesNotMatchChunk(sum, chunk);
// From here `split` is what EXECUTED, not what was planned. A leg that stood down
// reports zero, so the return value and the event cannot claim a buy that did not
// happen; `chunk` minus the sum is what stayed in the router for the next run.
split = _runLegs(split);
(, bool spikeAvailable) = _pflyAboveTwap();
emit Executed(
chunk,
split,
tsiScore,
hasSignal,
spikeAvailable,
alloc.pcockBurnBps > 0,
_pflyPoolUsable(),
msg.sender
);
}
/// @dev The whole planning step, shared by `execute()` and `previewSplit()`.
///
/// They were separate once. `execute()` learned to cap before allocating (below) and
/// the preview did not, so it went on evaluating the allocation at the UNCAPPED chunk
/// and forecasting pFLY legs standing down that would in fact trade. Its only test
/// funded 10,000 PBURN against a mock with no reserves, where the cap never binds, so
/// the two could not be told apart there. One function cannot drift from itself.
function _plan(uint256 balance)
private
view
returns (uint256 chunk, Allocation memory alloc, uint256 tsiScore, bool hasSignal)
{
chunk = balance > maxExecutionAmount ? maxExecutionAmount : balance;
(tsiScore, hasSignal) = _readTsi();
// ORDER MATTERS, and getting it wrong stands every leg down.
//
// The cap must be applied BEFORE the allocation is evaluated, because the
// allocation's guards ask "would this leg fill above the floor?" and the answer
// depends on the trade's size. Evaluating at the uncapped chunk asks about a trade
// that will never be placed: a 35,000 PBURN swap has ~3.7% impact against this
// pool and fails a 2% floor, so the honest path stood itself down while the capped
// trade it would actually make - around 9,500 - has well under 1%.
//
// Whatever the cap holds back is NOT redistributed. It stays in the router's
// balance and the next execute() picks it up, which is what lets a backlog drain
// across runs instead of forcing one oversized trade.
uint256 capped = _capChunkForSwaps(chunk, _baseAllocation());
bool swapsFitUnderTheCap = capped >= minExecutionAmount;
if (swapsFitUnderTheCap) {
chunk = capped;
}
// ELSE: the pool is too shallow to take even a minimum-sized run within the cap.
//
// Reverting here would be wrong, and an earlier revision did: `BelowMinimum` fired
// and NOTHING executed, including the PBURN burn and treasury legs, which need no
// price and no counterparty. A thin pool would have bricked fee processing
// entirely, and the thinner the pool the more certainly it would stay bricked -
// the balance grows, the cap does not.
//
// So the chunk stays whole and the trading legs stand down instead. The run still
// happens; it just does not trade.
alloc = _effectiveAllocation(chunk, tsiScore, hasSignal);
if (!swapsFitUnderTheCap) {
alloc = _standDownTradingLegs(alloc);
}
}
/// @dev Returns what executed. The PBURN burn and treasury legs need no counterparty and
/// always run; each trading leg either fills at or above its floor or stands down.
function _runLegs(Split memory planned) private returns (Split memory done) {
if (planned.pburnBurn > 0) {
_burnPburn(planned.pburnBurn);
done.pburnBurn = planned.pburnBurn;
}
if (planned.pflyBurn > 0) {
(bool filled, uint256 received) = _buyAndBurn(pfly, planned.pflyBurn);
if (filled) {
done.pflyBurn = planned.pflyBurn;
totalPflyBurned += received;
emit PflyBurned(planned.pflyBurn, received);
}
}
if (planned.pcockBurn > 0) {
(bool filled, uint256 received) = _buyAndBurn(pcock, planned.pcockBurn);
if (filled) {
done.pcockBurn = planned.pcockBurn;
totalPcockBurned += received;
emit PcockBurned(planned.pcockBurn, received);
}
}
if (planned.pol > 0 && _addPol(planned.pol)) done.pol = planned.pol;
if (planned.treasury > 0) {
_fundTreasury(planned.treasury);
done.treasury = planned.treasury;
}
}
/// @dev Splits `chunk` by bps, folding all integer-division dust into the treasury
/// leg so the five parts always re-sum to exactly `chunk`.
///
/// `SplitDoesNotMatchChunk` remains unreachable by construction, and the third
/// leg does not change that. Every branch in `_effectiveAllocation` conserves
/// the bps total: the stress branch moves pFLY bps into POL and treasury, the
/// spike branch moves pFLY bps into PBURN burn, and the pCOCK carve moves pFLY
/// bps into pCOCK. `setDefaultAllocation` rejects any total other than 10000, so
/// the first four legs can never exceed the chunk and treasury absorbs exactly
/// the integer-division dust. If a future branch ever broke that conservation,
/// the subtraction below would panic on underflow before the assertion could
/// catch it - so the assertion is a backstop for a wrong-but-non-underflowing
/// total, and `testFuzzSplitAlwaysSumsToChunk` pins the property directly.
function _splitChunk(uint256 chunk, Allocation memory alloc) private pure returns (Split memory split) {
split.pburnBurn = (chunk * alloc.pburnBurnBps) / BPS_DENOMINATOR;
split.pflyBurn = (chunk * alloc.pflyBurnBps) / BPS_DENOMINATOR;
split.pcockBurn = (chunk * alloc.pcockBurnBps) / BPS_DENOMINATOR;
split.pol = (chunk * alloc.polBps) / BPS_DENOMINATOR;
split.treasury = chunk - split.pburnBurn - split.pflyBurn - split.pcockBurn - split.pol;
}
/// @dev Blends the epoch preset with live TSI and pFLY price signals, then carves the
/// pCOCK leg out of whatever pFLY budget survives.
function _effectiveAllocation(uint256 chunk, uint256 tsiScore, bool hasSignal)
private
view
returns (Allocation memory alloc)
{
alloc = _baseAllocation();
// No TSI module wired: use the base allocation untouched rather than
// pretending to have a health reading either way.
if (!hasSignal) return _applyPflyPoolGuard(chunk, _applyPcockCarve(chunk, alloc));
if (tsiScore < tsiStressThreshold) {
// Treasury under stress: halve the pFLY burn leg and move it into
// treasury and POL, which rebuild strength instead of spending it.
uint16 moved = alloc.pflyBurnBps / 2;
alloc.pflyBurnBps -= moved;
uint16 toPol = moved / 2;
alloc.polBps += toPol;
alloc.treasuryBps += (moved - toPol);
} else if (_spikeRedirectApplies()) {
// pFLY spiking above its own TWAP: buying it now is expensive, so
// redirect part of that leg into burning PBURN instead.
uint16 moved = uint16((uint256(alloc.pflyBurnBps) * pflySpikeRedirectBps) / BPS_DENOMINATOR);
alloc.pflyBurnBps -= moved;
alloc.pburnBurnBps += moved;
}
alloc = _applyPcockCarve(chunk, alloc);
return _applyPflyPoolGuard(chunk, alloc);
}
/// @dev Stands down the two legs that depend on the PBURN/pFLY pool when that pool is
/// missing or empty, rather than letting `getAmountsOut` revert the whole
/// execution.
///
/// This was the live bug, and it was not pCOCK-specific: the pFLY leg has always
/// called `_minOut` unguarded, and so has the POL leg, which pairs PBURN against
/// pFLY. A drained reference pool therefore bricked `execute()` entirely - PBURN
/// burn and treasury funding included, neither of which needs the pool at all.
/// Adding the pCOCK guard alone would have left that untouched.
///
/// Where the freed budget goes is chosen to keep doing the nearest useful thing:
/// the pFLY buyback becomes a PBURN burn (still deflationary, needs no pool), and
/// POL becomes treasury funding (still balance-sheet, needs no pool).
function _applyPflyPoolGuard(uint256 chunk, Allocation memory alloc)
private
view
returns (Allocation memory)
{
uint256 pflyLeg = (chunk * alloc.pflyBurnBps) / BPS_DENOMINATOR;
uint256 polSwap = ((chunk * alloc.polBps) / BPS_DENOMINATOR) / 2;
bool poolOk = _pflyPoolUsable();
// CHECK THE COMBINED SIZE, NOT EACH LEG SEPARATELY.
//
// Both legs sell PBURN into the same pool and they execute in sequence, so the
// first one moves the price the second one gets. Checking them independently
// against the current price passes two trades that each clear the floor alone and
// then reverts mid-execution when the second faces the first one's impact -
// `INSUFFICIENT_OUTPUT_AMOUNT` from inside `_buyAndBurn`, after the burn leg has
// already run.
//
// The combined amount is the conservative test: impact is superlinear in size, so
// if the whole quantity clears the floor in one hypothetical trade, each sequential
// piece of it clears with room to spare.
uint256 totalSwap = pflyLeg + polSwap;
bool boundOk = totalSwap == 0 || twapBounded(pfly, totalSwap);
if (poolOk && boundOk) return alloc;
return _standDownTradingLegs(alloc);
}
/// @notice Fold every leg that needs a swap into one that does not.
function _standDownTradingLegs(Allocation memory alloc)
private
pure
returns (Allocation memory)
{
// WHERE THE UNTRADED ALLOCATION GOES, and why:
// pFLY burn -> PBURN burn. Burning PBURN needs no swap, so it cannot be
// sandwiched. The deflationary intent is preserved.
// POL -> treasury. Adding liquidity requires a swap first; holding the
// PBURN in the treasury needs none.
// pCOCK -> PBURN burn. Same reasoning as pFLY.
//
// Neither destination trades, so an unbounded market cannot cost the protocol
// anything on this execution. The fees are not skipped - they are redirected to
// the legs that are safe when no price can be trusted.
alloc.pburnBurnBps += alloc.pflyBurnBps + alloc.pcockBurnBps;
alloc.pflyBurnBps = 0;
alloc.pcockBurnBps = 0;
alloc.treasuryBps += alloc.polBps;
alloc.polBps = 0;
return alloc;
}
/// @notice Largest chunk whose swaps into the pFLY pool stay within `maxFlowInto(pfly)`.
///
/// @dev Shrinks the chunk rather than redistributing the excess, so whatever is not
/// traded stays in the router and the next `execute()` picks it up. That is what
/// makes a backlog drain: each run takes a bounded bite, and the bites continue
/// until the balance is gone.
///
/// The pFLY pool takes two swaps in one run - the buyback and half of POL - and a
/// sandwich wraps the whole run, so it is their SUM that is capped, counted before the
/// pCOCK carve moves part of the buyback elsewhere so the cap holds whatever the carve
/// does. Checking each swap alone would pass two that sum past the break-even.
///
/// With no time-average for the pool, nothing trades into it this run - its legs stand
/// down at their floors - so there is nothing to sandwich, and the cap only sizes the
/// bite. The spot reserve does for that: it keeps an outage from routing a whole
/// 100,000 chunk to burn and treasury in one run.
function _capChunkForSwaps(uint256 chunk, Allocation memory alloc)
private
view
returns (uint256)
{
// The two legs that swap into PBURN/pFLY: the buyback, and half of POL.
uint256 swapBps = uint256(alloc.pflyBurnBps) + uint256(alloc.polBps) / 2;
if (swapBps == 0) return chunk;
uint256 depth = poolDepthAtTwap(pfly);
if (depth == 0) (, depth) = _poolFor(pfly);
if (depth == 0) return chunk;
// wouldSwap = chunk * swapBps / BPS must not exceed depth * halfBps / (2 * BPS), so the
// chunk is at most depth * halfBps / (2 * swapBps) - one division, rounding down.
uint256 capped = Math.mulDiv(depth, _flowHalfBps(pfly), 2 * swapBps);
return capped < chunk ? capped : chunk;
}
/// @dev Moves `pcockShareBps` of the remaining pFLY leg into the pCOCK leg, but only
/// if the pCOCK pool can actually absorb that trade.
///
/// The fallback is the point of the whole design: when the leg is unavailable
/// NOTHING is carved, so the allocation is byte-for-byte what this router would
/// have produced without a pCOCK leg at all. That makes the feature safe to ship
/// dormant - before the token exists, before the pool is funded - because the
/// inert behaviour is provably the previous behaviour rather than a new untested
/// path. `testUnavailablePcockMatchesTwoTokenRouterExactly` pins that.
///
/// Carving last, after the stress and spike branches, means pCOCK takes its
/// share of whatever pFLY budget survives those adjustments rather than
/// competing with them.
function _applyPcockCarve(uint256 chunk, Allocation memory alloc)
private
view
returns (Allocation memory)
{
uint16 moved = uint16((uint256(alloc.pflyBurnBps) * pcockShareBps) / BPS_DENOMINATOR);
if (moved == 0 || chunk == 0) return alloc;
// Never more into the pCOCK pool than half ITS sandwich break-even. The chunk was sized
// by the pFLY pool, which says nothing about this one - that is how the leg used to
// outgrow a pool it traded in. A thin pool gets a smaller carve, not a smaller chunk:
// what it cannot take stays in the pFLY leg, which the chunk cap already sized for it.
uint256 fits = (maxFlowInto(pcock) * BPS_DENOMINATOR) / chunk;
if (fits < moved) moved = uint16(fits);
if (moved == 0) return alloc;
uint256 pcockLeg = (chunk * moved) / BPS_DENOMINATOR;
if (!_pcockLegAvailable(pcockLeg)) return alloc;
// AND it must be priceable. `_pcockLegAvailable` checks only that the pool exists
// and is deep enough for the size; it says nothing about whether a time-average
// exists to bound the fill against.
//
// Without this the carve went ahead and `_buyAndBurn` hit `_minOut`, which reverts
// `TwapUnavailable` - taking the entire execution down, including the burn and
// treasury legs that need no price. A third trading path reaching the floor with
// no pre-check, where pFLY and POL both had one.
if (!twapBounded(pcock, pcockLeg)) return alloc;
alloc.pflyBurnBps -= moved;
alloc.pcockBurnBps = moved;
return alloc;
}
/// @dev Resolves the PBURN pool for `token`, returning address(0) unless it exists
/// AND holds liquidity on both sides.
/// @return pair The pool, or address(0) if unusable.
/// @return pburnReserve The PBURN-side reserve, 0 when unusable.
function _poolFor(address token) private view returns (address pair, uint256 pburnReserve) {
if (token == address(0)) return (address(0), 0);
pair = IPulseXFactory(dexRouter.factory()).getPair(address(pburn), token);
if (pair == address(0)) return (address(0), 0);
(uint112 reserve0, uint112 reserve1,) = IPulseXPair(pair).getReserves();
if (reserve0 == 0 || reserve1 == 0) return (address(0), 0);
pburnReserve = IPulseXPair(pair).token0() == address(pburn) ? reserve0 : reserve1;
}
/// @dev Can `amountIn` PBURN be routed through `token`'s pool right now?
///
/// Every failure mode returns false rather than reverting, and that matters more
/// than it looks. `_minOut` calls `getAmountsOut`, which resolves the pair by
/// CREATE2 and calls `getReserves()` on it. Against a codeless address that
/// reverts on the extcodesize check BEFORE the call, which `try/catch` cannot
/// intercept - so an unguarded leg reverts every `execute()` while its pool does
/// not exist, taking every OTHER leg down with it. Screening here is what keeps a
/// missing pool a dormant leg instead of a bricked router.
///
/// @param maxTradeBps Ceiling on amountIn as a share of the pool's PBURN reserve.
/// Pass 0 to check existence and liquidity only, with no size bound.
function _legTradable(address token, uint256 amountIn, uint16 maxTradeBps)
private
view
returns (bool)
{
if (amountIn == 0) return false;
(address pair, uint256 pburnReserve) = _poolFor(token);
if (pair == address(0)) return false;
if (maxTradeBps == 0) return true;
// Constant-product loss is monotonic in amountIn/reserveIn, so bounding that
// ratio bounds price impact without needing a quote.
return amountIn * BPS_DENOMINATOR <= pburnReserve * maxTradeBps;
}
function _pcockLegAvailable(uint256 amountIn) private view returns (bool) {
return _legTradable(pcock, amountIn, maxPcockTradeBps);
}
/// @dev The pFLY pool backs BOTH the pFLY buyback leg and the POL leg, since POL
/// pairs PBURN against pFLY. One drained pool takes out both.
///
/// No size ceiling here, deliberately. The bug being fixed is bricking, not
/// slippage, and pFLY is the reference pair - imposing a depth cap on the
/// primary leg would change live economics to fix a liveness bug. If a pFLY
/// depth ceiling is ever wanted it should be its own decision, not a side
/// effect of this one.
function _pflyPoolUsable() private view returns (bool) {
(address pair,) = _poolFor(pfly);
return pair != address(0);
}
/// @notice Whether the pCOCK pool exists and holds liquidity, independent of size.
/// @dev Separates "no pool" from "pool too thin for this chunk" for monitoring.
function pcockPoolReady() external view returns (bool) {
(address pair,) = _poolFor(pcock);
return pair != address(0);
}
/// @notice Whether the pCOCK leg would fire for a trade of `amountIn` PBURN.
function pcockLegAvailable(uint256 amountIn) external view returns (bool) {
return _pcockLegAvailable(amountIn);
}
// --- The sandwich bound ---------------------------------------------------------------------
//
// A sandwich wraps a whole execute(): the bot sells PBURN into a pool, the router's swaps
// land at the worse price, the bot sells back. To first order it gains d x flow and pays
// d x cost x depth, where d is how far it moved the price, `flow` is the PBURN this run
// sends into that pool, `depth` is the pool's PBURN side and `cost` is what one of its
// swaps costs it - PulseX's fee plus PBURN's own. The sign does not depend on d, the floor
// or the TWAP: a sandwich pays exactly when flow > cost x depth. So every pool's flow is
// capped at HALF of that, and no sandwich pays - whatever the pools hold relative to each
// other, and whether or not pFLY taxes its pairs. Measured on real pools in
// test/fork/MainnetLaunchRehearsal.t.sol, section 5. Governance cannot raise this bound:
// `maxSwapBps` can only tighten it.
/// @notice What one PBURN-side swap through `token`'s pool costs a sandwich bot, in bps:
/// PulseX's fee, plus PBURN's own transfer fee unless the token exempts that pair.
/// Read live, so a governance fee cut or a pair exemption tightens the cap with it.
function sandwichCostBps(address token) public view returns (uint256) {
(address pair,) = _poolFor(token);
return uint256(POOL_FEE_BPS) + _pburnFeeOnPair(pair);
}
/// @notice `token`'s pool's PBURN side at the TWAP price, `sqrt(k / P_twap)`; 0 when there
/// is no pool or no usable time-average.
/// @dev What a front-run cannot inflate. Selling PBURN in raises the PBURN reserve one for
/// one, for free; it raises `k` only by the fee it pays into the pool - real depth,
/// bought at 0.29% - and it cannot move the TWAP inside its own block.
function poolDepthAtTwap(address token) public view returns (uint256) {
(address pair,) = _poolFor(token);
if (pair == address(0)) return 0;
try oracle.consult(pair, address(pburn), 1e18) returns (uint256 otherPerPburn) {
if (otherPerPburn == 0) return 0;
(uint112 reserve0, uint112 reserve1,) = IPulseXPair(pair).getReserves();
return Math.sqrt(Math.mulDiv(uint256(reserve0) * uint256(reserve1), 1e18, otherPerPburn));
} catch {
return 0;
}
}
/// @notice Most PBURN one run may send into `token`'s pool: half a sandwich's break-even
/// (`sandwichCostBps / 2` of `poolDepthAtTwap`), or `maxSwapBps` if governance set
/// that lower. 0 when the depth cannot be measured - and then nothing trades there.
function maxFlowInto(address token) public view returns (uint256) {
return Math.mulDiv(poolDepthAtTwap(token), _flowHalfBps(token), 2 * uint256(BPS_DENOMINATOR));
}
/// @dev The flow cap in HALF-bps, to keep the half of an odd cost exact: the smaller of the
/// sandwich cost (half of it, doubled) and twice `maxSwapBps`. `maxSwapBps` = 0 leaves
/// only the sandwich bound, which governance cannot switch off.
function _flowHalfBps(address token) private view returns (uint256) {
uint256 cost = sandwichCostBps(token);
uint256 impact = 2 * uint256(maxSwapBps);
return maxSwapBps == 0 || cost < impact ? cost : impact;
}
/// @dev PBURN's fee on a transfer to or from `pair`: 0 if the token exempts the pair, since
/// a bot trading through it then pays only PulseX. The testnet token names the getter
/// `isFeeExempt` and the launch token `isExempt`, so both are asked. Read by raw
/// staticcall, because a typed call to a getter a token lacks reverts in the caller.
function _pburnFeeOnPair(address pair) private view returns (uint256) {
address token = address(pburn);
if (
pair != address(0)
&& (
_readWord(token, abi.encodeWithSignature("isExempt(address)", pair)) != 0
|| _readWord(token, abi.encodeWithSignature("isFeeExempt(address)", pair)) != 0
)
) return 0;
return _readWord(token, abi.encodeWithSignature("feeBps()"));
}
/// @dev One 32-byte word from a view call, or 0 when the call fails or returns anything else.
function _readWord(address target, bytes memory data) private view returns (uint256) {
(bool ok, bytes memory ret) = target.staticcall(data);
return ok && ret.length == 32 ? abi.decode(ret, (uint256)) : 0;
}
/// @notice Whether the PBURN/pFLY pool can currently back the pFLY and POL legs.
/// @dev False means those legs stand down and their budget folds into PBURN burn and
/// treasury respectively. Before this guard existed, false meant `execute()`
/// reverted outright and the whole fee engine stopped.
function pflyPoolAvailable() external view returns (bool) {
return _pflyPoolUsable();
}
/// @dev Only redirect on a reading we actually have.
///
/// THE COMMENT HERE USED TO SAY an unavailable signal leaves the base allocation
/// in place, "which is neutral rather than permissive". That was wrong, and it is
/// the sentence that let audit finding A-2 sit unexamined.
///
/// Leaving the base allocation meant the router bought pFLY anyway, at a price
/// nothing had vouched for. For the spike protection specifically that is
/// fail-OPEN: the protection exists to avoid buying during a spike, and the
/// degraded path bought during spikes it could not see.
///
/// What IS fail-closed is the SIGNAL - `_pflyAboveTwap` returns
/// `(false, false)` rather than claiming "no spike", so monitoring can tell an
/// unavailable check from a quiet one. An honest signal and a safe action are
/// different properties, and conflating them is how a router with no working
/// price bound was described as failing closed for months.
///
/// The action is now safe too: `_applyPflyPoolGuard` stands the trading legs down
/// before this is reached, so an unavailable reading means no trade rather than an
/// unprotected one.
function _spikeRedirectApplies() private view returns (bool) {
(bool spiking, bool available) = _pflyAboveTwap();
return available && spiking;
}
function _baseAllocation() private view returns (Allocation memory) {
IEpoch epoch_ = epoch;
// A codeless address would revert on the extcodesize check BEFORE the call,
// which try/catch cannot intercept, so it is screened out here.
if (address(epoch_) == address(0) || address(epoch_).code.length == 0) return defaultAllocation;
(uint16 pburnBurnBps, uint16 pflyBurnBps, uint16 polBps, uint16 treasuryBps) = epoch_.allocation();
// pcockBurnBps is deliberately 0 here. `IEpoch.allocation()` returns four values
// and Epoch is already deployed; the pCOCK leg is carved out of pflyBurnBps later
// rather than widening that interface, so an epoch preset never has to know the
// second buyback target exists.
return Allocation({
pburnBurnBps: pburnBurnBps,
pflyBurnBps: pflyBurnBps,
pcockBurnBps: 0,
polBps: polBps,
treasuryBps: treasuryBps
});
}
/// @dev Reads the TSI health signal.
/// @return score Composite score, meaningful only when `hasSignal` is true.
/// @return hasSignal False when no TSI module is wired at all, in which case the
/// base allocation is used unmodified.
///
/// A broken or unconfigured TSI is deliberately reported as a score of 0 with
/// hasSignal true, which drives the conservative stress branch. An earlier
/// revision returned full health here, so an unreadable signal made the protocol
/// behave as though the treasury were perfectly healthy - fail-open on exactly
/// the input that exists to detect trouble.
///
/// The code-length screen matters because Solidity performs the extcodesize check
/// BEFORE the external call, and that revert is NOT catchable by try/catch.
function _readTsi() private view returns (uint256, bool) {
ITSI tsi_ = tsi;
if (address(tsi_) == address(0)) return (0, false);
if (address(tsi_).code.length == 0) return (0, true);
try tsi_.score() returns (uint256 value) {
return (value, true);
} catch {
return (0, true);
}
}
/// @dev Is pFLY spiking above its own TWAP?
/// @return spiking Meaningful only when `available` is true.
/// @return available False when no pair is wired, or when the oracle cannot
/// currently answer (no observation yet, or the averaging window has not
/// reached minPeriod).
///
/// An earlier revision returned a bare `false` for all three of those states, so
/// the router could not distinguish "no spike" from "could not check". That made
/// the spike check a silent no-op whenever the oracle was unavailable - the same
/// shape as the fail-open TSI, one layer down. The allocation behaviour for an
/// unavailable signal is no longer "unchanged (base allocation)" - the trading legs
/// stand down - and it is reported
/// rather than indistinguishable from a real reading.
function _pflyAboveTwap() private view returns (bool spiking, bool available) {
address pair = IPulseXFactory(dexRouter.factory()).getPair(address(pburn), pfly);
if (pair == address(0)) return (false, false);
try oracle.isQuoteSafe(pair, address(pburn), 1e18) returns (bool safe) {
if (safe) return (false, true); // within band: genuinely no spike
// Outside the band. Distinguish a spike from a crash.
uint256 spot = oracle.spotQuote(pair, address(pburn), 1e18);
uint256 twap = oracle.consult(pair, address(pburn), 1e18);
// More pFLY per PBURN than TWAP implies pFLY is cheap, not spiking.
return (spot < twap, true);
} catch {
return (false, false);
}
}
/// @notice Whether the pFLY spike check can currently produce a reading.
/// @dev Exposed so monitoring can tell an active-and-quiet check apart from an
/// unavailable one. A protective mechanism that is silently off is the
/// failure mode this protocol has hit repeatedly.
function spikeCheckAvailable() external view returns (bool) {
(, bool available) = _pflyAboveTwap();
return available;
}
// --- Legs ---------------------------------------------------------------
function _burnPburn(uint256 amount) private {
IBurnable(address(pburn)).burn(amount);
totalPburnBurned += amount;
emit PburnBurned(amount, IBurnable(address(pburn)).totalSupply());
}
/// @dev Buys `token` with `amount` PBURN and burns what comes back. Shared by the
/// pFLY and pCOCK legs so the two cannot drift apart in approval hygiene or
/// slippage handling. Accounting and events stay with the caller.
function _buyAndBurn(address token, uint256 amount) private returns (bool filled, uint256 received) {
address[] memory path = new address[](2);
path[0] = address(pburn);
path[1] = token;
uint256 minOut = _minOut(path, amount);
uint256 balanceBefore = IERC20(token).balanceOf(address(this));
pburn.forceApprove(address(dexRouter), amount);
filled = _trySwap(token, amount, minOut, path);
// Clear any allowance the swap did not consume: the DEX router must never
// retain a standing claim on router funds between executions. On BOTH paths - a
// stood-down leg consumed nothing and would otherwise leave the whole amount approved.
pburn.forceApprove(address(dexRouter), 0);
if (!filled) return (false, 0);
received = IERC20(token).balanceOf(address(this)) - balanceBefore;
if (received == 0) revert SwapProducedNothing();
// These are external tokens: burn by sending to the dead address rather than
// assuming they expose a burn() entry point.
IERC20(token).safeTransfer(DEAD, received);
}
/// @dev Half the POL leg is swapped for pFLY, then both sides are paired and the
/// resulting LP is sent to the Treasury as protocol-owned liquidity.
function _addPol(uint256 amount) private returns (bool filled) {
uint256 half = amount / 2;
uint256 pburnSide = amount - half;
address[] memory path = new address[](2);
path[0] = address(pburn);
path[1] = pfly;
uint256 balanceBefore = IERC20(pfly).balanceOf(address(this));
pburn.forceApprove(address(dexRouter), half);
filled = _trySwap(pfly, half, _minOut(path, half), path);
if (!filled) {
// Nothing moved: the swap is the leg's first transfer, and a stood-down swap
// reverted in full. Clear the approval and leave the whole amount for next run.
pburn.forceApprove(address(dexRouter), 0);
return false;
}
uint256 pairedReceived = IERC20(pfly).balanceOf(address(this)) - balanceBefore;
if (pairedReceived == 0) revert SwapProducedNothing();
pburn.forceApprove(address(dexRouter), pburnSide);
IERC20(pfly).forceApprove(address(dexRouter), pairedReceived);
(,, uint256 liquidity) = dexRouter.addLiquidity(
address(pburn),
pfly,
pburnSide,
pairedReceived,
_applySlippage(pburnSide),
_applySlippage(pairedReceived),
treasury,
block.timestamp
);
// addLiquidity consumes at most the desired amounts and often less, so both
// allowances are cleared rather than left dangling as a standing claim.
pburn.forceApprove(address(dexRouter), 0);
IERC20(pfly).forceApprove(address(dexRouter), 0);
address pair = IPulseXFactory(dexRouter.factory()).getPair(address(pburn), pfly);
if (pair != address(0) && liquidity > 0) {
ITreasury(treasury).recordLpDeposit(pair, liquidity);
}
totalPolAdded += amount;
emit PolAdded(pburnSide, pairedReceived, liquidity);
}
/// @dev One swap, isolated so that its failure stands its own leg down instead of
/// reverting `execute()`.
///
/// WHY. pFLY on chain 369 taxes buys 3% and sells 5% through any pair its owner has
/// registered, the owner is an EOA, and registering a pair is one call we do not
/// control. Once PBURN/pFLY is registered, what reaches this router is 3% under what
/// the pool sends. The default floor leaves 71 bps between the pool's own output and
/// the minimum (200 floor - 100 cap - 29 fee), so the fill misses and PulseX reverts
/// `INSUFFICIENT_OUTPUT_AMOUNT`. Uncaught, that took the WHOLE run down with it -
/// including the PBURN burn and treasury legs, which need no counterparty at all.
/// Reproduced against mainnet state in test/fork/MainnetLaunchRehearsal.t.sol.
///
/// The floor still holds: a leg that cannot clear it does not trade. What changes is
/// only that one third party's configuration can no longer halt the legs it has
/// nothing to do with. This is the same rule `execute()` already applies to a pool
/// too thin for the cap - stand the trading legs down, run the rest.
///
/// WHY A FIXED GAS ALLOWANCE. Catching a failure makes the outcome depend on the gas
/// the caller sent, unless something stops it. `eth_estimateGas` searches for the least
/// gas at which a transaction does not revert; if a swap starved of gas were caught
/// and reported as a stand-down, a short-gas run would "succeed", the estimate would
/// settle there, and every keeper run sized by it would skip its trading legs.
///
/// The first version guarded this after the fact - "if at most 1/64 of the gas is
/// left, the callee ran out, so revert" (EIP-150). A test showed that is wrong. When
/// the out-of-gas happens two frames down (PulseX -> pair -> token), each intermediate
/// frame hands back the 1/64 it kept, the caller sees ~3/64, and the check waves it
/// through.
///
/// So the swap gets exactly `SWAP_GAS`, and the run reverts BEFORE the call if it cannot
/// supply that much. Whether a swap fits is then a property of the swap, never of the
/// keeper's gas limit: any run that gets past the check behaves the same as a run with
/// unlimited gas. A swap that genuinely needs more than `SWAP_GAS` stands down, every
/// time, visibly.
///
/// The revert reason is not captured. The output token is a third-party contract,
/// and copying arbitrary returndata is a gas-griefing surface; `minOut` in the event
/// is enough to re-simulate the miss.
function _trySwap(address token, uint256 amountIn, uint256 minOut, address[] memory path)
private
returns (bool)
{
// EIP-150 forwards at most 63/64 of what remains; make sure the full allowance can
// actually reach the callee, with headroom for the ABI encoding in between.
if (gasleft() < (SWAP_GAS * 64) / 63 + SWAP_GAS_HEADROOM) revert InsufficientGasForLeg();
try dexRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens{gas: SWAP_GAS}(
amountIn, minOut, path, address(this), block.timestamp
) {
return true;
} catch {
emit LegStoodDown(token, amountIn, minOut);
return false;
}
}
function _fundTreasury(uint256 amount) private {
pburn.safeTransfer(treasury, amount);
ITreasury(treasury).recordDeposit(address(pburn), amount);
totalTreasuryFunded += amount;
emit TreasuryFunded(address(pburn), amount);
}
/// @dev Minimum acceptable output: the GREATER of the spot-derived bound and the
/// TWAP-derived floor. **In practice the TWAP floor is the only one that can bind.**
///
/// An earlier comment here claimed spot was "the tighter of the two in calm
/// markets". That is wrong, and worth correcting rather than deleting: the spot
/// bound is `getAmountsOut(amountIn) * (1 - maxSlippageBps)`, quoted in the same
/// transaction, immediately before the swap that fills at exactly that quote. It
/// is a fraction of the number it is checking, so it is satisfied by construction.
/// `maxSlippageBps` has never constrained this router and a reader should not be
/// left thinking two protections are present when there is one.
///
/// It is retained for the one case where it is not vacuous: a transfer tax on the
/// input. PBURN taxes transfers and the router is currently fee-exempt, so less
/// would arrive at the pair than was quoted if that exemption were ever removed,
/// and the fill would come in under the quote. Cheap backstop, correctly labelled.
///
/// Under manipulation the spot bound collapses with the price - it is quoted from
/// the manipulated pool - while the TWAP floor does not move. The floor binds.
///
/// Reverts when no TWAP is available. Callers must not reach here for a token
/// whose leg has not been cleared by `_twapBounded`; the revert is a backstop
/// against a future caller that forgets, not the intended control flow.
function _minOut(address[] memory path, uint256 amountIn) private view returns (uint256) {
uint256[] memory amounts = dexRouter.getAmountsOut(amountIn, path);
uint256 spotBound = _applySlippage(amounts[amounts.length - 1]);
(uint256 floor, bool available) = _twapFloor(path[path.length - 1], amountIn);
if (!available) revert TwapUnavailable(path[path.length - 1]);
return floor > spotBound ? floor : spotBound;
}
/// @notice The TWAP-implied minimum for selling `amountIn` PBURN into the PBURN/`token`
/// pool, and whether a usable reading exists at all.
///
/// @dev Deliberately the same shape `BondingVault._lpValueInPburn` already uses:
/// `isQuoteSafe` to reject a spot that has diverged from the average, then
/// `consult` for the value itself. That pattern was correct in the vault and
/// absent from the router; this is not a second design, it is the first one
/// applied where it was missing.
function _twapFloor(address token, uint256 amountIn)
private
view
returns (uint256 floor, bool available)
{
(address pair,) = _poolFor(token);
if (pair == address(0)) return (0, false);
try oracle.isQuoteSafe(pair, address(pburn), amountIn) returns (bool safe) {
if (!safe) return (0, false);
} catch {
return (0, false);
}
try oracle.consult(pair, address(pburn), amountIn) returns (uint256 fair) {
if (fair == 0) return (0, false);
return ((fair * (BPS_DENOMINATOR - twapFloorBps)) / BPS_DENOMINATOR, true);
} catch {
return (0, false);
}
}
/// @notice Smallest pFLY-pool depth at the TWAP price at which the trading legs run at all.
///
/// @dev Below this the flow cap shrinks the chunk under `minExecutionAmount`, so the
/// trading legs stand down and every execution routes to burn and treasury
/// instead. That is safe but it is a cliff, and it is invisible from the outside,
/// so it is exposed rather than left to be discovered.
///
/// depth = minExecutionAmount * swapBps * 2 / halfBps
///
/// With the Phase 1 split (1500 pFLY burn + 4000 POL, half of which swaps) that is
/// 3500 bps of swap against a 29.5 bps cap at today's fees - half of 0.29% + 0.3% -
/// and a 1,000 PBURN minimum: **118,644 PBURN**. Before the sandwich bound, the 100 bps
/// `maxSwapBps` made it 35,000.
function minPoolDepthForTrading() external view returns (uint256) {
Allocation memory alloc = _baseAllocation();
uint256 swapBps = uint256(alloc.pflyBurnBps) + uint256(alloc.polBps) / 2;
if (swapBps == 0) return 0;
return Math.mulDiv(minExecutionAmount, swapBps * 2, _flowHalfBps(pfly));
}
/// @notice Whether a leg trading into `token` can be bounded AND would actually clear
/// that bound at the current price.
///
/// @dev Two distinct refusals, and both must stand a leg down rather than revert:
///
/// 1. No usable time-average - nothing to bound against.
/// 2. A usable average, but the pool would fill below the floor right now.
///
/// Checking (2) here rather than letting `_minOut` revert inside the swap is the
/// difference between "this leg does not trade this run" and "execute() reverts
/// and no leg trades, including the ones that need no price at all". The fees are
/// not lost either way, but only the first lets the burn and treasury legs run.
///
/// `isQuoteSafe` (inside `_twapFloor`) tolerates up to `maxDeviationBps` - 10% -
/// which is far wider than the 2% floor. Without this second check a manipulation
/// inside that band passes availability and then fills below the floor, which is
/// precisely the window a sandwicher would choose.
function twapBounded(address token, uint256 amountIn) public view returns (bool) {
(uint256 floor, bool available) = _twapFloor(token, amountIn);
if (!available) return false;
if (amountIn == 0) return true;
address[] memory path = new address[](2);
path[0] = address(pburn);
path[1] = token;
try dexRouter.getAmountsOut(amountIn, path) returns (uint256[] memory amounts) {
return amounts[amounts.length - 1] >= floor;
} catch {
return false;
}
}
function _applySlippage(uint256 amount) private view returns (uint256) {
return (amount * (BPS_DENOMINATOR - maxSlippageBps)) / BPS_DENOMINATOR;
}
// --- Views --------------------------------------------------------------
/// @notice Preview the split that `execute` would apply right now.
/// @notice The split the next `execute()` would PLAN, from the same `_plan` it uses. A
/// trading leg can still stand down at execution if its fill misses the floor -
/// `execute()` returns what ran, this returns what was intended.
function previewSplit() external view returns (uint256 chunk, Split memory split, uint256 tsiScore) {
Allocation memory alloc;
(chunk, alloc, tsiScore,) = _plan(pburn.balanceOf(address(this)));
split = _splitChunk(chunk, alloc);
}
function pendingFees() external view returns (uint256) {
return pburn.balanceOf(address(this));
}
// --- Governance ---------------------------------------------------------
function setDefaultAllocation(Allocation calldata alloc) external onlyRole(PARAMETER_ROLE) {
uint256 total = uint256(alloc.pburnBurnBps) + alloc.pflyBurnBps + alloc.pcockBurnBps
+ alloc.polBps + alloc.treasuryBps;
if (total != BPS_DENOMINATOR) revert AllocationMustTotalDenominator(total);
defaultAllocation = alloc;
emit AllocationUpdated(alloc);
}
function setExecutionBounds(uint256 minAmount, uint256 maxAmount, uint16 slippageBps)
external
onlyRole(PARAMETER_ROLE)
{
if (minAmount == 0 || maxAmount < minAmount || slippageBps >= BPS_DENOMINATOR) revert InvalidBounds();
minExecutionAmount = minAmount;
maxExecutionAmount = maxAmount;
maxSlippageBps = slippageBps;
emit ExecutionBoundsUpdated(minAmount, maxAmount, slippageBps);
}
/// @dev No credit ledger here on purpose: router burns are protocol-owned and have
/// no user to credit. Credit accrual lives in BondingVault and PBURN.
function setModules(address tsi_, address epoch_) external onlyRole(PARAMETER_ROLE) {
tsi = ITSI(tsi_);
epoch = IEpoch(epoch_);
emit ModulesUpdated(tsi_, epoch_);
}
/// @notice Point the second buyback leg at a token, or unset it with address(0).
/// @dev Unsetting is a deliberate kill switch: the leg stands down and the router
/// reverts to its two-token allocation without any other change.
/// @dev Activating a leg is not the same as being able to trade it safely. Before this
/// check, a single governance call opened a PBURN/pCOCK trading path with no
/// oracle coverage of any kind - the spike check is pFLY-specific, and `_minOut`
/// was spot-derived, so the leg would have traded entirely unbounded.
///
/// Requiring a mature TWAP here means the pair must have been observed for at
/// least `minPeriod` before it can receive a single trade. Note this is a
/// necessary condition, not a sufficient one: a TWAP over a thin pool is cheap to
/// hold off by the tolerance, so see the minimum-depth guidance in
/// `docs/PCOCK_ACTIVATION.md` before pointing this at anything.
function setPcockTarget(address pcock_) external onlyRole(PARAMETER_ROLE) {
if (pcock_ == address(pburn) || pcock_ == pfly) revert ZeroAddress();
if (pcock_ != address(0) && !twapBounded(pcock_, 1e18)) revert TwapUnavailable(pcock_);
address previous = pcock;
pcock = pcock_;
emit PcockTargetUpdated(previous, pcock_);
}
function setPcockParams(uint16 pcockShareBps_, uint16 maxPcockTradeBps_)
external
onlyRole(PARAMETER_ROLE)
{
if (pcockShareBps_ > BPS_DENOMINATOR || maxPcockTradeBps_ > BPS_DENOMINATOR) {
revert InvalidBounds();
}
pcockShareBps = pcockShareBps_;
maxPcockTradeBps = maxPcockTradeBps_;
emit PcockParamsUpdated(pcockShareBps_, maxPcockTradeBps_);
}
function setSignalParams(uint256 tsiStressThreshold_, uint16 pflySpikeRedirectBps_)
external
onlyRole(PARAMETER_ROLE)
{
if (pflySpikeRedirectBps_ > BPS_DENOMINATOR) revert InvalidBounds();
tsiStressThreshold = tsiStressThreshold_;
pflySpikeRedirectBps = pflySpikeRedirectBps_;
emit SignalParamsUpdated(tsiStressThreshold_, pflySpikeRedirectBps_);
}
/// @notice Tune the TWAP floor and the swap-size cap together.
///
/// @dev Together, deliberately. The two numbers are not independent: a swap of
/// `maxSwapBps` of the reserve moves price by roughly that fraction on constant
/// product and pays `POOL_FEE_BPS` on top, so unless that total fits inside the
/// floor's tolerance the leg can never fill. Separate setters would let a
/// well-intentioned operator raise the cap on Monday and discover on Friday that
/// every trading leg had been standing down since - the failure is silent, because
/// standing down is also what a healthy refusal looks like.
///
/// The bounds below are what governance cannot do:
/// - disable the floor (`0`), which reads as maximum strictness and is in fact a
/// permanent stand-down, since a real fill is always below the average;
/// - widen it past `MAX_TWAP_FLOOR_BPS`, which is what would let the sandwich
/// defence be removed without a code change;
/// - disable the swap cap (`0` is the uncapped sentinel in `_capChunkForSwaps`);
/// - set a cap whose own price impact does not fit inside the floor.
function setTwapGuards(uint16 twapFloorBps_, uint16 maxSwapBps_)
external
onlyRole(PARAMETER_ROLE)
{
if (twapFloorBps_ == 0 || twapFloorBps_ > MAX_TWAP_FLOOR_BPS) revert InvalidBounds();
if (maxSwapBps_ == 0 || maxSwapBps_ > MAX_SWAP_CAP_BPS) revert InvalidBounds();
if (uint256(maxSwapBps_) + POOL_FEE_BPS >= twapFloorBps_) revert InvalidBounds();
twapFloorBps = twapFloorBps_;
maxSwapBps = maxSwapBps_;
emit TwapGuardsUpdated(twapFloorBps_, maxSwapBps_);
}
}
lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../token/ERC20/IERC20.sol";
contracts/oracle/TwapOracle.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
import {IPulseXPair} from "../interfaces/IProtocol.sol";
/// @title TwapOracle
/// @notice Time-weighted average price oracle over PulseX (Uniswap V2) cumulative prices.
/// @dev Bond pricing and router allocation both read TWAP, never spot. Spot is used
/// only as a sanity bound: if spot deviates from TWAP by more than
/// `maxDeviationBps`, consumers can reject the quote. That combination resists
/// both flash-loan manipulation (which moves spot, not a long TWAP) and stale
/// oracles (which `maxStaleness` rejects).
contract TwapOracle is AccessControl {
bytes32 public constant UPDATER_ROLE = keccak256("UPDATER_ROLE");
uint16 public constant BPS_DENOMINATOR = 10_000;
struct Observation {
uint256 price0Cumulative;
uint256 price1Cumulative;
uint32 timestamp;
bool initialized;
}
/// @notice The newest observation for a pair. May be immature immediately after an
/// `update()`, in which case reads fall back to `previousObservations`.
mapping(address => Observation) public observations;
/// @notice The observation `update()` displaced. Always at least `minPeriod` older than
/// `observations[pair]`, because `update()` refuses to run any sooner.
///
/// @dev THIS SLOT IS THE FIX. With one slot, `update()` reset the averaging window to
/// zero and the feed went dark for `minPeriod` - and since `update()`'s own rate
/// limit is also `minPeriod`, any caller could hold it dark indefinitely for the
/// price of one transaction per window.
///
/// Keeping the displaced observation means a fresh one never blanks the feed: it
/// queues behind a mature one. Availability then depends only on elapsed time,
/// which is the property `test/OracleAvailabilityInvariant.t.sol` pins.
mapping(address => Observation) public previousObservations;
/// @notice Minimum seconds between observations. Shorter windows are easier to manipulate.
uint32 public minPeriod = 30 minutes;
/// @notice An observation older than this is considered unusable.
/// @notice Oldest observation `consult` will answer from.
///
/// @dev WAS 24 HOURS, AND THAT IS UNUSABLE ONCE THE TWAP IS A TRADING FLOOR.
///
/// `consult` averages over the whole elapsed window, so a stale observation does
/// not merely risk being out of date - it produces a LONGER average, which lags
/// spot further in any trend. The router's floor budget is 2%, of which the
/// trade's own impact (1% cap) and the pool fee (0.29%) consume 1.29%, leaving
/// 0.71% for the gap between spot and the average:
///
/// window 0.5%/h trend 1%/h 2%/h
/// 30m 0.1% 0.2% 0.5% <- inside budget
/// 1h 0.2% 0.5% 1.0%
/// 2h 0.5% 1.0% 2.0%
/// 24h 6.0% 12.0% 24.0% <- 3x the entire floor
///
/// Both directions hurt, differently. A falling market pushes spot below the
/// average and the legs stand down - safe, but the protocol stops buying exactly
/// when it is cheapest. A rising market leaves the floor BELOW spot by the lag,
/// and that gap is room a sandwicher can take while still clearing the floor.
///
/// Two hours is the backstop, not the operating point: with `update()` called
/// every `minPeriod` the window in use stays at 30-60 minutes, and this only
/// binds after three consecutive missed refreshes.
uint32 public maxStaleness = 2 hours;
/// @notice Maximum tolerated spot-vs-TWAP divergence before a quote is unsafe.
uint16 public maxDeviationBps = 1_000; // 10%
event ObservationUpdated(address indexed pair, uint256 price0Cumulative, uint256 price1Cumulative, uint32 timestamp);
event OracleParamsUpdated(uint32 minPeriod, uint32 maxStaleness, uint16 maxDeviationBps);
error PairNotInitialized(address pair);
error PeriodNotElapsed(address pair, uint32 elapsed, uint32 required);
error ObservationStale(address pair, uint32 age, uint32 maxAge);
error NoLiquidity(address pair);
error TokenNotInPair(address pair, address token);
error InvalidParams();
constructor(address admin) {
if (admin == address(0)) revert InvalidParams();
_grantRole(DEFAULT_ADMIN_ROLE, admin);
}
// --- Observation maintenance --------------------------------------------
/// @notice Seed the first observation for a pair. Callable once per pair.
function initialize(address pair) external {
Observation storage obs = observations[pair];
if (obs.initialized) return;
(uint256 price0Cumulative, uint256 price1Cumulative, uint32 timestamp) = currentCumulativePrices(pair);
obs.price0Cumulative = price0Cumulative;
obs.price1Cumulative = price1Cumulative;
obs.timestamp = timestamp;
obs.initialized = true;
emit ObservationUpdated(pair, price0Cumulative, price1Cumulative, timestamp);
}
/// @notice Roll the observation forward. Permissionless: anyone may keep the
/// oracle fresh, but `minPeriod` bounds how often it can be moved.
function update(address pair) external {
Observation storage obs = observations[pair];
if (!obs.initialized) revert PairNotInitialized(pair);
(uint256 price0Cumulative, uint256 price1Cumulative, uint32 timestamp) = currentCumulativePrices(pair);
uint32 elapsed;
unchecked {
elapsed = timestamp - obs.timestamp; // wraps correctly on uint32 overflow
}
if (elapsed < minPeriod) revert PeriodNotElapsed(pair, elapsed, minPeriod);
// Rotate, do not overwrite. The displaced observation is what keeps the feed
// answering during the `minPeriod` the new one needs to mature.
previousObservations[pair] = obs;
obs.price0Cumulative = price0Cumulative;
obs.price1Cumulative = price1Cumulative;
obs.timestamp = timestamp;
emit ObservationUpdated(pair, price0Cumulative, price1Cumulative, timestamp);
}
/// @notice The newest observation that has matured, which is what every read uses.
///
/// @dev Prefers `observations[pair]` once it is at least `minPeriod` old, because a
/// more recent window is a tighter average. Falls back to the displaced one while
/// the newest is still maturing.
///
/// The fallback is guaranteed usable rather than merely likely: `update()` reverts
/// unless `minPeriod` has elapsed, so anything it displaces is by construction at
/// least that old. There is no state in which both slots are immature except
/// during the first `minPeriod` after `initialize()`, which is honest - nothing
/// has been observed for long enough to average yet.
function _readable(address pair)
private
view
returns (Observation storage obs, uint32 elapsed)
{
(,, uint32 timestamp) = currentCumulativePrices(pair);
obs = observations[pair];
unchecked {
elapsed = timestamp - obs.timestamp;
}
if (elapsed >= minPeriod) return (obs, elapsed);
Observation storage prev = previousObservations[pair];
if (prev.initialized) {
uint32 prevElapsed;
unchecked {
prevElapsed = timestamp - prev.timestamp;
}
if (prevElapsed >= minPeriod) return (prev, prevElapsed);
}
// Neither has matured. Return the newest so the caller's own floor/staleness
// checks produce the accurate error rather than a misleading one.
return (obs, elapsed);
}
// --- Quotes -------------------------------------------------------------
/// @notice TWAP quote: how much of the other token `amountIn` of `tokenIn` is worth.
/// @dev Reverts if the observation is missing or stale.
function consult(address pair, address tokenIn, uint256 amountIn) public view returns (uint256 amountOut) {
if (!observations[pair].initialized) revert PairNotInitialized(pair);
(uint256 price0Cumulative, uint256 price1Cumulative,) = currentCumulativePrices(pair);
// The newest MATURE observation, not simply the newest. See `_readable`.
(Observation storage obs, uint32 elapsed) = _readable(pair);
// The averaging window must be at least minPeriod, not merely non-zero.
//
// A TWAP is only manipulation-resistant in proportion to the time it averages
// over. An earlier revision rejected only elapsed == 0, so a one-second-old
// observation produced a one-second average - spot price wearing a TWAP label.
// Worse, isQuoteSafe compares spot against that same near-spot average, so the
// deviation guard endorsed the manipulated number instead of rejecting it.
//
// This is NOT specific to initialize(). The exposure recurred after every
// update(), which is permissionless, so any caller could reset the window and
// read a manipulated price one block later. Enforcing the floor here fixes both
// at the read side, where the actual decision is made.
if (elapsed < minPeriod) revert PeriodNotElapsed(pair, elapsed, minPeriod);
if (elapsed > maxStaleness) revert ObservationStale(pair, elapsed, maxStaleness);
address token0 = IPulseXPair(pair).token0();
address token1 = IPulseXPair(pair).token1();
uint256 priceAverage;
if (tokenIn == token0) {
unchecked {
priceAverage = (price0Cumulative - obs.price0Cumulative) / elapsed;
}
} else if (tokenIn == token1) {
unchecked {
priceAverage = (price1Cumulative - obs.price1Cumulative) / elapsed;
}
} else {
revert TokenNotInPair(pair, tokenIn);
}
// priceAverage is UQ112x112; shift back down after multiplying.
amountOut = (priceAverage * amountIn) >> 112;
}
/// @notice Current spot quote straight from reserves, for the deviation guard only.
function spotQuote(address pair, address tokenIn, uint256 amountIn) public view returns (uint256) {
(uint112 reserve0, uint112 reserve1,) = IPulseXPair(pair).getReserves();
if (reserve0 == 0 || reserve1 == 0) revert NoLiquidity(pair);
address token0 = IPulseXPair(pair).token0();
address token1 = IPulseXPair(pair).token1();
if (tokenIn == token0) {
return (amountIn * reserve1) / reserve0;
} else if (tokenIn == token1) {
return (amountIn * reserve0) / reserve1;
}
revert TokenNotInPair(pair, tokenIn);
}
/// @notice True when spot sits within `maxDeviationBps` of the TWAP.
/// @dev Consumers should treat `false` as "do not trade right now".
function isQuoteSafe(address pair, address tokenIn, uint256 amountIn) external view returns (bool) {
uint256 twap = consult(pair, tokenIn, amountIn);
if (twap == 0) return false;
uint256 spot = spotQuote(pair, tokenIn, amountIn);
uint256 diff = spot > twap ? spot - twap : twap - spot;
return (diff * BPS_DENOMINATOR) / twap <= maxDeviationBps;
}
/// @notice Seconds until the observation `consult` reads is `minPeriod` old, or 0 once
/// it is. Reverts only if the pair was never initialized.
///
/// 0 does not mean `consult` answers - `isQuotable` says that. Past
/// `maxStaleness` this reads 0 too: while nobody refreshes, and for up to
/// `minPeriod` after a refresh that came too late, when the observation it
/// displaced is the readable one. In the second case the wait is what remains
/// of `minPeriod` on `observations(pair)`; in the first, only `update()` starts it.
/// @dev A quote refusal during an immature window is a safety behaviour, but it
/// reaches a user as a revert, which is indistinguishable from a broken
/// contract. This lets a caller render "try again in N seconds" instead, so
/// nobody is tempted to "fix" the safety behaviour later. Read it together with
/// `isQuotable`, as `scripts/lib/oracle-state.js` does.
function secondsUntilQuotable(address pair) external view returns (uint256) {
if (!observations[pair].initialized) revert PairNotInitialized(pair);
// Must agree with `consult`, which reads the newest MATURE observation. Reading
// the raw newest slot here would report a wait that consult does not actually
// impose - the reporting bug that hides the fix rather than the defect.
(, uint32 elapsed) = _readable(pair);
return elapsed >= minPeriod ? 0 : minPeriod - elapsed;
}
/// @notice True when `consult` would answer for this pair right now.
function isQuotable(address pair) external view returns (bool) {
if (!observations[pair].initialized) return false;
// Same source of truth as `consult`. These two disagreeing is how a monitor
// reports a healthy feed that cannot answer, or a dark feed that can.
(, uint32 elapsed) = _readable(pair);
return elapsed >= minPeriod && elapsed <= maxStaleness;
}
// --- Cumulative price reader --------------------------------------------
/// @notice Cumulative prices brought forward to `block.timestamp`.
/// @dev Mirrors UniswapV2OracleLibrary: the pair only writes cumulatives on sync,
/// so the elapsed-since-sync portion must be added in by the reader.
function currentCumulativePrices(address pair)
public
view
returns (uint256 price0Cumulative, uint256 price1Cumulative, uint32 blockTimestamp)
{
blockTimestamp = uint32(block.timestamp % 2 ** 32);
price0Cumulative = IPulseXPair(pair).price0CumulativeLast();
price1Cumulative = IPulseXPair(pair).price1CumulativeLast();
(uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast) = IPulseXPair(pair).getReserves();
if (reserve0 == 0 || reserve1 == 0) revert NoLiquidity(pair);
if (blockTimestampLast != blockTimestamp) {
uint32 timeElapsed;
unchecked {
timeElapsed = blockTimestamp - blockTimestampLast;
price0Cumulative += uint256(_encodeDiv(reserve1, reserve0)) * timeElapsed;
price1Cumulative += uint256(_encodeDiv(reserve0, reserve1)) * timeElapsed;
}
}
}
/// @dev UQ112x112 encode-then-divide: (numerator << 112) / denominator.
function _encodeDiv(uint112 numerator, uint112 denominator) private pure returns (uint224) {
return uint224((uint224(numerator) << 112) / denominator);
}
// --- Governance ---------------------------------------------------------
function setParams(uint32 minPeriod_, uint32 maxStaleness_, uint16 maxDeviationBps_)
external
onlyRole(DEFAULT_ADMIN_ROLE)
{
if (minPeriod_ == 0 || maxStaleness_ < minPeriod_ || maxDeviationBps_ > BPS_DENOMINATOR) {
revert InvalidParams();
}
minPeriod = minPeriod_;
maxStaleness = maxStaleness_;
maxDeviationBps = maxDeviationBps_;
emit OracleParamsUpdated(minPeriod_, maxStaleness_, maxDeviationBps_);
}
}
lib/openzeppelin-contracts/contracts/access/AccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../utils/introspection/ERC165.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
mapping(bytes32 role => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with an {AccessControlUnauthorizedAccount} error including the required role.
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
return _roles[role].hasRole[account];
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
* is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
* is missing `role`.
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
if (!hasRole(role, account)) {
_roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
/**
* @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
if (hasRole(role, account)) {
_roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}
lib/openzeppelin-contracts/contracts/utils/Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
lib/openzeppelin-contracts/contracts/utils/Errors.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of common custom errors used in multiple contracts
*
* IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
* It is recommended to avoid relying on the error API for critical functionality.
*
* _Available since v5.1._
*/
library Errors {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error InsufficientBalance(uint256 balance, uint256 needed);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedCall();
/**
* @dev The deployment failed.
*/
error FailedDeployment();
/**
* @dev A necessary precompile is missing.
*/
error MissingPrecompile(address);
}
lib/openzeppelin-contracts/contracts/access/IAccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/IAccessControl.sol)
pragma solidity ^0.8.20;
/**
* @dev External interface of AccessControl declared to support ERC-165 detection.
*/
interface IAccessControl {
/**
* @dev The `account` is missing a role.
*/
error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
/**
* @dev The caller of a function is not the expected one.
*
* NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
*/
error AccessControlBadConfirmation();
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call. This account bears the admin role (for the granted role).
* Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*/
function renounceRole(bytes32 role, address callerConfirmation) external;
}
lib/openzeppelin-contracts/contracts/utils/Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Address.sol)
pragma solidity ^0.8.20;
import {Errors} from "./Errors.sol";
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert Errors.InsufficientBalance(address(this).balance, amount);
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert Errors.FailedCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {Errors.FailedCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert Errors.InsufficientBalance(address(this).balance, value);
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
* of an unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {Errors.FailedCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
assembly ("memory-safe") {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert Errors.FailedCall();
}
}
}
lib/openzeppelin-contracts/contracts/utils/math/Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
import {Panic} from "../Panic.sol";
import {SafeCast} from "./SafeCast.sol";
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an success flag (no overflow).
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an success flag (no overflow).
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an success flag (no overflow).
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a success flag (no division by zero).
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
*
* IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
* However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
* one branch when needed, making this function more expensive.
*/
function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {
unchecked {
// branchless ternary works because:
// b ^ (a ^ b) == a
// b ^ 0 == b
return b ^ ((a ^ b) * SafeCast.toUint(condition));
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(a > b, a, b);
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(a < b, a, b);
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
Panic.panic(Panic.DIVISION_BY_ZERO);
}
// The following calculation ensures accurate ceiling division without overflow.
// Since a is non-zero, (a - 1) / b will not overflow.
// The largest possible result occurs when (a - 1) / b is type(uint256).max,
// but the largest value we can obtain is type(uint256).max - 1, which happens
// when a = type(uint256).max and b = 1.
unchecked {
return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);
}
}
/**
* @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
*
* Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use
// the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2²⁵⁶ + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.
if (denominator <= prod1) {
Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such
// that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv ≡ 1 mod 2⁴.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2⁸
inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶
inverse *= 2 - denominator * inverse; // inverse mod 2³²
inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴
inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸
inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2²⁵⁶. Since the preconditions guarantee that the outcome is
// less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @dev Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);
}
/**
* @dev Calculate the modular multiplicative inverse of a number in Z/nZ.
*
* If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.
* If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.
*
* If the input value is not inversible, 0 is returned.
*
* NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the
* inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.
*/
function invMod(uint256 a, uint256 n) internal pure returns (uint256) {
unchecked {
if (n == 0) return 0;
// The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)
// Used to compute integers x and y such that: ax + ny = gcd(a, n).
// When the gcd is 1, then the inverse of a modulo n exists and it's x.
// ax + ny = 1
// ax = 1 + (-y)n
// ax ≡ 1 (mod n) # x is the inverse of a modulo n
// If the remainder is 0 the gcd is n right away.
uint256 remainder = a % n;
uint256 gcd = n;
// Therefore the initial coefficients are:
// ax + ny = gcd(a, n) = n
// 0a + 1n = n
int256 x = 0;
int256 y = 1;
while (remainder != 0) {
uint256 quotient = gcd / remainder;
(gcd, remainder) = (
// The old remainder is the next gcd to try.
remainder,
// Compute the next remainder.
// Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd
// where gcd is at most n (capped to type(uint256).max)
gcd - remainder * quotient
);
(x, y) = (
// Increment the coefficient of a.
y,
// Decrement the coefficient of n.
// Can overflow, but the result is casted to uint256 so that the
// next value of y is "wrapped around" to a value between 0 and n - 1.
x - y * int256(quotient)
);
}
if (gcd != 1) return 0; // No inverse exists.
return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.
}
}
/**
* @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.
*
* From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is
* prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that
* `a**(p-2)` is the modular multiplicative inverse of a in Fp.
*
* NOTE: this function does NOT check that `p` is a prime greater than `2`.
*/
function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {
unchecked {
return Math.modExp(a, p - 2, p);
}
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)
*
* Requirements:
* - modulus can't be zero
* - underlying staticcall to precompile must succeed
*
* IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make
* sure the chain you're using it on supports the precompiled contract for modular exponentiation
* at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,
* the underlying function will succeed given the lack of a revert, but the result may be incorrectly
* interpreted as 0.
*/
function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {
(bool success, uint256 result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).
* It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying
* to operate modulo 0 or if the underlying precompile reverted.
*
* IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain
* you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in
* https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack
* of a revert, but the result may be incorrectly interpreted as 0.
*/
function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {
if (m == 0) return (false, 0);
assembly ("memory-safe") {
let ptr := mload(0x40)
// | Offset | Content | Content (Hex) |
// |-----------|------------|--------------------------------------------------------------------|
// | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x60:0x7f | value of b | 0x<.............................................................b> |
// | 0x80:0x9f | value of e | 0x<.............................................................e> |
// | 0xa0:0xbf | value of m | 0x<.............................................................m> |
mstore(ptr, 0x20)
mstore(add(ptr, 0x20), 0x20)
mstore(add(ptr, 0x40), 0x20)
mstore(add(ptr, 0x60), b)
mstore(add(ptr, 0x80), e)
mstore(add(ptr, 0xa0), m)
// Given the result < m, it's guaranteed to fit in 32 bytes,
// so we can use the memory scratch space located at offset 0.
success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)
result := mload(0x00)
}
}
/**
* @dev Variant of {modExp} that supports inputs of arbitrary length.
*/
function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {
(bool success, bytes memory result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Variant of {tryModExp} that supports inputs of arbitrary length.
*/
function tryModExp(
bytes memory b,
bytes memory e,
bytes memory m
) internal view returns (bool success, bytes memory result) {
if (_zeroBytes(m)) return (false, new bytes(0));
uint256 mLen = m.length;
// Encode call args in result and move the free memory pointer
result = abi.encodePacked(b.length, e.length, mLen, b, e, m);
assembly ("memory-safe") {
let dataPtr := add(result, 0x20)
// Write result on top of args to avoid allocating extra memory.
success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)
// Overwrite the length.
// result.length > returndatasize() is guaranteed because returndatasize() == m.length
mstore(result, mLen)
// Set the memory pointer after the returned data.
mstore(0x40, add(dataPtr, mLen))
}
}
/**
* @dev Returns whether the provided byte array is zero.
*/
function _zeroBytes(bytes memory byteArray) private pure returns (bool) {
for (uint256 i = 0; i < byteArray.length; ++i) {
if (byteArray[i] != 0) {
return false;
}
}
return true;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* This method is based on Newton's method for computing square roots; the algorithm is restricted to only
* using integer operations.
*/
function sqrt(uint256 a) internal pure returns (uint256) {
unchecked {
// Take care of easy edge cases when a == 0 or a == 1
if (a <= 1) {
return a;
}
// In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a
// sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between
// the current value as `ε_n = | x_n - sqrt(a) |`.
//
// For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root
// of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is
// bigger than any uint256.
//
// By noticing that
// `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`
// we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar
// to the msb function.
uint256 aa = a;
uint256 xn = 1;
if (aa >= (1 << 128)) {
aa >>= 128;
xn <<= 64;
}
if (aa >= (1 << 64)) {
aa >>= 64;
xn <<= 32;
}
if (aa >= (1 << 32)) {
aa >>= 32;
xn <<= 16;
}
if (aa >= (1 << 16)) {
aa >>= 16;
xn <<= 8;
}
if (aa >= (1 << 8)) {
aa >>= 8;
xn <<= 4;
}
if (aa >= (1 << 4)) {
aa >>= 4;
xn <<= 2;
}
if (aa >= (1 << 2)) {
xn <<= 1;
}
// We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).
//
// We can refine our estimation by noticing that the middle of that interval minimizes the error.
// If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).
// This is going to be our x_0 (and ε_0)
xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)
// From here, Newton's method give us:
// x_{n+1} = (x_n + a / x_n) / 2
//
// One should note that:
// x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a
// = ((x_n² + a) / (2 * x_n))² - a
// = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a
// = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)
// = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)
// = (x_n² - a)² / (2 * x_n)²
// = ((x_n² - a) / (2 * x_n))²
// ≥ 0
// Which proves that for all n ≥ 1, sqrt(a) ≤ x_n
//
// This gives us the proof of quadratic convergence of the sequence:
// ε_{n+1} = | x_{n+1} - sqrt(a) |
// = | (x_n + a / x_n) / 2 - sqrt(a) |
// = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |
// = | (x_n - sqrt(a))² / (2 * x_n) |
// = | ε_n² / (2 * x_n) |
// = ε_n² / | (2 * x_n) |
//
// For the first iteration, we have a special case where x_0 is known:
// ε_1 = ε_0² / | (2 * x_0) |
// ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))
// ≤ 2**(2*e-4) / (3 * 2**(e-1))
// ≤ 2**(e-3) / 3
// ≤ 2**(e-3-log2(3))
// ≤ 2**(e-4.5)
//
// For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:
// ε_{n+1} = ε_n² / | (2 * x_n) |
// ≤ (2**(e-k))² / (2 * 2**(e-1))
// ≤ 2**(2*e-2*k) / 2**e
// ≤ 2**(e-2*k)
xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5) -- special case, see above
xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9) -- general case with k = 4.5
xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18) -- general case with k = 9
xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36) -- general case with k = 18
xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72) -- general case with k = 36
xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144) -- general case with k = 72
// Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision
// ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either
// sqrt(a) or sqrt(a) + 1.
return xn - SafeCast.toUint(xn > a / xn);
}
}
/**
* @dev Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
uint256 exp;
unchecked {
exp = 128 * SafeCast.toUint(value > (1 << 128) - 1);
value >>= exp;
result += exp;
exp = 64 * SafeCast.toUint(value > (1 << 64) - 1);
value >>= exp;
result += exp;
exp = 32 * SafeCast.toUint(value > (1 << 32) - 1);
value >>= exp;
result += exp;
exp = 16 * SafeCast.toUint(value > (1 << 16) - 1);
value >>= exp;
result += exp;
exp = 8 * SafeCast.toUint(value > (1 << 8) - 1);
value >>= exp;
result += exp;
exp = 4 * SafeCast.toUint(value > (1 << 4) - 1);
value >>= exp;
result += exp;
exp = 2 * SafeCast.toUint(value > (1 << 2) - 1);
value >>= exp;
result += exp;
result += SafeCast.toUint(value > 1);
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
uint256 isGt;
unchecked {
isGt = SafeCast.toUint(value > (1 << 128) - 1);
value >>= isGt * 128;
result += isGt * 16;
isGt = SafeCast.toUint(value > (1 << 64) - 1);
value >>= isGt * 64;
result += isGt * 8;
isGt = SafeCast.toUint(value > (1 << 32) - 1);
value >>= isGt * 32;
result += isGt * 4;
isGt = SafeCast.toUint(value > (1 << 16) - 1);
value >>= isGt * 16;
result += isGt * 2;
result += SafeCast.toUint(value > (1 << 8) - 1);
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}
lib/openzeppelin-contracts/contracts/utils/math/SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.20;
/**
* @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeCast {
/**
* @dev Value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
/**
* @dev An int value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedIntToUint(int256 value);
/**
* @dev Value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
/**
* @dev An uint value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedUintToInt(uint256 value);
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toUint248(uint256 value) internal pure returns (uint248) {
if (value > type(uint248).max) {
revert SafeCastOverflowedUintDowncast(248, value);
}
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toUint240(uint256 value) internal pure returns (uint240) {
if (value > type(uint240).max) {
revert SafeCastOverflowedUintDowncast(240, value);
}
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toUint232(uint256 value) internal pure returns (uint232) {
if (value > type(uint232).max) {
revert SafeCastOverflowedUintDowncast(232, value);
}
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toUint224(uint256 value) internal pure returns (uint224) {
if (value > type(uint224).max) {
revert SafeCastOverflowedUintDowncast(224, value);
}
return uint224(value);
}
/**
* @dev Returns the downcasted uint216 from uint256, reverting on
* overflow (when the input is greater than largest uint216).
*
* Counterpart to Solidity's `uint216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toUint216(uint256 value) internal pure returns (uint216) {
if (value > type(uint216).max) {
revert SafeCastOverflowedUintDowncast(216, value);
}
return uint216(value);
}
/**
* @dev Returns the downcasted uint208 from uint256, reverting on
* overflow (when the input is greater than largest uint208).
*
* Counterpart to Solidity's `uint208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toUint208(uint256 value) internal pure returns (uint208) {
if (value > type(uint208).max) {
revert SafeCastOverflowedUintDowncast(208, value);
}
return uint208(value);
}
/**
* @dev Returns the downcasted uint200 from uint256, reverting on
* overflow (when the input is greater than largest uint200).
*
* Counterpart to Solidity's `uint200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toUint200(uint256 value) internal pure returns (uint200) {
if (value > type(uint200).max) {
revert SafeCastOverflowedUintDowncast(200, value);
}
return uint200(value);
}
/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toUint192(uint256 value) internal pure returns (uint192) {
if (value > type(uint192).max) {
revert SafeCastOverflowedUintDowncast(192, value);
}
return uint192(value);
}
/**
* @dev Returns the downcasted uint184 from uint256, reverting on
* overflow (when the input is greater than largest uint184).
*
* Counterpart to Solidity's `uint184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toUint184(uint256 value) internal pure returns (uint184) {
if (value > type(uint184).max) {
revert SafeCastOverflowedUintDowncast(184, value);
}
return uint184(value);
}
/**
* @dev Returns the downcasted uint176 from uint256, reverting on
* overflow (when the input is greater than largest uint176).
*
* Counterpart to Solidity's `uint176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toUint176(uint256 value) internal pure returns (uint176) {
if (value > type(uint176).max) {
revert SafeCastOverflowedUintDowncast(176, value);
}
return uint176(value);
}
/**
* @dev Returns the downcasted uint168 from uint256, reverting on
* overflow (when the input is greater than largest uint168).
*
* Counterpart to Solidity's `uint168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toUint168(uint256 value) internal pure returns (uint168) {
if (value > type(uint168).max) {
revert SafeCastOverflowedUintDowncast(168, value);
}
return uint168(value);
}
/**
* @dev Returns the downcasted uint160 from uint256, reverting on
* overflow (when the input is greater than largest uint160).
*
* Counterpart to Solidity's `uint160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toUint160(uint256 value) internal pure returns (uint160) {
if (value > type(uint160).max) {
revert SafeCastOverflowedUintDowncast(160, value);
}
return uint160(value);
}
/**
* @dev Returns the downcasted uint152 from uint256, reverting on
* overflow (when the input is greater than largest uint152).
*
* Counterpart to Solidity's `uint152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toUint152(uint256 value) internal pure returns (uint152) {
if (value > type(uint152).max) {
revert SafeCastOverflowedUintDowncast(152, value);
}
return uint152(value);
}
/**
* @dev Returns the downcasted uint144 from uint256, reverting on
* overflow (when the input is greater than largest uint144).
*
* Counterpart to Solidity's `uint144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toUint144(uint256 value) internal pure returns (uint144) {
if (value > type(uint144).max) {
revert SafeCastOverflowedUintDowncast(144, value);
}
return uint144(value);
}
/**
* @dev Returns the downcasted uint136 from uint256, reverting on
* overflow (when the input is greater than largest uint136).
*
* Counterpart to Solidity's `uint136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toUint136(uint256 value) internal pure returns (uint136) {
if (value > type(uint136).max) {
revert SafeCastOverflowedUintDowncast(136, value);
}
return uint136(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toUint128(uint256 value) internal pure returns (uint128) {
if (value > type(uint128).max) {
revert SafeCastOverflowedUintDowncast(128, value);
}
return uint128(value);
}
/**
* @dev Returns the downcasted uint120 from uint256, reverting on
* overflow (when the input is greater than largest uint120).
*
* Counterpart to Solidity's `uint120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toUint120(uint256 value) internal pure returns (uint120) {
if (value > type(uint120).max) {
revert SafeCastOverflowedUintDowncast(120, value);
}
return uint120(value);
}
/**
* @dev Returns the downcasted uint112 from uint256, reverting on
* overflow (when the input is greater than largest uint112).
*
* Counterpart to Solidity's `uint112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toUint112(uint256 value) internal pure returns (uint112) {
if (value > type(uint112).max) {
revert SafeCastOverflowedUintDowncast(112, value);
}
return uint112(value);
}
/**
* @dev Returns the downcasted uint104 from uint256, reverting on
* overflow (when the input is greater than largest uint104).
*
* Counterpart to Solidity's `uint104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toUint104(uint256 value) internal pure returns (uint104) {
if (value > type(uint104).max) {
revert SafeCastOverflowedUintDowncast(104, value);
}
return uint104(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toUint96(uint256 value) internal pure returns (uint96) {
if (value > type(uint96).max) {
revert SafeCastOverflowedUintDowncast(96, value);
}
return uint96(value);
}
/**
* @dev Returns the downcasted uint88 from uint256, reverting on
* overflow (when the input is greater than largest uint88).
*
* Counterpart to Solidity's `uint88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toUint88(uint256 value) internal pure returns (uint88) {
if (value > type(uint88).max) {
revert SafeCastOverflowedUintDowncast(88, value);
}
return uint88(value);
}
/**
* @dev Returns the downcasted uint80 from uint256, reverting on
* overflow (when the input is greater than largest uint80).
*
* Counterpart to Solidity's `uint80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toUint80(uint256 value) internal pure returns (uint80) {
if (value > type(uint80).max) {
revert SafeCastOverflowedUintDowncast(80, value);
}
return uint80(value);
}
/**
* @dev Returns the downcasted uint72 from uint256, reverting on
* overflow (when the input is greater than largest uint72).
*
* Counterpart to Solidity's `uint72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toUint72(uint256 value) internal pure returns (uint72) {
if (value > type(uint72).max) {
revert SafeCastOverflowedUintDowncast(72, value);
}
return uint72(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toUint64(uint256 value) internal pure returns (uint64) {
if (value > type(uint64).max) {
revert SafeCastOverflowedUintDowncast(64, value);
}
return uint64(value);
}
/**
* @dev Returns the downcasted uint56 from uint256, reverting on
* overflow (when the input is greater than largest uint56).
*
* Counterpart to Solidity's `uint56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toUint56(uint256 value) internal pure returns (uint56) {
if (value > type(uint56).max) {
revert SafeCastOverflowedUintDowncast(56, value);
}
return uint56(value);
}
/**
* @dev Returns the downcasted uint48 from uint256, reverting on
* overflow (when the input is greater than largest uint48).
*
* Counterpart to Solidity's `uint48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toUint48(uint256 value) internal pure returns (uint48) {
if (value > type(uint48).max) {
revert SafeCastOverflowedUintDowncast(48, value);
}
return uint48(value);
}
/**
* @dev Returns the downcasted uint40 from uint256, reverting on
* overflow (when the input is greater than largest uint40).
*
* Counterpart to Solidity's `uint40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toUint40(uint256 value) internal pure returns (uint40) {
if (value > type(uint40).max) {
revert SafeCastOverflowedUintDowncast(40, value);
}
return uint40(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toUint32(uint256 value) internal pure returns (uint32) {
if (value > type(uint32).max) {
revert SafeCastOverflowedUintDowncast(32, value);
}
return uint32(value);
}
/**
* @dev Returns the downcasted uint24 from uint256, reverting on
* overflow (when the input is greater than largest uint24).
*
* Counterpart to Solidity's `uint24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toUint24(uint256 value) internal pure returns (uint24) {
if (value > type(uint24).max) {
revert SafeCastOverflowedUintDowncast(24, value);
}
return uint24(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toUint16(uint256 value) internal pure returns (uint16) {
if (value > type(uint16).max) {
revert SafeCastOverflowedUintDowncast(16, value);
}
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toUint8(uint256 value) internal pure returns (uint8) {
if (value > type(uint8).max) {
revert SafeCastOverflowedUintDowncast(8, value);
}
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*/
function toUint256(int256 value) internal pure returns (uint256) {
if (value < 0) {
revert SafeCastOverflowedIntToUint(value);
}
return uint256(value);
}
/**
* @dev Returns the downcasted int248 from int256, reverting on
* overflow (when the input is less than smallest int248 or
* greater than largest int248).
*
* Counterpart to Solidity's `int248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toInt248(int256 value) internal pure returns (int248 downcasted) {
downcasted = int248(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(248, value);
}
}
/**
* @dev Returns the downcasted int240 from int256, reverting on
* overflow (when the input is less than smallest int240 or
* greater than largest int240).
*
* Counterpart to Solidity's `int240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toInt240(int256 value) internal pure returns (int240 downcasted) {
downcasted = int240(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(240, value);
}
}
/**
* @dev Returns the downcasted int232 from int256, reverting on
* overflow (when the input is less than smallest int232 or
* greater than largest int232).
*
* Counterpart to Solidity's `int232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toInt232(int256 value) internal pure returns (int232 downcasted) {
downcasted = int232(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(232, value);
}
}
/**
* @dev Returns the downcasted int224 from int256, reverting on
* overflow (when the input is less than smallest int224 or
* greater than largest int224).
*
* Counterpart to Solidity's `int224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toInt224(int256 value) internal pure returns (int224 downcasted) {
downcasted = int224(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(224, value);
}
}
/**
* @dev Returns the downcasted int216 from int256, reverting on
* overflow (when the input is less than smallest int216 or
* greater than largest int216).
*
* Counterpart to Solidity's `int216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toInt216(int256 value) internal pure returns (int216 downcasted) {
downcasted = int216(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(216, value);
}
}
/**
* @dev Returns the downcasted int208 from int256, reverting on
* overflow (when the input is less than smallest int208 or
* greater than largest int208).
*
* Counterpart to Solidity's `int208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toInt208(int256 value) internal pure returns (int208 downcasted) {
downcasted = int208(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(208, value);
}
}
/**
* @dev Returns the downcasted int200 from int256, reverting on
* overflow (when the input is less than smallest int200 or
* greater than largest int200).
*
* Counterpart to Solidity's `int200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toInt200(int256 value) internal pure returns (int200 downcasted) {
downcasted = int200(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(200, value);
}
}
/**
* @dev Returns the downcasted int192 from int256, reverting on
* overflow (when the input is less than smallest int192 or
* greater than largest int192).
*
* Counterpart to Solidity's `int192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toInt192(int256 value) internal pure returns (int192 downcasted) {
downcasted = int192(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(192, value);
}
}
/**
* @dev Returns the downcasted int184 from int256, reverting on
* overflow (when the input is less than smallest int184 or
* greater than largest int184).
*
* Counterpart to Solidity's `int184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toInt184(int256 value) internal pure returns (int184 downcasted) {
downcasted = int184(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(184, value);
}
}
/**
* @dev Returns the downcasted int176 from int256, reverting on
* overflow (when the input is less than smallest int176 or
* greater than largest int176).
*
* Counterpart to Solidity's `int176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toInt176(int256 value) internal pure returns (int176 downcasted) {
downcasted = int176(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(176, value);
}
}
/**
* @dev Returns the downcasted int168 from int256, reverting on
* overflow (when the input is less than smallest int168 or
* greater than largest int168).
*
* Counterpart to Solidity's `int168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toInt168(int256 value) internal pure returns (int168 downcasted) {
downcasted = int168(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(168, value);
}
}
/**
* @dev Returns the downcasted int160 from int256, reverting on
* overflow (when the input is less than smallest int160 or
* greater than largest int160).
*
* Counterpart to Solidity's `int160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toInt160(int256 value) internal pure returns (int160 downcasted) {
downcasted = int160(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(160, value);
}
}
/**
* @dev Returns the downcasted int152 from int256, reverting on
* overflow (when the input is less than smallest int152 or
* greater than largest int152).
*
* Counterpart to Solidity's `int152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toInt152(int256 value) internal pure returns (int152 downcasted) {
downcasted = int152(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(152, value);
}
}
/**
* @dev Returns the downcasted int144 from int256, reverting on
* overflow (when the input is less than smallest int144 or
* greater than largest int144).
*
* Counterpart to Solidity's `int144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toInt144(int256 value) internal pure returns (int144 downcasted) {
downcasted = int144(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(144, value);
}
}
/**
* @dev Returns the downcasted int136 from int256, reverting on
* overflow (when the input is less than smallest int136 or
* greater than largest int136).
*
* Counterpart to Solidity's `int136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toInt136(int256 value) internal pure returns (int136 downcasted) {
downcasted = int136(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(136, value);
}
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toInt128(int256 value) internal pure returns (int128 downcasted) {
downcasted = int128(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(128, value);
}
}
/**
* @dev Returns the downcasted int120 from int256, reverting on
* overflow (when the input is less than smallest int120 or
* greater than largest int120).
*
* Counterpart to Solidity's `int120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toInt120(int256 value) internal pure returns (int120 downcasted) {
downcasted = int120(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(120, value);
}
}
/**
* @dev Returns the downcasted int112 from int256, reverting on
* overflow (when the input is less than smallest int112 or
* greater than largest int112).
*
* Counterpart to Solidity's `int112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toInt112(int256 value) internal pure returns (int112 downcasted) {
downcasted = int112(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(112, value);
}
}
/**
* @dev Returns the downcasted int104 from int256, reverting on
* overflow (when the input is less than smallest int104 or
* greater than largest int104).
*
* Counterpart to Solidity's `int104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toInt104(int256 value) internal pure returns (int104 downcasted) {
downcasted = int104(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(104, value);
}
}
/**
* @dev Returns the downcasted int96 from int256, reverting on
* overflow (when the input is less than smallest int96 or
* greater than largest int96).
*
* Counterpart to Solidity's `int96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toInt96(int256 value) internal pure returns (int96 downcasted) {
downcasted = int96(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(96, value);
}
}
/**
* @dev Returns the downcasted int88 from int256, reverting on
* overflow (when the input is less than smallest int88 or
* greater than largest int88).
*
* Counterpart to Solidity's `int88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toInt88(int256 value) internal pure returns (int88 downcasted) {
downcasted = int88(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(88, value);
}
}
/**
* @dev Returns the downcasted int80 from int256, reverting on
* overflow (when the input is less than smallest int80 or
* greater than largest int80).
*
* Counterpart to Solidity's `int80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toInt80(int256 value) internal pure returns (int80 downcasted) {
downcasted = int80(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(80, value);
}
}
/**
* @dev Returns the downcasted int72 from int256, reverting on
* overflow (when the input is less than smallest int72 or
* greater than largest int72).
*
* Counterpart to Solidity's `int72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toInt72(int256 value) internal pure returns (int72 downcasted) {
downcasted = int72(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(72, value);
}
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toInt64(int256 value) internal pure returns (int64 downcasted) {
downcasted = int64(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(64, value);
}
}
/**
* @dev Returns the downcasted int56 from int256, reverting on
* overflow (when the input is less than smallest int56 or
* greater than largest int56).
*
* Counterpart to Solidity's `int56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toInt56(int256 value) internal pure returns (int56 downcasted) {
downcasted = int56(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(56, value);
}
}
/**
* @dev Returns the downcasted int48 from int256, reverting on
* overflow (when the input is less than smallest int48 or
* greater than largest int48).
*
* Counterpart to Solidity's `int48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toInt48(int256 value) internal pure returns (int48 downcasted) {
downcasted = int48(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(48, value);
}
}
/**
* @dev Returns the downcasted int40 from int256, reverting on
* overflow (when the input is less than smallest int40 or
* greater than largest int40).
*
* Counterpart to Solidity's `int40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toInt40(int256 value) internal pure returns (int40 downcasted) {
downcasted = int40(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(40, value);
}
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toInt32(int256 value) internal pure returns (int32 downcasted) {
downcasted = int32(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(32, value);
}
}
/**
* @dev Returns the downcasted int24 from int256, reverting on
* overflow (when the input is less than smallest int24 or
* greater than largest int24).
*
* Counterpart to Solidity's `int24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toInt24(int256 value) internal pure returns (int24 downcasted) {
downcasted = int24(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(24, value);
}
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toInt16(int256 value) internal pure returns (int16 downcasted) {
downcasted = int16(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(16, value);
}
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toInt8(int256 value) internal pure returns (int8 downcasted) {
downcasted = int8(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(8, value);
}
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
if (value > uint256(type(int256).max)) {
revert SafeCastOverflowedUintToInt(value);
}
return int256(value);
}
/**
* @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
*/
function toUint(bool b) internal pure returns (uint256 u) {
assembly ("memory-safe") {
u := iszero(iszero(b))
}
}
}
lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
// bubble errors
if iszero(success) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
returnSize := returndatasize()
returnValue := mload(0)
}
if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
bool success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
returnSize := returndatasize()
returnValue := mload(0)
}
return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
}
}
lib/openzeppelin-contracts/contracts/utils/introspection/ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
lib/openzeppelin-contracts/contracts/interfaces/IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../utils/introspection/IERC165.sol";
contracts/interfaces/IProtocol.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
/// @notice Minimal PulseX (Uniswap V2) surface used across the protocol.
interface IPulseXFactory {
function getPair(address tokenA, address tokenB) external view returns (address pair);
function createPair(address tokenA, address tokenB) external returns (address pair);
}
interface IPulseXPair {
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 (uint256);
function price1CumulativeLast() external view returns (uint256);
function totalSupply() external view returns (uint256);
/// @dev A V2 pair is itself the LP ERC-20, so holdings are read from the pair.
function balanceOf(address account) external view returns (uint256);
}
interface IPulseXRouter {
function factory() external view returns (address);
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external;
function addLiquidity(
address tokenA,
address tokenB,
uint256 amountADesired,
uint256 amountBDesired,
uint256 amountAMin,
uint256 amountBMin,
address to,
uint256 deadline
) external returns (uint256 amountA, uint256 amountB, uint256 liquidity);
function getAmountsOut(uint256 amountIn, address[] calldata path) external view returns (uint256[] memory amounts);
}
/// @notice Burnable subset of PBURN used by the router and the credit ledger.
interface IBurnable {
function burn(uint256 amount) external;
function totalSupply() external view returns (uint256);
}
/// @notice Credit accrual hook. Only protocol contracts may call the granting functions.
interface IBurnCreditLedger {
function creditsOf(address account) external view returns (uint256);
function feeDiscountBps(address account) external view returns (uint16);
function grantForBurn(address account, uint256 burnedAmount) external;
function grantForBond(address account, uint256 lpValue) external;
function grantForGovernance(address account, uint256 weight) external;
}
/// @notice Read-only composite treasury health score consumed by the router.
interface ITSI {
/// @return score Composite score in basis points (0..10000).
function score() external view returns (uint256);
function components()
external
view
returns (uint256 treasuryValue, uint256 polValue, uint256 burnRate, uint256 liquidityDepth, uint256 supplyTrend);
}
/// @notice Treasury deposit surface used by the router and bonding vault.
interface ITreasury {
function recordDeposit(address asset, uint256 amount) external;
function recordLpDeposit(address pair, uint256 amount) external;
}
/// @notice Epoch parameter presets consumed by the router.
interface IEpoch {
enum Phase {
Bootstrapping,
AcceleratedBurn,
GovernanceAndGrowth,
Sustainability
}
function currentPhase() external view returns (Phase);
function allocation() external view returns (uint16 pburnBurnBps, uint16 pflyBurnBps, uint16 polBps, uint16 treasuryBps);
}
lib/openzeppelin-contracts/contracts/utils/ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
* consider using {ReentrancyGuardTransient} instead.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
_status = ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}
lib/openzeppelin-contracts/contracts/interfaces/IERC1363.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";
/**
* @title IERC1363
* @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
*
* Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
* after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
*/
interface IERC1363 is IERC20, IERC165 {
/*
* Note: the ERC-165 identifier for this interface is 0xb0202a11.
* 0xb0202a11 ===
* bytes4(keccak256('transferAndCall(address,uint256)')) ^
* bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
* bytes4(keccak256('approveAndCall(address,uint256)')) ^
* bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
*/
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format, sent in call to `spender`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}
lib/openzeppelin-contracts/contracts/utils/Panic.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol)
pragma solidity ^0.8.20;
/**
* @dev Helper library for emitting standardized panic codes.
*
* ```solidity
* contract Example {
* using Panic for uint256;
*
* // Use any of the declared internal constants
* function foo() { Panic.GENERIC.panic(); }
*
* // Alternatively
* function foo() { Panic.panic(Panic.GENERIC); }
* }
* ```
*
* Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].
*
* _Available since v5.1._
*/
// slither-disable-next-line unused-state
library Panic {
/// @dev generic / unspecified error
uint256 internal constant GENERIC = 0x00;
/// @dev used by the assert() builtin
uint256 internal constant ASSERT = 0x01;
/// @dev arithmetic underflow or overflow
uint256 internal constant UNDER_OVERFLOW = 0x11;
/// @dev division or modulo by zero
uint256 internal constant DIVISION_BY_ZERO = 0x12;
/// @dev enum conversion error
uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;
/// @dev invalid encoding in storage
uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;
/// @dev empty array pop
uint256 internal constant EMPTY_ARRAY_POP = 0x31;
/// @dev array out of bounds access
uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;
/// @dev resource error (too large allocation or too large array)
uint256 internal constant RESOURCE_ERROR = 0x41;
/// @dev calling invalid internal function
uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;
/// @dev Reverts with a panic code. Recommended to use with
/// the internal constants with predefined codes.
function panic(uint256 code) internal pure {
assembly ("memory-safe") {
mstore(0x00, 0x4e487b71)
mstore(0x20, code)
revert(0x1c, 0x24)
}
}
}
lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
Compiler Settings
{"viaIR":false,"remappings":["@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/","forge-std/=lib/forge-std/src/","ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/","erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/","halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/","openzeppelin-contracts/=lib/openzeppelin-contracts/"],"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}},"optimizer":{"runs":200,"enabled":true},"metadata":{"useLiteralContent":false,"bytecodeHash":"ipfs","appendCBOR":true},"libraries":{},"evmVersion":"paris"}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"admin","internalType":"address"},{"type":"address","name":"pburn_","internalType":"address"},{"type":"address","name":"pfly_","internalType":"address"},{"type":"address","name":"treasury_","internalType":"address"},{"type":"address","name":"dexRouter_","internalType":"address"},{"type":"address","name":"oracle_","internalType":"address"}]},{"type":"error","name":"AccessControlBadConfirmation","inputs":[]},{"type":"error","name":"AccessControlUnauthorizedAccount","inputs":[{"type":"address","name":"account","internalType":"address"},{"type":"bytes32","name":"neededRole","internalType":"bytes32"}]},{"type":"error","name":"AllocationMustTotalDenominator","inputs":[{"type":"uint256","name":"provided","internalType":"uint256"}]},{"type":"error","name":"BelowMinimum","inputs":[{"type":"uint256","name":"balance","internalType":"uint256"},{"type":"uint256","name":"minimum","internalType":"uint256"}]},{"type":"error","name":"InsufficientGasForLeg","inputs":[]},{"type":"error","name":"InvalidBounds","inputs":[]},{"type":"error","name":"ReentrancyGuardReentrantCall","inputs":[]},{"type":"error","name":"SafeERC20FailedOperation","inputs":[{"type":"address","name":"token","internalType":"address"}]},{"type":"error","name":"SplitDoesNotMatchChunk","inputs":[{"type":"uint256","name":"sum","internalType":"uint256"},{"type":"uint256","name":"chunk","internalType":"uint256"}]},{"type":"error","name":"SwapProducedNothing","inputs":[]},{"type":"error","name":"TwapUnavailable","inputs":[{"type":"address","name":"token","internalType":"address"}]},{"type":"error","name":"ZeroAddress","inputs":[]},{"type":"event","name":"AllocationUpdated","inputs":[{"type":"tuple","name":"allocation","internalType":"struct BuybackRouter.Allocation","indexed":false,"components":[{"type":"uint16","name":"pburnBurnBps","internalType":"uint16"},{"type":"uint16","name":"pflyBurnBps","internalType":"uint16"},{"type":"uint16","name":"pcockBurnBps","internalType":"uint16"},{"type":"uint16","name":"polBps","internalType":"uint16"},{"type":"uint16","name":"treasuryBps","internalType":"uint16"}]}],"anonymous":false},{"type":"event","name":"Executed","inputs":[{"type":"uint256","name":"chunk","internalType":"uint256","indexed":false},{"type":"tuple","name":"split","internalType":"struct BuybackRouter.Split","indexed":false,"components":[{"type":"uint256","name":"pburnBurn","internalType":"uint256"},{"type":"uint256","name":"pflyBurn","internalType":"uint256"},{"type":"uint256","name":"pcockBurn","internalType":"uint256"},{"type":"uint256","name":"pol","internalType":"uint256"},{"type":"uint256","name":"treasury","internalType":"uint256"}]},{"type":"uint256","name":"tsiScore","internalType":"uint256","indexed":false},{"type":"bool","name":"tsiSignalAvailable","internalType":"bool","indexed":false},{"type":"bool","name":"spikeSignalAvailable","internalType":"bool","indexed":false},{"type":"bool","name":"pcockLegAvailable","internalType":"bool","indexed":false},{"type":"bool","name":"pflyPoolAvailable","internalType":"bool","indexed":false},{"type":"address","name":"caller","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"ExecutionBoundsUpdated","inputs":[{"type":"uint256","name":"minAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"maxAmount","internalType":"uint256","indexed":false},{"type":"uint16","name":"maxSlippageBps","internalType":"uint16","indexed":false}],"anonymous":false},{"type":"event","name":"LegStoodDown","inputs":[{"type":"address","name":"token","internalType":"address","indexed":true},{"type":"uint256","name":"pburnAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"minOut","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ModulesUpdated","inputs":[{"type":"address","name":"tsi","internalType":"address","indexed":false},{"type":"address","name":"epoch","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"PburnBurned","inputs":[{"type":"uint256","name":"amount","internalType":"uint256","indexed":false},{"type":"uint256","name":"newTotalSupply","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"PcockBurned","inputs":[{"type":"uint256","name":"pburnSpent","internalType":"uint256","indexed":false},{"type":"uint256","name":"pcockReceived","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"PcockParamsUpdated","inputs":[{"type":"uint16","name":"pcockShareBps","internalType":"uint16","indexed":false},{"type":"uint16","name":"maxPcockTradeBps","internalType":"uint16","indexed":false}],"anonymous":false},{"type":"event","name":"PcockTargetUpdated","inputs":[{"type":"address","name":"previousToken","internalType":"address","indexed":true},{"type":"address","name":"newToken","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"PflyBurned","inputs":[{"type":"uint256","name":"pburnSpent","internalType":"uint256","indexed":false},{"type":"uint256","name":"pflyReceived","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"PolAdded","inputs":[{"type":"uint256","name":"pburnSpent","internalType":"uint256","indexed":false},{"type":"uint256","name":"pairedSpent","internalType":"uint256","indexed":false},{"type":"uint256","name":"liquidity","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"RoleAdminChanged","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32","indexed":true},{"type":"bytes32","name":"previousAdminRole","internalType":"bytes32","indexed":true},{"type":"bytes32","name":"newAdminRole","internalType":"bytes32","indexed":true}],"anonymous":false},{"type":"event","name":"RoleGranted","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32","indexed":true},{"type":"address","name":"account","internalType":"address","indexed":true},{"type":"address","name":"sender","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"RoleRevoked","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32","indexed":true},{"type":"address","name":"account","internalType":"address","indexed":true},{"type":"address","name":"sender","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"SignalParamsUpdated","inputs":[{"type":"uint256","name":"tsiStressThreshold","internalType":"uint256","indexed":false},{"type":"uint16","name":"pflySpikeRedirectBps","internalType":"uint16","indexed":false}],"anonymous":false},{"type":"event","name":"TreasuryFunded","inputs":[{"type":"address","name":"asset","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"TwapGuardsUpdated","inputs":[{"type":"uint16","name":"twapFloorBps","internalType":"uint16","indexed":false},{"type":"uint16","name":"maxSwapBps","internalType":"uint16","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint16","name":"","internalType":"uint16"}],"name":"BPS_DENOMINATOR","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"DEAD","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"DEFAULT_ADMIN_ROLE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"KEEPER_ROLE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint16","name":"","internalType":"uint16"}],"name":"MAX_SWAP_CAP_BPS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint16","name":"","internalType":"uint16"}],"name":"MAX_TWAP_FLOOR_BPS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"PARAMETER_ROLE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint16","name":"","internalType":"uint16"}],"name":"POOL_FEE_BPS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"SWAP_GAS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint16","name":"pburnBurnBps","internalType":"uint16"},{"type":"uint16","name":"pflyBurnBps","internalType":"uint16"},{"type":"uint16","name":"pcockBurnBps","internalType":"uint16"},{"type":"uint16","name":"polBps","internalType":"uint16"},{"type":"uint16","name":"treasuryBps","internalType":"uint16"}],"name":"defaultAllocation","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IPulseXRouter"}],"name":"dexRouter","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IEpoch"}],"name":"epoch","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"tuple","name":"split","internalType":"struct BuybackRouter.Split","components":[{"type":"uint256","name":"pburnBurn","internalType":"uint256"},{"type":"uint256","name":"pflyBurn","internalType":"uint256"},{"type":"uint256","name":"pcockBurn","internalType":"uint256"},{"type":"uint256","name":"pol","internalType":"uint256"},{"type":"uint256","name":"treasury","internalType":"uint256"}]}],"name":"execute","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"getRoleAdmin","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"grantRole","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"hasRole","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"maxExecutionAmount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"maxFlowInto","inputs":[{"type":"address","name":"token","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint16","name":"","internalType":"uint16"}],"name":"maxPcockTradeBps","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint16","name":"","internalType":"uint16"}],"name":"maxSlippageBps","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint16","name":"","internalType":"uint16"}],"name":"maxSwapBps","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"minExecutionAmount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"minPoolDepthForTrading","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract TwapOracle"}],"name":"oracle","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IERC20"}],"name":"pburn","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"pcock","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"pcockLegAvailable","inputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"pcockPoolReady","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint16","name":"","internalType":"uint16"}],"name":"pcockShareBps","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"pendingFees","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"pfly","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"pflyPoolAvailable","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint16","name":"","internalType":"uint16"}],"name":"pflySpikeRedirectBps","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"poolDepthAtTwap","inputs":[{"type":"address","name":"token","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"chunk","internalType":"uint256"},{"type":"tuple","name":"split","internalType":"struct BuybackRouter.Split","components":[{"type":"uint256","name":"pburnBurn","internalType":"uint256"},{"type":"uint256","name":"pflyBurn","internalType":"uint256"},{"type":"uint256","name":"pcockBurn","internalType":"uint256"},{"type":"uint256","name":"pol","internalType":"uint256"},{"type":"uint256","name":"treasury","internalType":"uint256"}]},{"type":"uint256","name":"tsiScore","internalType":"uint256"}],"name":"previewSplit","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceRole","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"callerConfirmation","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"revokeRole","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"sandwichCostBps","inputs":[{"type":"address","name":"token","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setDefaultAllocation","inputs":[{"type":"tuple","name":"alloc","internalType":"struct BuybackRouter.Allocation","components":[{"type":"uint16","name":"pburnBurnBps","internalType":"uint16"},{"type":"uint16","name":"pflyBurnBps","internalType":"uint16"},{"type":"uint16","name":"pcockBurnBps","internalType":"uint16"},{"type":"uint16","name":"polBps","internalType":"uint16"},{"type":"uint16","name":"treasuryBps","internalType":"uint16"}]}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setExecutionBounds","inputs":[{"type":"uint256","name":"minAmount","internalType":"uint256"},{"type":"uint256","name":"maxAmount","internalType":"uint256"},{"type":"uint16","name":"slippageBps","internalType":"uint16"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setModules","inputs":[{"type":"address","name":"tsi_","internalType":"address"},{"type":"address","name":"epoch_","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setPcockParams","inputs":[{"type":"uint16","name":"pcockShareBps_","internalType":"uint16"},{"type":"uint16","name":"maxPcockTradeBps_","internalType":"uint16"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setPcockTarget","inputs":[{"type":"address","name":"pcock_","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setSignalParams","inputs":[{"type":"uint256","name":"tsiStressThreshold_","internalType":"uint256"},{"type":"uint16","name":"pflySpikeRedirectBps_","internalType":"uint16"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setTwapGuards","inputs":[{"type":"uint16","name":"twapFloorBps_","internalType":"uint16"},{"type":"uint16","name":"maxSwapBps_","internalType":"uint16"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"spikeCheckAvailable","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"supportsInterface","inputs":[{"type":"bytes4","name":"interfaceId","internalType":"bytes4"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalPburnBurned","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalPcockBurned","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalPflyBurned","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalPolAdded","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalTreasuryFunded","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"treasury","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract ITSI"}],"name":"tsi","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"tsiStressThreshold","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"twapBounded","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"amountIn","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint16","name":"","internalType":"uint16"}],"name":"twapFloorBps","inputs":[]}]
Contract Creation Code
0x6101c0604052610fa06101208190526101408190526000610160526103e86101808190526101a052600580546001600160501b0319166903e803e800000fa00fa0179055683635c9adc5dea0000060065569152d02c7e14af68000006007556008805465ffffffffffff19908116646400c801f417909155600991909155600a805490911664c809c4138817905534801561009957600080fd5b50604051614f04380380614f048339810160408190526100b89161023d565b600180556001600160a01b03861615806100d957506001600160a01b038516155b806100eb57506001600160a01b038416155b806100fd57506001600160a01b038316155b8061010f57506001600160a01b038216155b8061012157506001600160a01b038116155b1561013f5760405163d92e233d60e01b815260040160405180910390fd5b61014a600087610175565b506001600160a01b0394851660805292841660a05290831660c052821660e0521661010052506102b1565b6000828152602081815260408083206001600160a01b038516845290915281205460ff16610217576000838152602081815260408083206001600160a01b03861684529091529020805460ff191660011790556101cf3390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a450600161021b565b5060005b92915050565b80516001600160a01b038116811461023857600080fd5b919050565b60008060008060008060c0878903121561025657600080fd5b61025f87610221565b955061026d60208801610221565b945061027b60408801610221565b935061028960608801610221565b925061029760808801610221565b91506102a560a08801610221565b90509295509295509295565b60805160a05160c05160e05161010051614a176104ed6000396000818161069b01528181610a99015281816118590152818161192f015281816119d70152818161269201526127600152600081816103de01528181611185015281816116f301528181611d0501528181612f6d01528181612fcf01528181613254015281816132e4015281816133ff015281816134530152818161347c015281816135ef015281816136440152818161366e01528181613cd30152613f5d01526000818161058e01528181613547015281816137e7015281816138d7015261394201526000818161040501528181610a2c01528181610d92015281816117b20152818161249c0152818161262b01528181612a6601528181612a9901528181612ade01528181613158015281816131b80152818161327e0152818161333801528181613431015281816134cc015281816136220152818161372d0152613c560152600081816106d70152818161095301528181610ac801528181610d5701528181610ede015281816110fa0152818161126f0152818161178a015281816118880152818161195e01528181611a0701528181611d9c01528181611ed1015281816121d7015281816126c10152818161278f01528181612cd701528181612d7801528181612e3c01528181612f4b01528181612fad0152818161310401528181613232015281816132c2015281816133dd015281816134ab015281816135cd01528181613705015281816138b50152818161391301526139c50152614a176000f3fe608060405234801561001057600080fd5b50600436106103835760003560e01c8063750f198a116101de578063c3b7df511161010f578063d74a5d00116100ad578063f69f84ee1161007c578063f69f84ee146107ef578063f8f64a3b146107f8578063f957cd611461080c578063fb63ee181461081f57600080fd5b8063d74a5d00146107b8578063e1a45218146107cb578063e5462864146107d4578063eafdbb23146107e757600080fd5b8063ca4325c5116100e9578063ca4325c51461077f578063cd61895814610788578063cd6c78731461079d578063d547741f146107a557600080fd5b8063c3b7df5114610755578063c4aa739514610768578063c4c257241461077657600080fd5b806384ccc7421161017c57806399e64b0c1161015657806399e64b0c1461071f578063a217fddf14610732578063a8419d581461073a578063b75358fe1461074257600080fd5b806384ccc742146106d2578063900cf0cf146106f957806391d148541461070c57600080fd5b80637b8fd616116101b85780637b8fd6161461067a5780637b97cbba1461068d5780637dc0d1d0146106965780637e9ec2b9146106bd57600080fd5b8063750f198a146105ea57806375a119ab146105f4578063793216441461060b57600080fd5b80633395bdc1116102b85780634fe369db11610256578063691afd6311610230578063691afd63146105b0578063695dd5e2146105c557806369f2b0a6146105d857806372e1ebd6146105e157600080fd5b80634fe369db14610561578063614619541461057457806361d027b31461058957600080fd5b8063399e79da11610292578063399e79da146105295780633df0f99414610532578063498d0313146105455780634bf1ca611461054e57600080fd5b80633395bdc1146104e6578063364bc15a146104ef57806336568abe1461051657600080fd5b806316653a371161032557806327227277116102ff578063272272771461049157806329f780b8146104995780632d505365146104c05780632f2ff15d146104d357600080fd5b806316653a3714610451578063224438d114610466578063248a9ca31461046e57600080fd5b80630758d924116103615780630758d924146103d9578063084d91b51461040057806309f4b0c3146104275780631417cd361461043e57600080fd5b806301ffc9a71461038857806303fd2a45146103b057806304d02328146103d1575b600080fd5b61039b61039636600461419f565b61082d565b60405190151581526020015b60405180910390f35b6103b961dead81565b6040516001600160a01b0390911681526020016103a7565b61039b610864565b6103b97f000000000000000000000000000000000000000000000000000000000000000081565b6103b97f000000000000000000000000000000000000000000000000000000000000000081565b61043060065481565b6040519081526020016103a7565b6004546103b9906001600160a01b031681565b61046461045f3660046141d9565b610876565b005b61043061093b565b61043061047c366004614212565b60009081526020819052604090206001015490565b6104306109cb565b600a546104ad9062010000900461ffff1681565b60405161ffff90911681526020016103a7565b6104306104ce366004614240565b610a5c565b6104646104e136600461425d565b610bfe565b6104ad6101f481565b6104307ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab81565b61046461052436600461425d565b610c29565b610430600e5481565b610430610540366004614240565b610c61565b6104ad6103e881565b61046461055c366004614282565b610c84565b61046461056f366004614240565b610d3d565b61057c610e8c565b6040516103a791906142bb565b6103b97f000000000000000000000000000000000000000000000000000000000000000081565b6104306000805160206149c283398151915281565b61039b6105d33660046142f4565b6110a1565b610430600b5481565b610430600d5481565b610430620f424081565b6105fc611242565b6040516103a793929190614320565b6005546106459061ffff80821691620100008104821691600160201b820481169166010000000000008104821691600160401b9091041685565b6040805161ffff968716815294861660208601529285169284019290925283166060830152909116608082015260a0016103a7565b6104646106883660046141d9565b611302565b610430600f5481565b6103b97f000000000000000000000000000000000000000000000000000000000000000081565b600a546104ad90600160201b900461ffff1681565b6103b97f000000000000000000000000000000000000000000000000000000000000000081565b6003546103b9906001600160a01b031681565b61039b61071a36600461425d565b611422565b61046461072d36600461436d565b61144b565b610430600081565b61039b6114da565b610430610750366004614240565b611506565b61039b610763366004614212565b61152a565b6008546104ad9061ffff1681565b610430600c5481565b61043060075481565b6008546104ad90600160201b900461ffff1681565b6104ad601d81565b6104646107b336600461425d565b611535565b6104646107c6366004614392565b61155a565b6104ad61271081565b6104646107e23660046143c0565b6115d9565b61039b6116e2565b61043060095481565b6008546104ad9062010000900461ffff1681565b6002546103b9906001600160a01b031681565b600a546104ad9061ffff1681565b60006001600160e01b03198216637965db0b60e01b148061085e57506301ffc9a760e01b6001600160e01b03198316145b92915050565b60008061086f6116ec565b9392505050565b6000805160206149c283398151915261088e81611a9d565b61271061ffff841611806108a7575061271061ffff8316115b156108c55760405163a883435760e01b815260040160405180910390fd5b600a805465ffffffff000019166201000061ffff86811691820265ffff00000000191692909217600160201b928616928302179092556040805192835260208301919091527f4a484c9dd677725a26fef1124046bb1452cbd053ff1d766a5dc5c7a6f3b9fc7391015b60405180910390a1505050565b6040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156109a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109c691906143db565b905090565b6000806109d6611aaa565b905060006002826060015161ffff166109ef9190614420565b826020015161ffff16610a029190614434565b905080600003610a155760009250505090565b600654610a5590610a27836002614447565b610a507f0000000000000000000000000000000000000000000000000000000000000000611bd1565b611c2c565b9250505090565b600080610a6883611ce7565b5090506001600160a01b038116610a825750600092915050565b6040516318db3e8360e21b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063636cfa0c90610afa9084907f000000000000000000000000000000000000000000000000000000000000000090670de0b6b3a76400009060040161445e565b602060405180830381865afa925050508015610b33575060408051601f3d908101601f19168201909252610b30918101906143db565b60015b610b405750600092915050565b80600003610b52575060009392505050565b600080836001600160a01b0316630902f1ac6040518163ffffffff1660e01b8152600401606060405180830381865afa158015610b93573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bb79190614499565b5091509150610bf4610bef826001600160701b0316846001600160701b0316610be09190614447565b670de0b6b3a764000086611c2c565b611f83565b9695505050505050565b600082815260208190526040902060010154610c1981611a9d565b610c2383836120d6565b50505050565b6001600160a01b0381163314610c525760405163334bd91960e11b815260040160405180910390fd5b610c5c8282612168565b505050565b600080610c6d83611ce7565b509050610c79816121d3565b61086f90601d614434565b6000805160206149c2833981519152610c9c81611a9d565b831580610ca857508383105b80610cb9575061271061ffff831610155b15610cd75760405163a883435760e01b815260040160405180910390fd5b600684905560078390556008805461ffff191661ffff84169081179091556040805186815260208101869052908101919091527ffc21bee290f9c27f1f68ad03795732385f34a4b52fc51a08a11dedc3dd6707749060600160405180910390a150505050565b6000805160206149c2833981519152610d5581611a9d565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b03161480610dc657507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b0316145b15610de45760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b03821615801590610e0b5750610e0982670de0b6b3a76400006110a1565b155b15610e3957604051630817760b60e11b81526001600160a01b03831660048201526024015b60405180910390fd5b600480546001600160a01b038481166001600160a01b0319831681179093556040519116919082907f932cafc1bfa395e33c078c0972247df1f26efb8a839a77d3c092c298522a175d90600090a3505050565b610e94614142565b7ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab610ebe81611a9d565b610ec66122ea565b6040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015610f2d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f5191906143db565b9050600654811015610f845760065460405163bd49034f60e01b8152610e30918391600401918252602082015260400190565b600080600080610f9385612314565b9350935093509350610fa58484612390565b965060008760800151886060015189604001518a602001518b60000151610fcc9190614434565b610fd69190614434565b610fe09190614434565b610fea9190614434565b90508481146110165760405163080d3f8160e21b81526004810182905260248101869052604401610e30565b61101f8861246b565b9750600061102b6116ec565b915050336001600160a01b03167f17930e2d94715b959eca76104a52a13b210ac876fed91c03520cf366b32a00ce878b87878660008c6040015161ffff1611611072612623565b60405161108597969594939291906144de565b60405180910390a25050505050505061109d60018055565b5090565b60008060006110b0858561264f565b91509150806110c45760009250505061085e565b836000036110d75760019250505061085e565b6040805160028082526060820183526000926020830190803683370190505090507f00000000000000000000000000000000000000000000000000000000000000008160008151811061112c5761112c614564565b60200260200101906001600160a01b031690816001600160a01b031681525050858160018151811061116057611160614564565b6001600160a01b03928316602091820292909201015260405163d06ca61f60e01b81527f00000000000000000000000000000000000000000000000000000000000000009091169063d06ca61f906111be90889085906004016145bf565b600060405180830381865afa9250505080156111fc57506040513d6000823e601f3d908101601f191682016040526111f991908101906145d8565b60015b61120c576000935050505061085e565b83816001835161121c91906146a5565b8151811061122c5761122c614564565b6020026020010151101594505050505092915050565b600061124c614142565b6000611256614171565b6040516370a0823160e01b81523060048201526112e7907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156112be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112e291906143db565b612314565b5091955090925090506112fa8482612390565b925050909192565b6000805160206149c283398151915261131a81611a9d565b61ffff8316158061133057506103e861ffff8416115b1561134e5760405163a883435760e01b815260040160405180910390fd5b61ffff8216158061136457506101f461ffff8316115b156113825760405163a883435760e01b815260040160405180910390fd5b61ffff8084169061139790601d908516614434565b106113b55760405163a883435760e01b815260040160405180910390fd5b6008805465ffffffff000019166201000061ffff86811691820265ffff00000000191692909217600160201b928616928302179092556040805192835260208301919091527fc28a52bd1071628b2a7a0fbb85d8a917655ae4522a0f9cf3078dffe999d1fd6d910161092e565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b6000805160206149c283398151915261146381611a9d565b61271061ffff8316111561148a5760405163a883435760e01b815260040160405180910390fd5b6009839055600a805461ffff191661ffff84169081179091556040805185815260208101929092527f6a4622eb76f2b4cbec83896009e96777a569df6bb9bf79d2e0db8f0d07869b37910161092e565b60045460009081906114f4906001600160a01b0316611ce7565b506001600160a01b0316151592915050565b600061085e61151483610a5c565b61151d84611bd1565b610a506127106002614447565b600061085e8261285c565b60008281526020819052604090206001015461155081611a9d565b610c238383612168565b6000805160206149c283398151915261157281611a9d565b600280546001600160a01b038581166001600160a01b03199283168117909355600380549186169190921681179091556040805192835260208301919091527f323c4c64ab73ed697a6c978f74508faec97aed0cdf6f5f0ed8d93b8d22dd5f18910161092e565b6000805160206149c28339815191526115f181611a9d565b600061160360a08401608085016146b8565b61ffff1661161760808501606086016146b8565b61ffff1661162b60608601604087016146b8565b61ffff1661163f60408701602088016146b8565b61ffff1661165060208801886146b8565b61ffff1661165e9190614434565b6116689190614434565b6116729190614434565b61167c9190614434565b905061271081146116a3576040516309a1b40560e11b815260048101829052602401610e30565b8260056116b082826146e2565b9050507f47120d9be00347ec103aef54ade7d2b046cc8fe05545a3d894e1d9f26fa7ebaf8360405161092e91906147b9565b60006109c6612623565b60008060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c45a01556040518163ffffffff1660e01b8152600401602060405180830381865afa15801561174f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117739190614832565b60405163e6a4390560e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301527f000000000000000000000000000000000000000000000000000000000000000081166024830152919091169063e6a4390590604401602060405180830381865afa158015611803573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118279190614832565b90506001600160a01b03811661184257506000928392509050565b6040516308b8dbf160e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906308b8dbf1906118ba9084907f000000000000000000000000000000000000000000000000000000000000000090670de0b6b3a76400009060040161445e565b602060405180830381865afa9250505080156118f3575060408051601f3d908101601f191682019092526118f09181019061484f565b60015b61190257506000928392509050565b8015611915575060009360019350915050565b60405163d69a9a9960e01b81526000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063d69a9a99906119909086907f000000000000000000000000000000000000000000000000000000000000000090670de0b6b3a76400009060040161445e565b602060405180830381865afa1580156119ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119d191906143db565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663636cfa0c857f0000000000000000000000000000000000000000000000000000000000000000670de0b6b3a76400006040518463ffffffff1660e01b8152600401611a4d9392919061445e565b602060405180830381865afa158015611a6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a8e91906143db565b90911095600195509350505050565b611aa78133612887565b50565b611ab2614171565b6003546001600160a01b0316801580611ad357506001600160a01b0381163b155b15611b2f5750506040805160a08101825260055461ffff808216835262010000820481166020840152600160201b82048116938301939093526601000000000000810483166060830152600160401b9004909116608082015290565b600080600080846001600160a01b03166388a17bde6040518163ffffffff1660e01b8152600401608060405180830381865afa158015611b73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b979190614871565b6040805160a08101825261ffff95861681529385166020850152600090840152908316606083015290911660808201529695505050505050565b600080611bdd83610c61565b600854909150600090611bfc90600160201b900461ffff166002614447565b600854909150600160201b900461ffff161580611c1857508082105b611c225780611c24565b815b949350505050565b6000838302816000198587098281108382030391505080600003611c6357838281611c5957611c596143f4565b049250505061086f565b808411611c7a57611c7a60038515026011186128c4565b6000848688096000868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150509392505050565b6000806001600160a01b038316611d0357506000928392509050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c45a01556040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d61573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d859190614832565b60405163e6a4390560e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301528581166024830152919091169063e6a4390590604401602060405180830381865afa158015611df5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e199190614832565b91506001600160a01b038216611e3457506000928392509050565b600080836001600160a01b0316630902f1ac6040518163ffffffff1660e01b8152600401606060405180830381865afa158015611e75573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e999190614499565b5091509150816001600160701b031660001480611ebd57506001600160701b038116155b15611ecf575060009485945092505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b0316630dfe16816040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f37573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f5b9190614832565b6001600160a01b031614611f6f5780611f71565b815b6001600160701b031692505050915091565b600060018211611f91575090565b816001600160801b8210611faa5760809190911c9060401b5b600160401b8210611fc05760409190911c9060201b5b600160201b8210611fd65760209190911c9060101b5b620100008210611feb5760109190911c9060081b5b6101008210611fff5760089190911c9060041b5b601082106120125760049190911c9060021b5b6004821061201e5760011b5b600302600190811c90818581612036576120366143f4565b048201901c9050600181858161204e5761204e6143f4565b048201901c90506001818581612066576120666143f4565b048201901c9050600181858161207e5761207e6143f4565b048201901c90506001818581612096576120966143f4565b048201901c905060018185816120ae576120ae6143f4565b048201901c90506120cd8185816120c7576120c76143f4565b04821190565b90039392505050565b60006120e28383611422565b612160576000838152602081815260408083206001600160a01b03861684529091529020805460ff191660011790556121183390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a450600161085e565b50600061085e565b60006121748383611422565b15612160576000838152602081815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a450600161085e565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b038316158015906122a857506040516001600160a01b038416602482015261225390829060440160408051601f198184030181529190526020810180516001600160e01b031663ad5dff7360e01b1790526128d6565b1515806122a857506040516001600160a01b03841660248201526122a590829060440160408051601f198184030181529190526020810180516001600160e01b03166301fa10c760e51b1790526128d6565b15155b156122b65750600092915050565b6040805160048152602481019091526020810180516001600160e01b03166324a9d85360e01b17905261086f9082906128d6565b60026001540361230d57604051633ee5aeb560e01b815260040160405180910390fd5b6002600155565b600061231e614171565b60008060075485116123305784612334565b6007545b935061233e61296e565b9092509050600061235685612351611aaa565b612a21565b6006549091508110801590612369578195505b612374868585612b1c565b9450806123875761238485612c59565b94505b50509193509193565b612398614142565b8151612710906123ac9061ffff1685614447565b6123b69190614420565b81526020820151612710906123cf9061ffff1685614447565b6123d99190614420565b60208201526040820151612710906123f59061ffff1685614447565b6123ff9190614420565b604082015260608201516127109061241b9061ffff1685614447565b6124259190614420565b6060820181905260408201516020830151835161244290876146a5565b61244c91906146a5565b61245691906146a5565b61246091906146a5565b608082015292915050565b612473614142565b81511561248a57815161248590612cc1565b815181525b602082015115612536576000806124c57f00000000000000000000000000000000000000000000000000000000000000008560200151612e13565b91509150811561253357836020015183602001818152505080600d60008282546124ef9190614434565b9091555050602084810151604080519182529181018390527feb4cbd166bc95c06297e9935f834af8b7948f1af87e4d1e5223176b2840b0a04910160405180910390a15b50505b6040820151156125d05760045460408301516000918291612560916001600160a01b031690612e13565b9150915081156125cd57836040015183604001818152505080600b600082825461258a9190614434565b90915550506040848101518151908152602081018390527f287af9e5c1e5f091114bc125285626f025f1dcdab3a70ae60cf93e5b90bb518e910160405180910390a15b50505b600082606001511180156125ec57506125ec82606001516130c0565b156125fc57606080830151908201525b60808201511561261e5761261382608001516138a8565b608080830151908201525b919050565b6000806114f47f0000000000000000000000000000000000000000000000000000000000000000611ce7565b600080600061265d85611ce7565b5090506001600160a01b03811661267b576000809250925050612855565b6040516308b8dbf160e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906308b8dbf1906126eb9084907f000000000000000000000000000000000000000000000000000000000000000090899060040161445e565b602060405180830381865afa925050508015612724575060408051601f3d908101601f191682019092526127219181019061484f565b60015b612735576000809250925050612855565b8061274857600080935093505050612855565b506040516318db3e8360e21b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063636cfa0c906127b99084907f000000000000000000000000000000000000000000000000000000000000000090899060040161445e565b602060405180830381865afa9250505080156127f2575060408051601f3d908101601f191682019092526127ef918101906143db565b60015b612803576000809250925050612855565b8060000361281957600080935093505050612855565b600854612710906128349062010000900461ffff16826148d0565b6128429061ffff1683614447565b61284c9190614420565b60019350935050505b9250929050565b600454600a5460009161085e916001600160a01b03909116908490600160201b900461ffff16613a16565b6128918282611422565b6128c05760405163e2517d3f60e01b81526001600160a01b038216600482015260248101839052604401610e30565b5050565b634e487b71600052806020526024601cfd5b6000806000846001600160a01b0316846040516128f391906148ea565b600060405180830381855afa9150503d806000811461292e576040519150601f19603f3d011682016040523d82523d6000602084013e612933565b606091505b5091509150818015612946575080516020145b612951576000612965565b8080602001905181019061296591906143db565b95945050505050565b60025460009081906001600160a01b03168061298f57506000928392509050565b806001600160a01b03163b6000036129ad5750600092600192509050565b806001600160a01b031663efedc6696040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015612a07575060408051601f3d908101601f19168201909252612a04918101906143db565b60015b612a175750600092600192509050565b9360019350915050565b6000806002836060015161ffff16612a399190614420565b836020015161ffff16612a4c9190614434565b905080600003612a5f578391505061085e565b6000612a8a7f0000000000000000000000000000000000000000000000000000000000000000610a5c565b905080600003612ac157612abd7f0000000000000000000000000000000000000000000000000000000000000000611ce7565b9150505b80600003612ad357849250505061085e565b6000612b0d82612b027f0000000000000000000000000000000000000000000000000000000000000000611bd1565b610a50866002614447565b90508581106129655785610bf4565b612b24614171565b612b2c611aaa565b905081612b4d57612b4684612b418684613a8f565b613bca565b905061086f565b600954831015612bd757600060028260200151612b6a9190614919565b90508082602001818151612b7e91906148d0565b61ffff169052506000612b92600283614919565b90508083606001818151612ba6919061493d565b61ffff16905250612bb781836148d0565b83608001818151612bc8919061493d565b61ffff16905250612c43915050565b612bdf613cae565b15612c4357600a54602082015160009161271091612c049161ffff9081169116614447565b612c0e9190614420565b90508082602001818151612c2291906148d0565b61ffff16905250815181908390612c3a90839061493d565b61ffff16905250505b612c4d8482613a8f565b9050611c248482613bca565b612c61614171565b81604001518260200151612c75919061493d565b82518390612c8490839061493d565b61ffff1690525060006020830181905260408301526060820151608083018051612caf90839061493d565b61ffff16905250506000606082015290565b604051630852cd8d60e31b8152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906342966c6890602401600060405180830381600087803b158015612d2357600080fd5b505af1158015612d37573d6000803e3d6000fd5b5050505080600c6000828254612d4d9190614434565b925050819055507f2edd0da3c5952f58d6d0bf58362bfa1ad3aecbb2da431ddc307803b28c4e1b9c817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612dd4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612df891906143db565b6040805192835260208301919091520160405180910390a150565b6040805160028082526060820183526000928392839290916020830190803683370190505090507f000000000000000000000000000000000000000000000000000000000000000081600081518110612e6e57612e6e614564565b60200260200101906001600160a01b031690816001600160a01b0316815250508481600181518110612ea257612ea2614564565b60200260200101906001600160a01b031690816001600160a01b0316815250506000612ece8286613cce565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038816906370a0823190602401602060405180830381865afa158015612f18573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f3c91906143db565b9050612f926001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000088613e3f565b612f9e87878486613efd565b9450612ff56001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f00000000000000000000000000000000000000000000000000000000000000006000613e3f565b846130095760008094509450505050612855565b6040516370a0823160e01b815230600482015281906001600160a01b038916906370a0823190602401602060405180830381865afa15801561304f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061307391906143db565b61307d91906146a5565b9350836000036130a05760405163b5b4985560e01b815260040160405180910390fd5b6130b66001600160a01b03881661dead86614026565b5050509250929050565b6000806130ce600284614420565b905060006130dc82856146a5565b60408051600280825260608201835292935060009290916020830190803683370190505090507f00000000000000000000000000000000000000000000000000000000000000008160008151811061313657613136614564565b60200260200101906001600160a01b031690816001600160a01b0316815250507f00000000000000000000000000000000000000000000000000000000000000008160018151811061318a5761318a614564565b6001600160a01b0392831660209182029290920101526040516370a0823160e01b81523060048201526000917f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa1580156131ff573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061322391906143db565b90506132796001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000086613e3f565b6132ae7f0000000000000000000000000000000000000000000000000000000000000000856132a88588613cce565b85613efd565b9450846133165761330a6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f00000000000000000000000000000000000000000000000000000000000000006000613e3f565b50600095945050505050565b6040516370a0823160e01b815230600482015260009082906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa15801561337f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133a391906143db565b6133ad91906146a5565b9050806000036133d05760405163b5b4985560e01b815260040160405180910390fd5b6134246001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000086613e3f565b6134786001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000083613e3f565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663e8e337007f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000088866134f68b614057565b6134ff89614057565b60405160e088901b6001600160e01b03191681526001600160a01b039687166004820152948616602486015260448501939093526064840191909152608483015260a48201527f000000000000000000000000000000000000000000000000000000000000000090911660c48201524260e4820152610104016060604051808303816000875af1158015613597573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135bb9190614957565b92506136159150506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f00000000000000000000000000000000000000000000000000000000000000006000613e3f565b61366a6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f00000000000000000000000000000000000000000000000000000000000000006000613e3f565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c45a01556040518163ffffffff1660e01b8152600401602060405180830381865afa1580156136ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136ee9190614832565b60405163e6a4390560e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301527f000000000000000000000000000000000000000000000000000000000000000081166024830152919091169063e6a4390590604401602060405180830381865afa15801561377e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137a29190614832565b90506001600160a01b038116158015906137bc5750600082115b156138445760405163dc850b3d60e01b81526001600160a01b038281166004830152602482018490527f0000000000000000000000000000000000000000000000000000000000000000169063dc850b3d90604401600060405180830381600087803b15801561382b57600080fd5b505af115801561383f573d6000803e3d6000fd5b505050505b88600e60008282546138569190614434565b909155505060408051878152602081018590529081018390527fbcdc45cfc4c2863aeb5b434c4521091f79995db2fbfaf5706840cf370f2a533e9060600160405180910390a150505050505050919050565b6138fc6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000083614026565b604051630f2bc5d760e11b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018390527f00000000000000000000000000000000000000000000000000000000000000001690631e578bae90604401600060405180830381600087803b15801561398657600080fd5b505af115801561399a573d6000803e3d6000fd5b5050505080600f60008282546139b09190614434565b90915550506040518181526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016907fda1785fe1dec267c363b06ae3e02c3e086123f9d611399f7a1cc98492518372e9060200160405180910390a250565b600082600003613a285750600061086f565b600080613a3486611ce7565b90925090506001600160a01b038216613a525760009250505061086f565b8361ffff16600003613a695760019250505061086f565b613a7761ffff851682614447565b613a8361271087614447565b11159695505050505050565b613a97614171565b600a54602083015160009161271091613abd9162010000900461ffff9081169116614447565b613ac79190614420565b905061ffff81161580613ad8575083155b15613ae6578291505061085e565b600454600090859061271090613b04906001600160a01b0316611506565b613b0e9190614447565b613b189190614420565b90508161ffff16811015613b2a578091505b8161ffff16600003613b4057839250505061085e565b6000612710613b5361ffff851688614447565b613b5d9190614420565b9050613b688161285c565b613b775784935050505061085e565b600454613b8d906001600160a01b0316826110a1565b613b9c5784935050505061085e565b8285602001818151613bae91906148d0565b61ffff9081169091529390931660408601525092949350505050565b613bd2614171565b602082015160009061271090613bec9061ffff1686614447565b613bf69190614420565b90506000600261271061ffff16856060015161ffff1687613c179190614447565b613c219190614420565b613c2b9190614420565b90506000613c37612623565b90506000613c458385614434565b90506000811580613c7b5750613c7b7f0000000000000000000000000000000000000000000000000000000000000000836110a1565b9050828015613c875750805b15613c9957869550505050505061085e565b613ca287612c59565b98975050505050505050565b6000806000613cbb6116ec565b91509150808015610a5557509092915050565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d06ca61f84866040518363ffffffff1660e01b8152600401613d1f9291906145bf565b600060405180830381865afa158015613d3c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613d6491908101906145d8565b90506000613d978260018451613d7a91906146a5565b81518110613d8a57613d8a614564565b6020026020010151614057565b9050600080613dcc8760018951613dae91906146a5565b81518110613dbe57613dbe614564565b60200260200101518761264f565b9150915080613e25578660018851613de491906146a5565b81518110613df457613df4614564565b6020026020010151604051630817760b60e11b8152600401610e3091906001600160a01b0391909116815260200190565b828211613e325782613e34565b815b979650505050505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052613e908482614087565b610c23576040516001600160a01b03848116602483015260006044830152613ef391869182169063095ea7b3906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506140d1565b610c2384826140d1565b6000614e20603f613f12620f42406040614447565b613f1c9190614420565b613f269190614434565b5a1015613f46576040516303f72b9f60e21b815260040160405180910390fd5b604051635c11d79560e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690635c11d79590620f424090613f9f9088908890889030904290600401614985565b600060405180830381600088803b158015613fb957600080fd5b5087f193505050508015613fcb575060015b61401b5760408051858152602081018590526001600160a01b038716917faca3a2d1e741e999653f8f356d17afda6e54ef430b374580735dd3acd2396b2b910160405180910390a2506000611c24565b506001949350505050565b6040516001600160a01b03838116602483015260448201839052610c5c91859182169063a9059cbb90606401613ec1565b6008546000906127109061406f9061ffff16826148d0565b61407d9061ffff1684614447565b61085e9190614420565b6000806000806020600086516020880160008a5af192503d91506000519050828015610bf4575081156140bd5780600114610bf4565b50505050506001600160a01b03163b151590565b600080602060008451602086016000885af1806140f4576040513d6000823e3d81fd5b50506000513d9150811561410c578060011415614119565b6001600160a01b0384163b155b15610c2357604051635274afe760e01b81526001600160a01b0385166004820152602401610e30565b6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915290565b6000602082840312156141b157600080fd5b81356001600160e01b03198116811461086f57600080fd5b61ffff81168114611aa757600080fd5b600080604083850312156141ec57600080fd5b82356141f7816141c9565b91506020830135614207816141c9565b809150509250929050565b60006020828403121561422457600080fd5b5035919050565b6001600160a01b0381168114611aa757600080fd5b60006020828403121561425257600080fd5b813561086f8161422b565b6000806040838503121561427057600080fd5b8235915060208301356142078161422b565b60008060006060848603121561429757600080fd5b833592506020840135915060408401356142b0816141c9565b809150509250925092565b60a0810161085e828480518252602081015160208301526040810151604083015260608101516060830152608081015160808301525050565b6000806040838503121561430757600080fd5b82356143128161422b565b946020939093013593505050565b83815260e0810161435f602083018580518252602081015160208301526040810151604083015260608101516060830152608081015160808301525050565b8260c0830152949350505050565b6000806040838503121561438057600080fd5b823591506020830135614207816141c9565b600080604083850312156143a557600080fd5b82356143b08161422b565b915060208301356142078161422b565b600060a08284031280156143d357600080fd5b509092915050565b6000602082840312156143ed57600080fd5b5051919050565b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60008261442f5761442f6143f4565b500490565b8082018082111561085e5761085e61440a565b808202811582820484141761085e5761085e61440a565b6001600160a01b039384168152919092166020820152604081019190915260600190565b80516001600160701b038116811461261e57600080fd5b6000806000606084860312156144ae57600080fd5b6144b784614482565b92506144c560208501614482565b9150604084015163ffffffff811681146142b057600080fd5b878152610160810161451e602083018980518252602081015160208301526040810151604083015260608101516060830152608081015160808301525050565b60c082019690965293151560e0850152911515610100840152151561012083015215156101409091015292915050565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b600081518084526020840193506020830160005b828110156145b55781516001600160a01b031686526020958601959091019060010161458e565b5093949350505050565b828152604060208201526000611c24604083018461457a565b6000602082840312156145ea57600080fd5b815167ffffffffffffffff81111561460157600080fd5b8201601f8101841361461257600080fd5b805167ffffffffffffffff81111561462c5761462c61454e565b8060051b604051601f19603f830116810181811067ffffffffffffffff821117156146595761465961454e565b60405291825260208184018101929081018784111561467757600080fd5b6020850194505b8385101561469a5784518082526020958601959093500161467e565b509695505050505050565b8181038181111561085e5761085e61440a565b6000602082840312156146ca57600080fd5b813561086f816141c9565b6000813561085e816141c9565b81356146ed816141c9565b61ffff8116905081548161ffff198216178355602084013561470e816141c9565b63ffff00008160101b168363ffffffff198416171784555050506040820135614736816141c9565b815465ffff00000000191660209190911b65ffff000000001617815560006060830135614762816141c9565b825467ffff0000000000001916603082901b67ffff000000000000161783559050506128c0614793608084016146d5565b825469ffff0000000000000000191660409190911b69ffff000000000000000016178255565b60a0810182356147c8816141c9565b61ffff16825260208301356147dc816141c9565b61ffff16602083015260408301356147f3816141c9565b61ffff166040830152606083013561480a816141c9565b61ffff1660608301526080830135614821816141c9565b61ffff811660808401525092915050565b60006020828403121561484457600080fd5b815161086f8161422b565b60006020828403121561486157600080fd5b8151801515811461086f57600080fd5b6000806000806080858703121561488757600080fd5b8451614892816141c9565b60208601519094506148a3816141c9565b60408601519093506148b4816141c9565b60608601519092506148c5816141c9565b939692955090935050565b61ffff828116828216039081111561085e5761085e61440a565b6000825160005b8181101561490b57602081860181015185830152016148f1565b506000920191825250919050565b600061ffff83168061492d5761492d6143f4565b8061ffff84160491505092915050565b61ffff818116838216019081111561085e5761085e61440a565b60008060006060848603121561496c57600080fd5b5050815160208301516040909301519094929350919050565b85815284602082015260a0604082015260006149a460a083018661457a565b6001600160a01b039490941660608301525060800152939250505056fe17d57ed1a1d10c36bff7484e6bb95371a28422a7baabf93343b1965f53f83fa0a264697066735822122068ef80461e91b18392ef13fc489a269e82c7ea22481d5c002660ff5a4b56472d64736f6c634300081a00330000000000000000000000008feb03a1590c041a5ae7d09ff326919129b6f785000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f01058000000000000000000000000b7d7d4cc36ff190d2dca2cd618a71f3574688dd0000000000000000000000000e1dedf16b298e3532fb551c21710e23d3b0a28b7000000000000000000000000dae9dd3d1a52cfce9d5f2fac7fde164d500e50f70000000000000000000000005c2598630691e3c51bc17d94119ea990603f4561
Deployed ByteCode
0x608060405234801561001057600080fd5b50600436106103835760003560e01c8063750f198a116101de578063c3b7df511161010f578063d74a5d00116100ad578063f69f84ee1161007c578063f69f84ee146107ef578063f8f64a3b146107f8578063f957cd611461080c578063fb63ee181461081f57600080fd5b8063d74a5d00146107b8578063e1a45218146107cb578063e5462864146107d4578063eafdbb23146107e757600080fd5b8063ca4325c5116100e9578063ca4325c51461077f578063cd61895814610788578063cd6c78731461079d578063d547741f146107a557600080fd5b8063c3b7df5114610755578063c4aa739514610768578063c4c257241461077657600080fd5b806384ccc7421161017c57806399e64b0c1161015657806399e64b0c1461071f578063a217fddf14610732578063a8419d581461073a578063b75358fe1461074257600080fd5b806384ccc742146106d2578063900cf0cf146106f957806391d148541461070c57600080fd5b80637b8fd616116101b85780637b8fd6161461067a5780637b97cbba1461068d5780637dc0d1d0146106965780637e9ec2b9146106bd57600080fd5b8063750f198a146105ea57806375a119ab146105f4578063793216441461060b57600080fd5b80633395bdc1116102b85780634fe369db11610256578063691afd6311610230578063691afd63146105b0578063695dd5e2146105c557806369f2b0a6146105d857806372e1ebd6146105e157600080fd5b80634fe369db14610561578063614619541461057457806361d027b31461058957600080fd5b8063399e79da11610292578063399e79da146105295780633df0f99414610532578063498d0313146105455780634bf1ca611461054e57600080fd5b80633395bdc1146104e6578063364bc15a146104ef57806336568abe1461051657600080fd5b806316653a371161032557806327227277116102ff578063272272771461049157806329f780b8146104995780632d505365146104c05780632f2ff15d146104d357600080fd5b806316653a3714610451578063224438d114610466578063248a9ca31461046e57600080fd5b80630758d924116103615780630758d924146103d9578063084d91b51461040057806309f4b0c3146104275780631417cd361461043e57600080fd5b806301ffc9a71461038857806303fd2a45146103b057806304d02328146103d1575b600080fd5b61039b61039636600461419f565b61082d565b60405190151581526020015b60405180910390f35b6103b961dead81565b6040516001600160a01b0390911681526020016103a7565b61039b610864565b6103b97f000000000000000000000000dae9dd3d1a52cfce9d5f2fac7fde164d500e50f781565b6103b97f000000000000000000000000b7d7d4cc36ff190d2dca2cd618a71f3574688dd081565b61043060065481565b6040519081526020016103a7565b6004546103b9906001600160a01b031681565b61046461045f3660046141d9565b610876565b005b61043061093b565b61043061047c366004614212565b60009081526020819052604090206001015490565b6104306109cb565b600a546104ad9062010000900461ffff1681565b60405161ffff90911681526020016103a7565b6104306104ce366004614240565b610a5c565b6104646104e136600461425d565b610bfe565b6104ad6101f481565b6104307ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab81565b61046461052436600461425d565b610c29565b610430600e5481565b610430610540366004614240565b610c61565b6104ad6103e881565b61046461055c366004614282565b610c84565b61046461056f366004614240565b610d3d565b61057c610e8c565b6040516103a791906142bb565b6103b97f000000000000000000000000e1dedf16b298e3532fb551c21710e23d3b0a28b781565b6104306000805160206149c283398151915281565b61039b6105d33660046142f4565b6110a1565b610430600b5481565b610430600d5481565b610430620f424081565b6105fc611242565b6040516103a793929190614320565b6005546106459061ffff80821691620100008104821691600160201b820481169166010000000000008104821691600160401b9091041685565b6040805161ffff968716815294861660208601529285169284019290925283166060830152909116608082015260a0016103a7565b6104646106883660046141d9565b611302565b610430600f5481565b6103b97f0000000000000000000000005c2598630691e3c51bc17d94119ea990603f456181565b600a546104ad90600160201b900461ffff1681565b6103b97f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f0105881565b6003546103b9906001600160a01b031681565b61039b61071a36600461425d565b611422565b61046461072d36600461436d565b61144b565b610430600081565b61039b6114da565b610430610750366004614240565b611506565b61039b610763366004614212565b61152a565b6008546104ad9061ffff1681565b610430600c5481565b61043060075481565b6008546104ad90600160201b900461ffff1681565b6104ad601d81565b6104646107b336600461425d565b611535565b6104646107c6366004614392565b61155a565b6104ad61271081565b6104646107e23660046143c0565b6115d9565b61039b6116e2565b61043060095481565b6008546104ad9062010000900461ffff1681565b6002546103b9906001600160a01b031681565b600a546104ad9061ffff1681565b60006001600160e01b03198216637965db0b60e01b148061085e57506301ffc9a760e01b6001600160e01b03198316145b92915050565b60008061086f6116ec565b9392505050565b6000805160206149c283398151915261088e81611a9d565b61271061ffff841611806108a7575061271061ffff8316115b156108c55760405163a883435760e01b815260040160405180910390fd5b600a805465ffffffff000019166201000061ffff86811691820265ffff00000000191692909217600160201b928616928302179092556040805192835260208301919091527f4a484c9dd677725a26fef1124046bb1452cbd053ff1d766a5dc5c7a6f3b9fc7391015b60405180910390a1505050565b6040516370a0823160e01b81523060048201526000907f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f010586001600160a01b0316906370a0823190602401602060405180830381865afa1580156109a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109c691906143db565b905090565b6000806109d6611aaa565b905060006002826060015161ffff166109ef9190614420565b826020015161ffff16610a029190614434565b905080600003610a155760009250505090565b600654610a5590610a27836002614447565b610a507f000000000000000000000000b7d7d4cc36ff190d2dca2cd618a71f3574688dd0611bd1565b611c2c565b9250505090565b600080610a6883611ce7565b5090506001600160a01b038116610a825750600092915050565b6040516318db3e8360e21b81526001600160a01b037f0000000000000000000000005c2598630691e3c51bc17d94119ea990603f4561169063636cfa0c90610afa9084907f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f0105890670de0b6b3a76400009060040161445e565b602060405180830381865afa925050508015610b33575060408051601f3d908101601f19168201909252610b30918101906143db565b60015b610b405750600092915050565b80600003610b52575060009392505050565b600080836001600160a01b0316630902f1ac6040518163ffffffff1660e01b8152600401606060405180830381865afa158015610b93573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bb79190614499565b5091509150610bf4610bef826001600160701b0316846001600160701b0316610be09190614447565b670de0b6b3a764000086611c2c565b611f83565b9695505050505050565b600082815260208190526040902060010154610c1981611a9d565b610c2383836120d6565b50505050565b6001600160a01b0381163314610c525760405163334bd91960e11b815260040160405180910390fd5b610c5c8282612168565b505050565b600080610c6d83611ce7565b509050610c79816121d3565b61086f90601d614434565b6000805160206149c2833981519152610c9c81611a9d565b831580610ca857508383105b80610cb9575061271061ffff831610155b15610cd75760405163a883435760e01b815260040160405180910390fd5b600684905560078390556008805461ffff191661ffff84169081179091556040805186815260208101869052908101919091527ffc21bee290f9c27f1f68ad03795732385f34a4b52fc51a08a11dedc3dd6707749060600160405180910390a150505050565b6000805160206149c2833981519152610d5581611a9d565b7f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f010586001600160a01b0316826001600160a01b03161480610dc657507f000000000000000000000000b7d7d4cc36ff190d2dca2cd618a71f3574688dd06001600160a01b0316826001600160a01b0316145b15610de45760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b03821615801590610e0b5750610e0982670de0b6b3a76400006110a1565b155b15610e3957604051630817760b60e11b81526001600160a01b03831660048201526024015b60405180910390fd5b600480546001600160a01b038481166001600160a01b0319831681179093556040519116919082907f932cafc1bfa395e33c078c0972247df1f26efb8a839a77d3c092c298522a175d90600090a3505050565b610e94614142565b7ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab610ebe81611a9d565b610ec66122ea565b6040516370a0823160e01b81523060048201526000907f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f010586001600160a01b0316906370a0823190602401602060405180830381865afa158015610f2d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f5191906143db565b9050600654811015610f845760065460405163bd49034f60e01b8152610e30918391600401918252602082015260400190565b600080600080610f9385612314565b9350935093509350610fa58484612390565b965060008760800151886060015189604001518a602001518b60000151610fcc9190614434565b610fd69190614434565b610fe09190614434565b610fea9190614434565b90508481146110165760405163080d3f8160e21b81526004810182905260248101869052604401610e30565b61101f8861246b565b9750600061102b6116ec565b915050336001600160a01b03167f17930e2d94715b959eca76104a52a13b210ac876fed91c03520cf366b32a00ce878b87878660008c6040015161ffff1611611072612623565b60405161108597969594939291906144de565b60405180910390a25050505050505061109d60018055565b5090565b60008060006110b0858561264f565b91509150806110c45760009250505061085e565b836000036110d75760019250505061085e565b6040805160028082526060820183526000926020830190803683370190505090507f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f010588160008151811061112c5761112c614564565b60200260200101906001600160a01b031690816001600160a01b031681525050858160018151811061116057611160614564565b6001600160a01b03928316602091820292909201015260405163d06ca61f60e01b81527f000000000000000000000000dae9dd3d1a52cfce9d5f2fac7fde164d500e50f79091169063d06ca61f906111be90889085906004016145bf565b600060405180830381865afa9250505080156111fc57506040513d6000823e601f3d908101601f191682016040526111f991908101906145d8565b60015b61120c576000935050505061085e565b83816001835161121c91906146a5565b8151811061122c5761122c614564565b6020026020010151101594505050505092915050565b600061124c614142565b6000611256614171565b6040516370a0823160e01b81523060048201526112e7907f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f010586001600160a01b0316906370a0823190602401602060405180830381865afa1580156112be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112e291906143db565b612314565b5091955090925090506112fa8482612390565b925050909192565b6000805160206149c283398151915261131a81611a9d565b61ffff8316158061133057506103e861ffff8416115b1561134e5760405163a883435760e01b815260040160405180910390fd5b61ffff8216158061136457506101f461ffff8316115b156113825760405163a883435760e01b815260040160405180910390fd5b61ffff8084169061139790601d908516614434565b106113b55760405163a883435760e01b815260040160405180910390fd5b6008805465ffffffff000019166201000061ffff86811691820265ffff00000000191692909217600160201b928616928302179092556040805192835260208301919091527fc28a52bd1071628b2a7a0fbb85d8a917655ae4522a0f9cf3078dffe999d1fd6d910161092e565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b6000805160206149c283398151915261146381611a9d565b61271061ffff8316111561148a5760405163a883435760e01b815260040160405180910390fd5b6009839055600a805461ffff191661ffff84169081179091556040805185815260208101929092527f6a4622eb76f2b4cbec83896009e96777a569df6bb9bf79d2e0db8f0d07869b37910161092e565b60045460009081906114f4906001600160a01b0316611ce7565b506001600160a01b0316151592915050565b600061085e61151483610a5c565b61151d84611bd1565b610a506127106002614447565b600061085e8261285c565b60008281526020819052604090206001015461155081611a9d565b610c238383612168565b6000805160206149c283398151915261157281611a9d565b600280546001600160a01b038581166001600160a01b03199283168117909355600380549186169190921681179091556040805192835260208301919091527f323c4c64ab73ed697a6c978f74508faec97aed0cdf6f5f0ed8d93b8d22dd5f18910161092e565b6000805160206149c28339815191526115f181611a9d565b600061160360a08401608085016146b8565b61ffff1661161760808501606086016146b8565b61ffff1661162b60608601604087016146b8565b61ffff1661163f60408701602088016146b8565b61ffff1661165060208801886146b8565b61ffff1661165e9190614434565b6116689190614434565b6116729190614434565b61167c9190614434565b905061271081146116a3576040516309a1b40560e11b815260048101829052602401610e30565b8260056116b082826146e2565b9050507f47120d9be00347ec103aef54ade7d2b046cc8fe05545a3d894e1d9f26fa7ebaf8360405161092e91906147b9565b60006109c6612623565b60008060007f000000000000000000000000dae9dd3d1a52cfce9d5f2fac7fde164d500e50f76001600160a01b031663c45a01556040518163ffffffff1660e01b8152600401602060405180830381865afa15801561174f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117739190614832565b60405163e6a4390560e01b81526001600160a01b037f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f01058811660048301527f000000000000000000000000b7d7d4cc36ff190d2dca2cd618a71f3574688dd081166024830152919091169063e6a4390590604401602060405180830381865afa158015611803573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118279190614832565b90506001600160a01b03811661184257506000928392509050565b6040516308b8dbf160e01b81526001600160a01b037f0000000000000000000000005c2598630691e3c51bc17d94119ea990603f456116906308b8dbf1906118ba9084907f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f0105890670de0b6b3a76400009060040161445e565b602060405180830381865afa9250505080156118f3575060408051601f3d908101601f191682019092526118f09181019061484f565b60015b61190257506000928392509050565b8015611915575060009360019350915050565b60405163d69a9a9960e01b81526000906001600160a01b037f0000000000000000000000005c2598630691e3c51bc17d94119ea990603f4561169063d69a9a99906119909086907f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f0105890670de0b6b3a76400009060040161445e565b602060405180830381865afa1580156119ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119d191906143db565b905060007f0000000000000000000000005c2598630691e3c51bc17d94119ea990603f45616001600160a01b031663636cfa0c857f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f01058670de0b6b3a76400006040518463ffffffff1660e01b8152600401611a4d9392919061445e565b602060405180830381865afa158015611a6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a8e91906143db565b90911095600195509350505050565b611aa78133612887565b50565b611ab2614171565b6003546001600160a01b0316801580611ad357506001600160a01b0381163b155b15611b2f5750506040805160a08101825260055461ffff808216835262010000820481166020840152600160201b82048116938301939093526601000000000000810483166060830152600160401b9004909116608082015290565b600080600080846001600160a01b03166388a17bde6040518163ffffffff1660e01b8152600401608060405180830381865afa158015611b73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b979190614871565b6040805160a08101825261ffff95861681529385166020850152600090840152908316606083015290911660808201529695505050505050565b600080611bdd83610c61565b600854909150600090611bfc90600160201b900461ffff166002614447565b600854909150600160201b900461ffff161580611c1857508082105b611c225780611c24565b815b949350505050565b6000838302816000198587098281108382030391505080600003611c6357838281611c5957611c596143f4565b049250505061086f565b808411611c7a57611c7a60038515026011186128c4565b6000848688096000868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150509392505050565b6000806001600160a01b038316611d0357506000928392509050565b7f000000000000000000000000dae9dd3d1a52cfce9d5f2fac7fde164d500e50f76001600160a01b031663c45a01556040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d61573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d859190614832565b60405163e6a4390560e01b81526001600160a01b037f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f01058811660048301528581166024830152919091169063e6a4390590604401602060405180830381865afa158015611df5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e199190614832565b91506001600160a01b038216611e3457506000928392509050565b600080836001600160a01b0316630902f1ac6040518163ffffffff1660e01b8152600401606060405180830381865afa158015611e75573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e999190614499565b5091509150816001600160701b031660001480611ebd57506001600160701b038116155b15611ecf575060009485945092505050565b7f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f010586001600160a01b0316846001600160a01b0316630dfe16816040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f37573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f5b9190614832565b6001600160a01b031614611f6f5780611f71565b815b6001600160701b031692505050915091565b600060018211611f91575090565b816001600160801b8210611faa5760809190911c9060401b5b600160401b8210611fc05760409190911c9060201b5b600160201b8210611fd65760209190911c9060101b5b620100008210611feb5760109190911c9060081b5b6101008210611fff5760089190911c9060041b5b601082106120125760049190911c9060021b5b6004821061201e5760011b5b600302600190811c90818581612036576120366143f4565b048201901c9050600181858161204e5761204e6143f4565b048201901c90506001818581612066576120666143f4565b048201901c9050600181858161207e5761207e6143f4565b048201901c90506001818581612096576120966143f4565b048201901c905060018185816120ae576120ae6143f4565b048201901c90506120cd8185816120c7576120c76143f4565b04821190565b90039392505050565b60006120e28383611422565b612160576000838152602081815260408083206001600160a01b03861684529091529020805460ff191660011790556121183390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a450600161085e565b50600061085e565b60006121748383611422565b15612160576000838152602081815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a450600161085e565b60007f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f010586001600160a01b038316158015906122a857506040516001600160a01b038416602482015261225390829060440160408051601f198184030181529190526020810180516001600160e01b031663ad5dff7360e01b1790526128d6565b1515806122a857506040516001600160a01b03841660248201526122a590829060440160408051601f198184030181529190526020810180516001600160e01b03166301fa10c760e51b1790526128d6565b15155b156122b65750600092915050565b6040805160048152602481019091526020810180516001600160e01b03166324a9d85360e01b17905261086f9082906128d6565b60026001540361230d57604051633ee5aeb560e01b815260040160405180910390fd5b6002600155565b600061231e614171565b60008060075485116123305784612334565b6007545b935061233e61296e565b9092509050600061235685612351611aaa565b612a21565b6006549091508110801590612369578195505b612374868585612b1c565b9450806123875761238485612c59565b94505b50509193509193565b612398614142565b8151612710906123ac9061ffff1685614447565b6123b69190614420565b81526020820151612710906123cf9061ffff1685614447565b6123d99190614420565b60208201526040820151612710906123f59061ffff1685614447565b6123ff9190614420565b604082015260608201516127109061241b9061ffff1685614447565b6124259190614420565b6060820181905260408201516020830151835161244290876146a5565b61244c91906146a5565b61245691906146a5565b61246091906146a5565b608082015292915050565b612473614142565b81511561248a57815161248590612cc1565b815181525b602082015115612536576000806124c57f000000000000000000000000b7d7d4cc36ff190d2dca2cd618a71f3574688dd08560200151612e13565b91509150811561253357836020015183602001818152505080600d60008282546124ef9190614434565b9091555050602084810151604080519182529181018390527feb4cbd166bc95c06297e9935f834af8b7948f1af87e4d1e5223176b2840b0a04910160405180910390a15b50505b6040820151156125d05760045460408301516000918291612560916001600160a01b031690612e13565b9150915081156125cd57836040015183604001818152505080600b600082825461258a9190614434565b90915550506040848101518151908152602081018390527f287af9e5c1e5f091114bc125285626f025f1dcdab3a70ae60cf93e5b90bb518e910160405180910390a15b50505b600082606001511180156125ec57506125ec82606001516130c0565b156125fc57606080830151908201525b60808201511561261e5761261382608001516138a8565b608080830151908201525b919050565b6000806114f47f000000000000000000000000b7d7d4cc36ff190d2dca2cd618a71f3574688dd0611ce7565b600080600061265d85611ce7565b5090506001600160a01b03811661267b576000809250925050612855565b6040516308b8dbf160e01b81526001600160a01b037f0000000000000000000000005c2598630691e3c51bc17d94119ea990603f456116906308b8dbf1906126eb9084907f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f0105890899060040161445e565b602060405180830381865afa925050508015612724575060408051601f3d908101601f191682019092526127219181019061484f565b60015b612735576000809250925050612855565b8061274857600080935093505050612855565b506040516318db3e8360e21b81526001600160a01b037f0000000000000000000000005c2598630691e3c51bc17d94119ea990603f4561169063636cfa0c906127b99084907f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f0105890899060040161445e565b602060405180830381865afa9250505080156127f2575060408051601f3d908101601f191682019092526127ef918101906143db565b60015b612803576000809250925050612855565b8060000361281957600080935093505050612855565b600854612710906128349062010000900461ffff16826148d0565b6128429061ffff1683614447565b61284c9190614420565b60019350935050505b9250929050565b600454600a5460009161085e916001600160a01b03909116908490600160201b900461ffff16613a16565b6128918282611422565b6128c05760405163e2517d3f60e01b81526001600160a01b038216600482015260248101839052604401610e30565b5050565b634e487b71600052806020526024601cfd5b6000806000846001600160a01b0316846040516128f391906148ea565b600060405180830381855afa9150503d806000811461292e576040519150601f19603f3d011682016040523d82523d6000602084013e612933565b606091505b5091509150818015612946575080516020145b612951576000612965565b8080602001905181019061296591906143db565b95945050505050565b60025460009081906001600160a01b03168061298f57506000928392509050565b806001600160a01b03163b6000036129ad5750600092600192509050565b806001600160a01b031663efedc6696040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015612a07575060408051601f3d908101601f19168201909252612a04918101906143db565b60015b612a175750600092600192509050565b9360019350915050565b6000806002836060015161ffff16612a399190614420565b836020015161ffff16612a4c9190614434565b905080600003612a5f578391505061085e565b6000612a8a7f000000000000000000000000b7d7d4cc36ff190d2dca2cd618a71f3574688dd0610a5c565b905080600003612ac157612abd7f000000000000000000000000b7d7d4cc36ff190d2dca2cd618a71f3574688dd0611ce7565b9150505b80600003612ad357849250505061085e565b6000612b0d82612b027f000000000000000000000000b7d7d4cc36ff190d2dca2cd618a71f3574688dd0611bd1565b610a50866002614447565b90508581106129655785610bf4565b612b24614171565b612b2c611aaa565b905081612b4d57612b4684612b418684613a8f565b613bca565b905061086f565b600954831015612bd757600060028260200151612b6a9190614919565b90508082602001818151612b7e91906148d0565b61ffff169052506000612b92600283614919565b90508083606001818151612ba6919061493d565b61ffff16905250612bb781836148d0565b83608001818151612bc8919061493d565b61ffff16905250612c43915050565b612bdf613cae565b15612c4357600a54602082015160009161271091612c049161ffff9081169116614447565b612c0e9190614420565b90508082602001818151612c2291906148d0565b61ffff16905250815181908390612c3a90839061493d565b61ffff16905250505b612c4d8482613a8f565b9050611c248482613bca565b612c61614171565b81604001518260200151612c75919061493d565b82518390612c8490839061493d565b61ffff1690525060006020830181905260408301526060820151608083018051612caf90839061493d565b61ffff16905250506000606082015290565b604051630852cd8d60e31b8152600481018290527f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f010586001600160a01b0316906342966c6890602401600060405180830381600087803b158015612d2357600080fd5b505af1158015612d37573d6000803e3d6000fd5b5050505080600c6000828254612d4d9190614434565b925050819055507f2edd0da3c5952f58d6d0bf58362bfa1ad3aecbb2da431ddc307803b28c4e1b9c817f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f010586001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612dd4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612df891906143db565b6040805192835260208301919091520160405180910390a150565b6040805160028082526060820183526000928392839290916020830190803683370190505090507f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f0105881600081518110612e6e57612e6e614564565b60200260200101906001600160a01b031690816001600160a01b0316815250508481600181518110612ea257612ea2614564565b60200260200101906001600160a01b031690816001600160a01b0316815250506000612ece8286613cce565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038816906370a0823190602401602060405180830381865afa158015612f18573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f3c91906143db565b9050612f926001600160a01b037f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f01058167f000000000000000000000000dae9dd3d1a52cfce9d5f2fac7fde164d500e50f788613e3f565b612f9e87878486613efd565b9450612ff56001600160a01b037f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f01058167f000000000000000000000000dae9dd3d1a52cfce9d5f2fac7fde164d500e50f76000613e3f565b846130095760008094509450505050612855565b6040516370a0823160e01b815230600482015281906001600160a01b038916906370a0823190602401602060405180830381865afa15801561304f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061307391906143db565b61307d91906146a5565b9350836000036130a05760405163b5b4985560e01b815260040160405180910390fd5b6130b66001600160a01b03881661dead86614026565b5050509250929050565b6000806130ce600284614420565b905060006130dc82856146a5565b60408051600280825260608201835292935060009290916020830190803683370190505090507f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f010588160008151811061313657613136614564565b60200260200101906001600160a01b031690816001600160a01b0316815250507f000000000000000000000000b7d7d4cc36ff190d2dca2cd618a71f3574688dd08160018151811061318a5761318a614564565b6001600160a01b0392831660209182029290920101526040516370a0823160e01b81523060048201526000917f000000000000000000000000b7d7d4cc36ff190d2dca2cd618a71f3574688dd016906370a0823190602401602060405180830381865afa1580156131ff573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061322391906143db565b90506132796001600160a01b037f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f01058167f000000000000000000000000dae9dd3d1a52cfce9d5f2fac7fde164d500e50f786613e3f565b6132ae7f000000000000000000000000b7d7d4cc36ff190d2dca2cd618a71f3574688dd0856132a88588613cce565b85613efd565b9450846133165761330a6001600160a01b037f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f01058167f000000000000000000000000dae9dd3d1a52cfce9d5f2fac7fde164d500e50f76000613e3f565b50600095945050505050565b6040516370a0823160e01b815230600482015260009082906001600160a01b037f000000000000000000000000b7d7d4cc36ff190d2dca2cd618a71f3574688dd016906370a0823190602401602060405180830381865afa15801561337f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133a391906143db565b6133ad91906146a5565b9050806000036133d05760405163b5b4985560e01b815260040160405180910390fd5b6134246001600160a01b037f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f01058167f000000000000000000000000dae9dd3d1a52cfce9d5f2fac7fde164d500e50f786613e3f565b6134786001600160a01b037f000000000000000000000000b7d7d4cc36ff190d2dca2cd618a71f3574688dd0167f000000000000000000000000dae9dd3d1a52cfce9d5f2fac7fde164d500e50f783613e3f565b60007f000000000000000000000000dae9dd3d1a52cfce9d5f2fac7fde164d500e50f76001600160a01b031663e8e337007f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f010587f000000000000000000000000b7d7d4cc36ff190d2dca2cd618a71f3574688dd088866134f68b614057565b6134ff89614057565b60405160e088901b6001600160e01b03191681526001600160a01b039687166004820152948616602486015260448501939093526064840191909152608483015260a48201527f000000000000000000000000e1dedf16b298e3532fb551c21710e23d3b0a28b790911660c48201524260e4820152610104016060604051808303816000875af1158015613597573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135bb9190614957565b92506136159150506001600160a01b037f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f01058167f000000000000000000000000dae9dd3d1a52cfce9d5f2fac7fde164d500e50f76000613e3f565b61366a6001600160a01b037f000000000000000000000000b7d7d4cc36ff190d2dca2cd618a71f3574688dd0167f000000000000000000000000dae9dd3d1a52cfce9d5f2fac7fde164d500e50f76000613e3f565b60007f000000000000000000000000dae9dd3d1a52cfce9d5f2fac7fde164d500e50f76001600160a01b031663c45a01556040518163ffffffff1660e01b8152600401602060405180830381865afa1580156136ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136ee9190614832565b60405163e6a4390560e01b81526001600160a01b037f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f01058811660048301527f000000000000000000000000b7d7d4cc36ff190d2dca2cd618a71f3574688dd081166024830152919091169063e6a4390590604401602060405180830381865afa15801561377e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137a29190614832565b90506001600160a01b038116158015906137bc5750600082115b156138445760405163dc850b3d60e01b81526001600160a01b038281166004830152602482018490527f000000000000000000000000e1dedf16b298e3532fb551c21710e23d3b0a28b7169063dc850b3d90604401600060405180830381600087803b15801561382b57600080fd5b505af115801561383f573d6000803e3d6000fd5b505050505b88600e60008282546138569190614434565b909155505060408051878152602081018590529081018390527fbcdc45cfc4c2863aeb5b434c4521091f79995db2fbfaf5706840cf370f2a533e9060600160405180910390a150505050505050919050565b6138fc6001600160a01b037f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f01058167f000000000000000000000000e1dedf16b298e3532fb551c21710e23d3b0a28b783614026565b604051630f2bc5d760e11b81526001600160a01b037f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f0105881166004830152602482018390527f000000000000000000000000e1dedf16b298e3532fb551c21710e23d3b0a28b71690631e578bae90604401600060405180830381600087803b15801561398657600080fd5b505af115801561399a573d6000803e3d6000fd5b5050505080600f60008282546139b09190614434565b90915550506040518181526001600160a01b037f000000000000000000000000dd99adf15d84d425f02983e730b0db8e27f0105816907fda1785fe1dec267c363b06ae3e02c3e086123f9d611399f7a1cc98492518372e9060200160405180910390a250565b600082600003613a285750600061086f565b600080613a3486611ce7565b90925090506001600160a01b038216613a525760009250505061086f565b8361ffff16600003613a695760019250505061086f565b613a7761ffff851682614447565b613a8361271087614447565b11159695505050505050565b613a97614171565b600a54602083015160009161271091613abd9162010000900461ffff9081169116614447565b613ac79190614420565b905061ffff81161580613ad8575083155b15613ae6578291505061085e565b600454600090859061271090613b04906001600160a01b0316611506565b613b0e9190614447565b613b189190614420565b90508161ffff16811015613b2a578091505b8161ffff16600003613b4057839250505061085e565b6000612710613b5361ffff851688614447565b613b5d9190614420565b9050613b688161285c565b613b775784935050505061085e565b600454613b8d906001600160a01b0316826110a1565b613b9c5784935050505061085e565b8285602001818151613bae91906148d0565b61ffff9081169091529390931660408601525092949350505050565b613bd2614171565b602082015160009061271090613bec9061ffff1686614447565b613bf69190614420565b90506000600261271061ffff16856060015161ffff1687613c179190614447565b613c219190614420565b613c2b9190614420565b90506000613c37612623565b90506000613c458385614434565b90506000811580613c7b5750613c7b7f000000000000000000000000b7d7d4cc36ff190d2dca2cd618a71f3574688dd0836110a1565b9050828015613c875750805b15613c9957869550505050505061085e565b613ca287612c59565b98975050505050505050565b6000806000613cbb6116ec565b91509150808015610a5557509092915050565b6000807f000000000000000000000000dae9dd3d1a52cfce9d5f2fac7fde164d500e50f76001600160a01b031663d06ca61f84866040518363ffffffff1660e01b8152600401613d1f9291906145bf565b600060405180830381865afa158015613d3c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613d6491908101906145d8565b90506000613d978260018451613d7a91906146a5565b81518110613d8a57613d8a614564565b6020026020010151614057565b9050600080613dcc8760018951613dae91906146a5565b81518110613dbe57613dbe614564565b60200260200101518761264f565b9150915080613e25578660018851613de491906146a5565b81518110613df457613df4614564565b6020026020010151604051630817760b60e11b8152600401610e3091906001600160a01b0391909116815260200190565b828211613e325782613e34565b815b979650505050505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052613e908482614087565b610c23576040516001600160a01b03848116602483015260006044830152613ef391869182169063095ea7b3906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506140d1565b610c2384826140d1565b6000614e20603f613f12620f42406040614447565b613f1c9190614420565b613f269190614434565b5a1015613f46576040516303f72b9f60e21b815260040160405180910390fd5b604051635c11d79560e01b81526001600160a01b037f000000000000000000000000dae9dd3d1a52cfce9d5f2fac7fde164d500e50f71690635c11d79590620f424090613f9f9088908890889030904290600401614985565b600060405180830381600088803b158015613fb957600080fd5b5087f193505050508015613fcb575060015b61401b5760408051858152602081018590526001600160a01b038716917faca3a2d1e741e999653f8f356d17afda6e54ef430b374580735dd3acd2396b2b910160405180910390a2506000611c24565b506001949350505050565b6040516001600160a01b03838116602483015260448201839052610c5c91859182169063a9059cbb90606401613ec1565b6008546000906127109061406f9061ffff16826148d0565b61407d9061ffff1684614447565b61085e9190614420565b6000806000806020600086516020880160008a5af192503d91506000519050828015610bf4575081156140bd5780600114610bf4565b50505050506001600160a01b03163b151590565b600080602060008451602086016000885af1806140f4576040513d6000823e3d81fd5b50506000513d9150811561410c578060011415614119565b6001600160a01b0384163b155b15610c2357604051635274afe760e01b81526001600160a01b0385166004820152602401610e30565b6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915290565b6000602082840312156141b157600080fd5b81356001600160e01b03198116811461086f57600080fd5b61ffff81168114611aa757600080fd5b600080604083850312156141ec57600080fd5b82356141f7816141c9565b91506020830135614207816141c9565b809150509250929050565b60006020828403121561422457600080fd5b5035919050565b6001600160a01b0381168114611aa757600080fd5b60006020828403121561425257600080fd5b813561086f8161422b565b6000806040838503121561427057600080fd5b8235915060208301356142078161422b565b60008060006060848603121561429757600080fd5b833592506020840135915060408401356142b0816141c9565b809150509250925092565b60a0810161085e828480518252602081015160208301526040810151604083015260608101516060830152608081015160808301525050565b6000806040838503121561430757600080fd5b82356143128161422b565b946020939093013593505050565b83815260e0810161435f602083018580518252602081015160208301526040810151604083015260608101516060830152608081015160808301525050565b8260c0830152949350505050565b6000806040838503121561438057600080fd5b823591506020830135614207816141c9565b600080604083850312156143a557600080fd5b82356143b08161422b565b915060208301356142078161422b565b600060a08284031280156143d357600080fd5b509092915050565b6000602082840312156143ed57600080fd5b5051919050565b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60008261442f5761442f6143f4565b500490565b8082018082111561085e5761085e61440a565b808202811582820484141761085e5761085e61440a565b6001600160a01b039384168152919092166020820152604081019190915260600190565b80516001600160701b038116811461261e57600080fd5b6000806000606084860312156144ae57600080fd5b6144b784614482565b92506144c560208501614482565b9150604084015163ffffffff811681146142b057600080fd5b878152610160810161451e602083018980518252602081015160208301526040810151604083015260608101516060830152608081015160808301525050565b60c082019690965293151560e0850152911515610100840152151561012083015215156101409091015292915050565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b600081518084526020840193506020830160005b828110156145b55781516001600160a01b031686526020958601959091019060010161458e565b5093949350505050565b828152604060208201526000611c24604083018461457a565b6000602082840312156145ea57600080fd5b815167ffffffffffffffff81111561460157600080fd5b8201601f8101841361461257600080fd5b805167ffffffffffffffff81111561462c5761462c61454e565b8060051b604051601f19603f830116810181811067ffffffffffffffff821117156146595761465961454e565b60405291825260208184018101929081018784111561467757600080fd5b6020850194505b8385101561469a5784518082526020958601959093500161467e565b509695505050505050565b8181038181111561085e5761085e61440a565b6000602082840312156146ca57600080fd5b813561086f816141c9565b6000813561085e816141c9565b81356146ed816141c9565b61ffff8116905081548161ffff198216178355602084013561470e816141c9565b63ffff00008160101b168363ffffffff198416171784555050506040820135614736816141c9565b815465ffff00000000191660209190911b65ffff000000001617815560006060830135614762816141c9565b825467ffff0000000000001916603082901b67ffff000000000000161783559050506128c0614793608084016146d5565b825469ffff0000000000000000191660409190911b69ffff000000000000000016178255565b60a0810182356147c8816141c9565b61ffff16825260208301356147dc816141c9565b61ffff16602083015260408301356147f3816141c9565b61ffff166040830152606083013561480a816141c9565b61ffff1660608301526080830135614821816141c9565b61ffff811660808401525092915050565b60006020828403121561484457600080fd5b815161086f8161422b565b60006020828403121561486157600080fd5b8151801515811461086f57600080fd5b6000806000806080858703121561488757600080fd5b8451614892816141c9565b60208601519094506148a3816141c9565b60408601519093506148b4816141c9565b60608601519092506148c5816141c9565b939692955090935050565b61ffff828116828216039081111561085e5761085e61440a565b6000825160005b8181101561490b57602081860181015185830152016148f1565b506000920191825250919050565b600061ffff83168061492d5761492d6143f4565b8061ffff84160491505092915050565b61ffff818116838216019081111561085e5761085e61440a565b60008060006060848603121561496c57600080fd5b5050815160208301516040909301519094929350919050565b85815284602082015260a0604082015260006149a460a083018661457a565b6001600160a01b039490941660608301525060800152939250505056fe17d57ed1a1d10c36bff7484e6bb95371a28422a7baabf93343b1965f53f83fa0a264697066735822122068ef80461e91b18392ef13fc489a269e82c7ea22481d5c002660ff5a4b56472d64736f6c634300081a0033