Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
RewardsEngineV1_2
Compiler Version
v0.8.26+commit.8a97fa7a
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
// REWARDS ENGINE V1.2
// Carbon copy of Spearbit-audited V1 RewardsEngine + batch claim functions (V1.1 additions only)
pragma solidity 0.8.26;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
interface IRewardsMintable {
function mint(address to, uint256 amount) external;
}
interface IPolicyDistributionConfig {
function getDistributionSkimBps() external view returns (uint16);
function getCAPPrice() external view returns (uint256);
}
// Minimal interface to read CollateralAttestation pointer from PolicyManager
interface IPolicyAttestationRef {
function collateralAttestation() external view returns (address);
}
// Minimal interface for CollateralAttestation to fetch current CR (18 decimals)
interface ICollateralAttestationView {
function getCollateralRatio() external view returns (uint256);
function isAttestationStale() external view returns (bool);
}
// Minimal interface to refresh PolicyManager's band before reading band-dependent config
interface IPolicyBandRefresh {
function refreshBand() external returns (uint8);
}
interface ILiquidityReserve {
function withdrawDistributionSkim(address to, uint256 amount) external;
}
// RewardsEngine tracks balance-time units, routes USDC coupons into BUCK mints, and enforces late-entry rules.
// LiquidityWindow tops up reserve; this contract mirrors inflows into token rewards with band-aware haircuts.
// Goal is to keep distribution math transparent: all accounting in 18-dec, explicit epochs, rich telemetry.
contract RewardsEngineV1_2 is
Initializable,
AccessControlUpgradeable,
PausableUpgradeable,
MulticallUpgradeable,
UUPSUpgradeable
{
using SafeERC20 for IERC20;
using Math for uint256;
// -------------------------------------------------------------------------
// Role constants
// -------------------------------------------------------------------------
bytes32 public constant ADMIN_ROLE = DEFAULT_ADMIN_ROLE;
bytes32 public constant DISTRIBUTOR_ROLE = keccak256("DISTRIBUTOR_ROLE");
bytes32 public constant CLAIM_ADMIN_ROLE = keccak256("CLAIM_ADMIN_ROLE"); // V1.2 addition
// -------------------------------------------------------------------------
// Constants
// -------------------------------------------------------------------------
uint256 private constant ACC_PRECISION = 1e18;
uint256 private constant PRICE_SCALE = 1e18;
uint256 private constant USDC_TO_18 = 1e12;
uint256 private constant BPS_DENOMINATOR = 10_000;
// -------------------------------------------------------------------------
// Errors
// -------------------------------------------------------------------------
error NotToken();
error InvalidConfig();
error BalanceUnderflow();
error NothingToDistribute();
error ClaimTooSmall(uint256 claimable, uint256 minRequired);
error NoRewardsDeclared();
error InvalidRecipient();
error InvalidRecoverySink(address account);
error UnsupportedRecoveryAsset(address token);
error ZeroAddress();
error InvalidAmount();
error InvalidOraclePrice();
error MaxTokensPerEpochExceeded(uint256 requested, uint256 maxAllowed);
error InvalidMaxTokensPerEpoch();
error AlreadyDistributed();
error EpochNotConfigured();
error DistributionTooEarly();
error DistributionBlockedDuringDepeg(uint256 capPrice);
error MustDistributeBeforeNewEpoch();
error MaxClaimPerTxExceeded(uint256 requested, uint256 maxAllowed);
error ClaimExceedsHeadroom(uint256 requested, uint256 headroom);
error StaleAttestationForClaim();
error NothingToClaim(); // V1.2 addition
error BatchTooLarge(uint256 size, uint256 max); // V1.2 addition
// -------------------------------------------------------------------------
// Storage - account tracking
// -------------------------------------------------------------------------
// Per-account ledger storing balances, accrued units, and gating metadata.
struct AccountState {
uint256 balance; // Current BUCK balance observed via hook
uint64 lastClaimedEpoch; // Last epoch user claimed rewards for
uint64 lastAccrualTime; // Timestamp of last unit accrual (for current epoch)
uint64 lastAccruedEpoch; // Epoch id for which unitsAccrued applies (rolls forward lazily)
uint64 lastInflow; // Timestamp when balance last increased
uint256 unitsAccrued; // Time-weighted units accrued in the CURRENT epoch (balance * seconds)
uint256 pendingRewards; // Rewards credited from PRIOR epochs, waiting to be claimed (in BUCK)
uint256 rewardDebt; // Baseline of accRewardPerUnit applied to accrued units (for O(1) claims)
bool excluded; // True when account is excluded from earning
bool eligible; // False if any outflow before checkpoint end
uint256 lateInflow; // Tokens received after checkpointStart (don't earn until next epoch)
uint64 lateInflowEpoch; // Epoch when lateInflow was recorded (for lazy reset)
}
mapping(address => AccountState) private _accounts;
// -------------------------------------------------------------------------
// Storage - admin wiring & config
// -------------------------------------------------------------------------
address public token; // BUCK token expected to call onBalanceChange / mint rewards
address public policyManager; // Required for CAP pricing and distribution skim
address public treasury; // Treasury to receive skim fees (also receives breakage)
address public liquidityReserve; // LiquidityReserve to pull USDC from
address public reserveUSDC; // USDC token address for reserve balance checks
uint256 public minClaimTokens;
// -------------------------------------------------------------------------
// Storage - epoch configuration
// -------------------------------------------------------------------------
uint64 public currentEpochId;
uint64 public epochStart;
uint64 public epochEnd;
uint64 public checkpointStart; // Start of checkpoint window (must hold through)
uint64 public checkpointEnd; // End of checkpoint window
// -------------------------------------------------------------------------
// Storage - global integrator (per-epoch)
// -------------------------------------------------------------------------
uint256 public globalEligibleUnits; // Integral of eligible supply THIS epoch
uint256 public currentEligibleSupply; // Sum of eligible account balances
uint64 public lastGlobalUpdateTime; // Last time global integrator was updated
uint256 public treasuryUnitsThisEpoch; // Pre-checkpoint breakage accumulator
uint256 public futureBreakageUnits; // Post-checkpoint breakage (remaining days → DAO)
uint256 public totalBreakageAllTime; // Lifetime breakage counter
uint256 public totalExcludedSupply; // Sum of excluded account balances
bool public distributedThisEpoch; // Enforce one distribution per epoch
bool public blockDistributeOnDepeg; // Block distributions when CAP < $1 (default: true)
// -------------------------------------------------------------------------
// Storage - per-epoch distribution data & reporting (GLOBAL)
// -------------------------------------------------------------------------
// Global cumulative reward index (incremented each distribution)
uint256 public accRewardPerUnit; // scaled by ACC_PRECISION
// Per-epoch timing (used for epoch-boundary finalization in _settleAccount)
mapping(uint64 => uint64) public epochStartTime; // Start timestamp for each epoch
mapping(uint64 => uint64) public epochEndTime; // End timestamp for each epoch
// Lightweight per-epoch report for analytics (no per-user snapshots)
struct EpochReport {
uint64 distributionTime; // Timestamp when distribute() executed for this epoch
uint256 denominatorUnits; // Denominator used for deltaIndex (eligible + sink units)
uint256 deltaIndex; // tokensAllocated / denominatorUnits (scaled by ACC_PRECISION)
uint256 tokensAllocated; // Tokens allocated (minus dust carry)
uint256 dustCarry; // Dust carried to next distribution
}
mapping(uint64 => EpochReport) public epochReport;
// -------------------------------------------------------------------------
// Storage - reward accounting
// -------------------------------------------------------------------------
uint64 public lastDistributedEpochId;
uint256 public lastDistributionCAPPrice; // CAP price used for distribution (18 decimals)
uint256 public totalRewardsDeclared;
uint256 public totalRewardsClaimed;
uint256 public dust; // Leftover BUCK from division rounding, carried into next distribution
mapping(address => bool) public isRecoverySink;
uint256 public maxTokensToMintPerEpoch; // Maximum BUCK tokens that can be minted in one epoch
uint256 public currentEpochTokensMinted; // Tracks tokens minted in current epoch
uint64 public lastMintEpochId; // Tracks which epoch we last minted in (for reset logic)
// Breakage sink receives forfeited units (pre- and post-checkpoint). Always excluded from accrual.
address public breakageSink;
// Claim-time controls
bool public enforceCROnClaim; // When true, revert claims that would push CR below 1.0
uint256 public maxClaimTokensPerTx; // Optional per-transaction cap for claims (0 = unlimited)
// -------------------------------------------------------------------------
// Events
// -------------------------------------------------------------------------
// Epoch & Checkpoint
event EpochConfigured(
uint64 indexed epochId,
uint64 epochStart,
uint64 epochEnd,
uint64 checkpointStart,
uint64 checkpointEnd
);
// Distribution
event DistributionPriced(
uint64 indexed epochId, uint256 couponUsdc, uint256 capPrice, uint256 tokensFromCoupon
);
event DistributionSkimCollected(
uint64 indexed epochId, uint256 skimUsdc, uint16 skimBps, address indexed treasury
);
event DistributionDeclared(
uint64 indexed epochId,
uint256 tokensAllocated,
uint256 denominatorUnits,
uint256 globalEligibleUnits,
uint256 treasuryBreakage,
uint256 futureBreakage,
uint256 deltaIndex,
uint256 dustCarry,
uint256 grossAPYBps,
uint256 netAPYBps
);
// Claims
event RewardClaimed(
address indexed account,
address indexed recipient,
uint256 amount,
uint64 fromEpoch,
uint64 toEpoch
);
// Breakage
event ProportionalBreakage(
address indexed account,
uint256 amountSold,
uint256 unitsForfeit,
uint64 indexed epochId
);
event FutureBreakage(
address indexed account,
uint256 amountSold,
uint256 futureUnits,
uint256 remainingSeconds,
uint64 indexed epochId
);
// Admin
event MinClaimUpdated(uint256 minClaimTokens);
event TokenHookUpdated(address indexed token);
event TreasuryUpdated(address indexed treasury);
event PolicyManagerUpdated(address indexed policyManager);
event AccountExcluded(address indexed account, bool isExcluded);
event MaxTokensPerEpochUpdated(uint256 maxTokens);
event DepegGuardUpdated(bool blocked);
event RecoverySinkSet(address indexed sink, bool allowed);
event TokensRecovered(
address indexed caller, address indexed token, address indexed to, uint256 amount
);
event BreakageSinkUpdated(address indexed oldSink, address indexed newSink);
event CROnClaimEnforcementUpdated(bool enabled);
event MaxClaimPerTxUpdated(uint256 maxTokens);
// V1.2 addition: batch claim events
event ClaimForExecuted(
address indexed claimAdmin,
address indexed user,
address indexed recipient,
uint256 amount
);
event BatchClaimExecuted(
address indexed claimAdmin,
uint256 usersProcessed,
uint256 totalClaimed
);
// -------------------------------------------------------------------------
// Constructor & Initializer
// -------------------------------------------------------------------------
// Implementation constructor locks initialization; actual setup comes via proxy initializer.
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
// Wire admin + distributor roles and baseline earning parameters.
// Called once post deploy; recover sinks start with admin to keep rescue paths simple.
function initialize(
address admin,
address distributor,
uint256 minClaimTokens_
) external initializer {
__AccessControl_init();
__Pausable_init();
__Multicall_init();
__UUPSUpgradeable_init();
if (admin == address(0)) revert InvalidConfig();
if (distributor == address(0)) revert InvalidConfig();
_grantRole(ADMIN_ROLE, admin);
_grantRole(DISTRIBUTOR_ROLE, distributor);
minClaimTokens = minClaimTokens_;
isRecoverySink[admin] = true;
blockDistributeOnDepeg = true; // Default: block distributions during depeg for safety
}
// -------------------------------------------------------------------------
// UUPS Upgrade Authorization
// -------------------------------------------------------------------------
// Only the admin role can approve new logic for this UUPS contract.
function _authorizeUpgrade(address newImplementation) internal override onlyRole(ADMIN_ROLE) {}
// -------------------------------------------------------------------------
// Modifiers
// -------------------------------------------------------------------------
modifier onlyToken() {
if (msg.sender != token) revert NotToken();
_;
}
// V1.2 addition
modifier onlyClaimAdmin() {
_checkRole(CLAIM_ADMIN_ROLE);
_;
}
// -------------------------------------------------------------------------
// Admin configuration
// -------------------------------------------------------------------------
// Connect reserve + USDC token so we can validate distributions and pull skims.
// Both addresses must be set before distribute() enforcement kicks in.
function setReserveAddresses(address liquidityReserve_, address reserveUSDC_)
external
onlyRole(ADMIN_ROLE)
{
if (liquidityReserve_ == address(0) || reserveUSDC_ == address(0)) revert ZeroAddress();
liquidityReserve = liquidityReserve_;
reserveUSDC = reserveUSDC_;
}
// Cap BUCK minted per epoch to throttle emissions if policy wants a hard ceiling.
// Resets automatically when a new epoch id is configured.
function setMaxTokensToMintPerEpoch(uint256 maxTokens) external onlyRole(ADMIN_ROLE) {
if (maxTokens == 0) revert InvalidMaxTokensPerEpoch();
maxTokensToMintPerEpoch = maxTokens;
emit MaxTokensPerEpochUpdated(maxTokens);
}
// Adjust dust filter so tiny claims don't clog gas or event logs.
// Can be tuned up or down without touching accrued units.
function setMinClaimTokens(uint256 minClaimTokens_) external onlyRole(ADMIN_ROLE) {
minClaimTokens = minClaimTokens_;
emit MinClaimUpdated(minClaimTokens_);
}
// Toggle CR guard for claims (prevents CR dropping below 1 after mint)
function setEnforceCROnClaim(bool enabled) external onlyRole(ADMIN_ROLE) {
enforceCROnClaim = enabled;
emit CROnClaimEnforcementUpdated(enabled);
}
// Configure a maximum claim size per transaction (0 disables the cap)
function setMaxClaimTokensPerTx(uint256 maxTokens) external onlyRole(ADMIN_ROLE) {
maxClaimTokensPerTx = maxTokens;
emit MaxClaimPerTxUpdated(maxTokens);
}
// Whitelist addresses that may receive recovered tokens (treasury multisig, etc.).
// Ensures rescue flows terminate in known good destinations only.
function setRecoverySink(address sink, bool allowed) external onlyRole(ADMIN_ROLE) {
if (sink == address(0)) revert ZeroAddress();
isRecoverySink[sink] = allowed;
emit RecoverySinkSet(sink, allowed);
}
// BUCK token registers here so only it can call onBalanceChange/mint.
// Protects hooks from rogue contracts trying to spoof balance updates.
function setToken(address token_) external onlyRole(ADMIN_ROLE) {
if (token_ == address(0)) revert ZeroAddress();
token = token_;
emit TokenHookUpdated(token_);
}
// Treasury receives coupon skim and can be rotated as governance matures.
// Required for distribute() to successfully push skim via LiquidityReserve.
function setTreasury(address treasury_) external onlyRole(ADMIN_ROLE) {
if (treasury_ == address(0)) revert ZeroAddress();
treasury = treasury_;
emit TreasuryUpdated(treasury_);
}
/// @notice Set the breakage sink address (always excluded from accrual)
/// @dev Excludes the new sink in-place and adjusts eligible/excluded supply counters
function setBreakageSink(address sink) external onlyRole(ADMIN_ROLE) {
if (sink == address(0)) revert ZeroAddress();
_accrueGlobal();
// Ensure sink is settled before changing flags
_settleAccount(sink);
AccountState storage s = _accounts[sink];
if (!s.excluded) {
if (s.eligible && s.balance > 0) {
if (currentEligibleSupply >= s.balance) {
currentEligibleSupply -= s.balance;
} else {
currentEligibleSupply = 0;
}
}
totalExcludedSupply += s.balance;
s.excluded = true;
s.eligible = false;
// Reset units and rewardDebt to keep accounting invariant intact.
s.unitsAccrued = 0;
s.rewardDebt = 0;
s.lastAccrualTime = _cappedTimestamp();
}
address old = breakageSink;
breakageSink = sink;
emit BreakageSinkUpdated(old, sink);
}
// Hard stop on new distributions during incidents.
// Keeps coupons safely in reserve until the pause is lifted.
function pauseDistribute() external onlyRole(ADMIN_ROLE) {
_pause();
}
// Resume distributions after remediation.
// Emits no event; rely on `pause()` logs for incident history.
function unpauseDistribute() external onlyRole(ADMIN_ROLE) {
_unpause();
}
// Wire PolicyManager so we can read band/fees/haircuts in real time.
// Safe to set to zero when testing since distribute() guards against null policy.
function setPolicyManager(address policyManager_) external onlyRole(ADMIN_ROLE) {
policyManager = policyManager_;
emit PolicyManagerUpdated(policyManager_);
}
// Toggle depeg guard: when true, distribute() reverts if CAP price < $1.
// Default is true for safety; governance can disable if needed during stress.
function setBlockDistributeOnDepeg(bool blocked) external onlyRole(ADMIN_ROLE) {
blockDistributeOnDepeg = blocked;
emit DepegGuardUpdated(blocked);
}
// Configures a new epoch with checkpoint window for eligibility verification.
// Checkpoint window (e.g., days 12-16): holders must NOT sell during this period to earn.
// Late entries (after checkpointStart) are ineligible for this epoch.
function configureEpoch(
uint64 epochId,
uint64 epochStart_,
uint64 epochEnd_,
uint64 checkpointStart_,
uint64 checkpointEnd_
) external onlyRole(ADMIN_ROLE) {
_configureEpochInternal(epochId, epochStart_, epochEnd_, checkpointStart_, checkpointEnd_);
}
function _configureEpochInternal(
uint64 epochId,
uint64 epochStart_,
uint64 epochEnd_,
uint64 checkpointStart_,
uint64 checkpointEnd_
) internal {
if (epochEnd_ <= epochStart_) revert InvalidConfig();
if (epochId <= currentEpochId) revert InvalidConfig();
if (checkpointStart_ <= epochStart_) revert InvalidConfig();
if (checkpointEnd_ >= epochEnd_) revert InvalidConfig();
if (checkpointEnd_ <= checkpointStart_) revert InvalidConfig();
// Prevent configuring new epoch before distributing the current one
// This ensures distribute() always applies to the intended epoch
if (currentEpochId > 0 && !distributedThisEpoch) revert MustDistributeBeforeNewEpoch();
currentEpochId = epochId;
epochStart = epochStart_;
epochEnd = epochEnd_;
checkpointStart = checkpointStart_;
checkpointEnd = checkpointEnd_;
// Store epoch timing for settlement/analytics
epochStartTime[epochId] = epochStart_;
epochEndTime[epochId] = epochEnd_;
// Reset global state for new epoch
lastGlobalUpdateTime = epochStart_;
globalEligibleUnits = 0;
treasuryUnitsThisEpoch = 0;
futureBreakageUnits = 0;
distributedThisEpoch = false;
// Eligible supply = total supply MINUS excluded accounts
address token_ = token;
if (token_ != address(0)) {
currentEligibleSupply = IERC20(token_).totalSupply() - totalExcludedSupply;
}
emit EpochConfigured(epochId, epochStart_, epochEnd_, checkpointStart_, checkpointEnd_);
}
// Admin can exclude system wallets from earning (or re-include).
// Updates eligible supply tracking to maintain global integrator accuracy.
function setAccountExcluded(address account, bool isExcluded) external onlyRole(ADMIN_ROLE) {
AccountState storage s = _accounts[account];
// No change needed
if (s.excluded == isExcluded) return;
// Accrue global units before changing eligible supply
_accrueGlobal();
// Settle account's current units
_settleAccount(account);
if (isExcluded) {
// Excluding: remove from eligible supply, add to excluded supply
if (s.balance > 0 && s.eligible) {
if (currentEligibleSupply >= s.balance) {
currentEligibleSupply -= s.balance;
} else {
currentEligibleSupply = 0;
}
}
totalExcludedSupply += s.balance;
// Reset their units for this epoch (they don't earn when excluded)
// Also reset rewardDebt to keep accounting invariant intact.
s.unitsAccrued = 0;
s.rewardDebt = 0;
s.eligible = false;
} else {
// Re-including: remove from excluded supply
if (totalExcludedSupply >= s.balance) {
totalExcludedSupply -= s.balance;
} else {
totalExcludedSupply = 0;
}
// Apply late-entry rule: re-inclusions after checkpointStart don't earn this epoch
uint64 now_ = _cappedTimestamp();
bool isLateEntry = (checkpointStart > 0 && now_ >= checkpointStart && now_ < epochEnd);
if (isLateEntry) {
// Late re-inclusion: ineligible for current epoch, don't add to eligible supply
s.eligible = false;
s.lastAccrualTime = now_;
} else if (s.balance > 0) {
// Normal re-inclusion: add to eligible supply
currentEligibleSupply += s.balance;
s.eligible = true;
}
}
s.excluded = isExcluded;
s.lastAccrualTime = _cappedTimestamp();
emit AccountExcluded(account, isExcluded);
}
// -------------------------------------------------------------------------
// Token hook entrypoint
// -------------------------------------------------------------------------
// Token hook: BUCK calls this on transfer to keep balance-time units synced.
// Handles mint/burn/transfer with minimal assumptions about caller.
function onBalanceChange(address from, address to, uint256 amount) external onlyToken {
// Skip self-transfers - no economic change, prevents breakage inflation griefing
if (from == to) return;
// Do not accrue for the RewardsEngine contract itself, but still process the counterparty
if (from != address(0) && from != address(this)) {
_handleOutflow(from, amount);
}
if (to != address(0) && to != address(this)) {
_handleInflow(to, amount);
}
}
// -------------------------------------------------------------------------
// Reward distribution
// -------------------------------------------------------------------------
// Core coupon handler: validates reserve deposits, applies skim, calculates reward per unit.
// ONE distribution per epoch - stores epochReport with deltaIndex for claim logic.
function distribute(uint256 couponUsdcAmount)
external
onlyRole(DISTRIBUTOR_ROLE)
whenNotPaused
returns (uint256 allocated, uint256 newDust)
{
// Ensure band-dependent config (e.g., distributionSkimBps) is current
if (policyManager != address(0)) {
IPolicyBandRefresh(policyManager).refreshBand();
// Depeg guard: block distributions when CAP < $1 to prevent solvency degradation
// Can be toggled off by admin if governance needs to distribute during stress
if (blockDistributeOnDepeg) {
uint256 depegCheckPrice = IPolicyDistributionConfig(policyManager).getCAPPrice();
if (depegCheckPrice < 1e18) revert DistributionBlockedDuringDepeg(depegCheckPrice);
}
}
// Enforce one distribution per epoch
if (distributedThisEpoch) revert AlreadyDistributed();
// Ensure epoch is configured before distributing
if (epochEnd == 0) revert EpochNotConfigured();
// Ensure we're at or past epoch end (prevents mid-epoch distribution)
if (block.timestamp < epochEnd) revert DistributionTooEarly();
// Security: Pull USDC directly from distributor to prove funds deposited
if (liquidityReserve == address(0) || reserveUSDC == address(0)) {
revert InvalidConfig();
}
// Transfer coupon USDC from caller to reserve
IERC20(reserveUSDC).safeTransferFrom(msg.sender, liquidityReserve, couponUsdcAmount);
// Apply distribution skim fee before calculating BUCK allocation
uint256 netCouponUsdc = couponUsdcAmount;
uint256 skimUsdc = 0;
uint16 skimBps = 0;
{
address policy = policyManager;
if (policy != address(0)) {
IPolicyDistributionConfig config = IPolicyDistributionConfig(policy);
skimBps = config.getDistributionSkimBps();
if (skimBps > 0 && treasury != address(0) && liquidityReserve != address(0)) {
skimUsdc = Math.mulDiv(couponUsdcAmount, skimBps, BPS_DENOMINATOR);
netCouponUsdc = couponUsdcAmount - skimUsdc;
}
}
}
// Finalize global units up to epoch end (or now if mid-epoch)
_accrueGlobal();
// CAP pricing: $1 when CR ≥ 1, else max(oracle, CR)
uint256 tokensFromCoupon;
uint256 capPrice;
{
address policy = policyManager;
if (policy == address(0)) revert InvalidConfig();
IPolicyDistributionConfig config = IPolicyDistributionConfig(policy);
// Withdraw skim BEFORE getCAPPrice so CR calculation sees correct reserve balance
if (skimUsdc > 0) {
ILiquidityReserve(liquidityReserve).withdrawDistributionSkim(treasury, skimUsdc);
emit DistributionSkimCollected(currentEpochId, skimUsdc, skimBps, treasury);
}
capPrice = config.getCAPPrice();
if (capPrice == 0) revert InvalidOraclePrice();
if (netCouponUsdc > type(uint256).max / USDC_TO_18) revert InvalidAmount();
uint256 scaledCoupon = netCouponUsdc * USDC_TO_18;
tokensFromCoupon = Math.mulDiv(scaledCoupon, PRICE_SCALE, capPrice);
emit DistributionPriced(currentEpochId, couponUsdcAmount, capPrice, tokensFromCoupon);
lastDistributionCAPPrice = capPrice;
}
// Check if we need to reset epoch counter (new epoch)
if (lastMintEpochId != currentEpochId) {
currentEpochTokensMinted = 0;
lastMintEpochId = currentEpochId;
}
// Enforce max tokens per epoch if configured
uint256 totalReward = tokensFromCoupon + dust;
if (maxTokensToMintPerEpoch > 0) {
if (currentEpochTokensMinted + totalReward > maxTokensToMintPerEpoch) {
revert MaxTokensPerEpochExceeded(
currentEpochTokensMinted + totalReward, maxTokensToMintPerEpoch
);
}
}
if (totalReward == 0) revert NothingToDistribute();
// Calculate total units: eligible holders + treasury breakage + future breakage
uint256 totalUnits = globalEligibleUnits + treasuryUnitsThisEpoch + futureBreakageUnits;
// Calculate APY metrics for event emission
uint256 grossAPYBps = 0;
uint256 netAPYBps = 0;
{
uint256 totalSupply = IERC20(token).totalSupply();
if (totalSupply > 0 && capPrice > 0 && couponUsdcAmount > 0) {
uint256 epochDurationSeconds = epochEnd > epochStart ? epochEnd - epochStart : 30 days;
if (epochDurationSeconds < 1 days) epochDurationSeconds = 30 days;
uint256 totalSupplyValueUSD = Math.mulDiv(totalSupply, capPrice, PRICE_SCALE);
if (totalSupplyValueUSD > 0) {
// Calculate APY in single step to preserve precision
// Old approach lost precision: returnBps truncates (e.g., 1.9 → 1), then *365 = 365 instead of ~700
// New approach: (coupon * BPS * 365days) / (supplyValue * epochDuration) preserves precision
uint256 denominatorScaled = totalSupplyValueUSD * epochDurationSeconds;
uint256 grossCouponScaled = couponUsdcAmount * USDC_TO_18;
grossAPYBps = Math.mulDiv(grossCouponScaled * 365 days, BPS_DENOMINATOR, denominatorScaled);
uint256 netCouponScaled = netCouponUsdc * USDC_TO_18;
netAPYBps = Math.mulDiv(netCouponScaled * 365 days, BPS_DENOMINATOR, denominatorScaled);
}
}
}
// Calculate reward per unit and store epoch report for settlement/analytics
uint256 rewardPerUnitStored;
if (totalUnits > 0) {
rewardPerUnitStored = Math.mulDiv(totalReward, ACC_PRECISION, totalUnits);
allocated = Math.mulDiv(totalUnits, rewardPerUnitStored, ACC_PRECISION);
newDust = totalReward - allocated;
} else {
// No eligible units - carry forward as dust
rewardPerUnitStored = 0;
allocated = 0;
newDust = totalReward;
}
// Auto-mint protocol breakage share to the breakage sink (always excluded from accrual)
if (rewardPerUnitStored > 0) {
uint256 sinkUnits = treasuryUnitsThisEpoch + futureBreakageUnits;
if (sinkUnits > 0) {
uint256 sinkShare = Math.mulDiv(sinkUnits, rewardPerUnitStored, ACC_PRECISION);
address sinkAddr = breakageSink != address(0) ? breakageSink : treasury;
if (sinkAddr != address(0) && sinkShare > 0) {
_mintRewards(sinkAddr, sinkShare);
totalRewardsClaimed += sinkShare;
emit RewardClaimed(sinkAddr, sinkAddr, sinkShare, currentEpochId, currentEpochId);
}
}
}
// Update global cumulative index used for O(1) claims.
accRewardPerUnit += rewardPerUnitStored;
// Store epoch report for analytics and epoch-boundary finalization
// Use capped timestamp so late distributions still cap accrual at epochEnd
epochReport[currentEpochId] = EpochReport({
distributionTime: _cappedTimestamp(),
denominatorUnits: totalUnits,
deltaIndex: rewardPerUnitStored,
tokensAllocated: allocated,
dustCarry: newDust
});
// Update state
dust = newDust;
totalRewardsDeclared += allocated;
distributedThisEpoch = true;
// Update epoch minting counter
if (maxTokensToMintPerEpoch > 0) {
currentEpochTokensMinted += allocated;
}
lastDistributedEpochId = currentEpochId;
emit DistributionDeclared(
currentEpochId,
allocated,
totalUnits,
globalEligibleUnits,
treasuryUnitsThisEpoch,
futureBreakageUnits,
rewardPerUnitStored,
newDust,
grossAPYBps,
netAPYBps
);
// Reset current-epoch integrators after distribution.
// The values have been captured in epochReport and emitted; reset for cleanliness
// (configureEpoch will also reset when the next epoch starts)
globalEligibleUnits = 0;
treasuryUnitsThisEpoch = 0;
futureBreakageUnits = 0;
return (allocated, newDust);
}
// Users claim rewards using O(1) logic:
// - Prior epoch rewards are lazily credited to pendingRewards during settlement across epoch boundaries
// - Current epoch accrual is converted via accRewardPerUnit index minus rewardDebt baseline
// Claim = pendingRewards + (unitsAccrued * accIndex - rewardDebt).
function claim(address recipient) external returns (uint256 amount) {
if (recipient == address(0)) revert InvalidRecipient();
AccountState storage s = _accounts[msg.sender];
// Settle first to:
// - Accrue current-epoch units to now (capped)
// - Lazily credit prior epoch rewards into pendingRewards when crossing epoch boundary
_settleAccount(msg.sender);
uint64 endEpoch = lastDistributedEpochId;
if (endEpoch == 0) revert NoRewardsDeclared();
// Compute total claimable amount.
// pendingRewards = finalized prior epochs (credited at epoch boundaries)
// Current epoch contribution = unitsAccrued * accRewardPerUnit - rewardDebt
amount = s.pendingRewards;
if (s.unitsAccrued > 0 && accRewardPerUnit > 0) {
uint256 currentEpochReward = Math.mulDiv(s.unitsAccrued, accRewardPerUnit, ACC_PRECISION);
if (currentEpochReward > s.rewardDebt) {
amount += currentEpochReward - s.rewardDebt;
}
}
if (amount < minClaimTokens) revert ClaimTooSmall(amount, minClaimTokens);
// Optional per-transaction cap
if (maxClaimTokensPerTx > 0 && amount > maxClaimTokensPerTx) {
revert MaxClaimPerTxExceeded(amount, maxClaimTokensPerTx);
}
// Optional CR headroom guard
if (enforceCROnClaim) {
address pm = policyManager;
if (pm == address(0)) revert InvalidConfig();
address att = IPolicyAttestationRef(pm).collateralAttestation();
if (att == address(0)) revert InvalidConfig();
// Require fresh attestation data for solvency decisions
if (ICollateralAttestationView(att).isAttestationStale()) revert StaleAttestationForClaim();
uint256 cr = ICollateralAttestationView(att).getCollateralRatio(); // 18 decimals
uint256 L = IERC20(token).totalSupply();
// capSupply = floor(L * cr / 1e18)
uint256 capSupply = Math.mulDiv(L, cr, ACC_PRECISION);
uint256 headroom = capSupply > L ? capSupply - L : 0;
if (amount > headroom) {
revert ClaimExceedsHeadroom(amount, headroom);
}
}
// Update state - reset all accumulators
uint64 fromEpoch = s.lastClaimedEpoch + 1;
s.lastClaimedEpoch = endEpoch;
s.pendingRewards = 0;
s.unitsAccrued = 0;
s.rewardDebt = 0;
totalRewardsClaimed += amount;
_mintRewards(recipient, amount);
emit RewardClaimed(msg.sender, recipient, amount, fromEpoch, endEpoch);
}
// -------------------------------------------------------------------------
// V1.2 addition: Batch Claim Functions
// -------------------------------------------------------------------------
/// @notice Claim rewards on behalf of a user (admin only)
/// @param user The user whose rewards to claim
/// @param recipient Where to send the rewards (usually same as user)
/// @return amount The amount of rewards claimed
function claimFor(address user, address recipient)
external
onlyClaimAdmin
returns (uint256 amount)
{
if (user == address(0)) revert ZeroAddress();
if (recipient == address(0)) revert InvalidRecipient();
amount = _executeClaimFor(user, recipient);
emit ClaimForExecuted(msg.sender, user, recipient, amount);
}
/// @notice Batch claim for multiple users (admin only)
/// @param users Array of user addresses to claim for
/// @return totalClaimed Total amount claimed across all users
function batchClaimFor(address[] calldata users)
external
onlyClaimAdmin
returns (uint256 totalClaimed)
{
if (users.length > 200) revert BatchTooLarge(users.length, 200);
uint256 claimed;
uint256 processed;
for (uint256 i = 0; i < users.length; i++) {
address user = users[i];
if (user == address(0)) continue;
claimed = _executeClaimForSafe(user, user);
if (claimed > 0) {
totalClaimed += claimed;
processed++;
emit ClaimForExecuted(msg.sender, user, user, claimed);
}
}
emit BatchClaimExecuted(msg.sender, processed, totalClaimed);
}
/// @notice Get pending rewards for a user (view function for batch planning)
function getPendingForUser(address user) external view returns (uint256 pending) {
return this.pendingRewards(user);
}
/// @notice Get pending rewards for multiple users (view function for batch planning)
function getBatchPending(address[] calldata users)
external
view
returns (uint256[] memory amounts, uint256 total)
{
amounts = new uint256[](users.length);
for (uint256 i = 0; i < users.length; i++) {
amounts[i] = this.pendingRewards(users[i]);
total += amounts[i];
}
}
/// @dev Execute claim for a user, sending to recipient. Reverts if nothing to claim.
function _executeClaimFor(address user, address recipient) internal returns (uint256 amount) {
AccountState storage s = _accounts[user];
_settleAccount(user);
uint64 endEpoch = lastDistributedEpochId;
if (endEpoch == 0) revert NoRewardsDeclared();
amount = s.pendingRewards;
if (s.unitsAccrued > 0 && accRewardPerUnit > 0) {
uint256 currentEpochReward = Math.mulDiv(s.unitsAccrued, accRewardPerUnit, ACC_PRECISION);
if (currentEpochReward > s.rewardDebt) {
amount += currentEpochReward - s.rewardDebt;
}
}
if (amount == 0) revert NothingToClaim();
uint64 fromEpoch = s.lastClaimedEpoch + 1;
s.lastClaimedEpoch = endEpoch;
s.pendingRewards = 0;
s.unitsAccrued = 0;
s.rewardDebt = 0;
totalRewardsClaimed += amount;
_mintRewards(recipient, amount);
emit RewardClaimed(user, recipient, amount, fromEpoch, endEpoch);
}
/// @dev Safe version that returns 0 instead of reverting on no rewards
function _executeClaimForSafe(address user, address recipient) internal returns (uint256 amount) {
AccountState storage s = _accounts[user];
_settleAccount(user);
uint64 endEpoch = lastDistributedEpochId;
if (endEpoch == 0) return 0;
amount = s.pendingRewards;
if (s.unitsAccrued > 0 && accRewardPerUnit > 0) {
uint256 currentEpochReward = Math.mulDiv(s.unitsAccrued, accRewardPerUnit, ACC_PRECISION);
if (currentEpochReward > s.rewardDebt) {
amount += currentEpochReward - s.rewardDebt;
}
}
if (amount == 0) return 0;
uint64 fromEpoch = s.lastClaimedEpoch + 1;
s.lastClaimedEpoch = endEpoch;
s.pendingRewards = 0;
s.unitsAccrued = 0;
s.rewardDebt = 0;
totalRewardsClaimed += amount;
_mintRewards(recipient, amount);
emit RewardClaimed(user, recipient, amount, fromEpoch, endEpoch);
}
// -------------------------------------------------------------------------
// Public view functions
// -------------------------------------------------------------------------
/// @notice Calculate pending rewards for an account
/// @dev O(1): Simulates settle to now and computes pendingRewards + (unitsAccrued * accIndex - rewardDebt)
/// Full formula includes both finalized prior epochs and current epoch accrual.
function pendingRewards(address account) external view returns (uint256 reward) {
AccountState storage s = _accounts[account];
if (s.excluded) return 0;
// Start with already-finalized prior epoch rewards
reward = s.pendingRewards;
// Simulate epoch-boundary finalization if needed
uint256 simulatedUnits = s.unitsAccrued;
uint256 simulatedDebt = s.rewardDebt;
uint64 simulatedEpoch = s.lastAccruedEpoch;
uint64 simulatedLastAccrualTime = s.lastAccrualTime;
// If crossing epoch boundary, simulate finalization
if (simulatedEpoch > 0 && simulatedEpoch < currentEpochId) {
EpochReport storage report = epochReport[simulatedEpoch];
// Calculate earning balance for the prior epoch being finalized
uint256 priorEpochEarning = s.balance;
if (s.lateInflowEpoch == simulatedEpoch && s.lateInflow > 0) {
priorEpochEarning = priorEpochEarning > s.lateInflow ? priorEpochEarning - s.lateInflow : 0;
}
// Accrue remaining time in prior epoch up to distribution time (not epoch end)
if (s.eligible && priorEpochEarning > 0 && report.distributionTime > 0) {
if (simulatedLastAccrualTime < report.distributionTime) {
uint256 remainingElapsed = uint256(report.distributionTime - simulatedLastAccrualTime);
simulatedUnits += priorEpochEarning * remainingElapsed;
}
}
// Finalize prior epoch
if (report.deltaIndex > 0 && simulatedUnits > 0) {
reward += Math.mulDiv(simulatedUnits, report.deltaIndex, ACC_PRECISION);
}
// Handle multi-epoch gaps (use distributionTime - epochStart, not full duration)
uint64 nextEpoch = simulatedEpoch + 1;
while (nextEpoch < currentEpochId) {
EpochReport storage gapReport = epochReport[nextEpoch];
// Calculate earning balance for this gap epoch
uint256 gapEarning = s.balance;
if (s.lateInflowEpoch == nextEpoch && s.lateInflow > 0) {
gapEarning = gapEarning > s.lateInflow ? gapEarning - s.lateInflow : 0;
}
if (gapReport.deltaIndex > 0 && gapEarning > 0 && gapReport.distributionTime > 0) {
uint64 gapStart = epochStartTime[nextEpoch];
if (gapReport.distributionTime > gapStart) {
uint256 gapUnits = gapEarning * uint256(gapReport.distributionTime - gapStart);
reward += Math.mulDiv(gapUnits, gapReport.deltaIndex, ACC_PRECISION);
}
}
nextEpoch++;
}
// Reset simulated accumulators for current epoch
simulatedUnits = 0;
simulatedDebt = 0;
simulatedLastAccrualTime = epochStartTime[currentEpochId];
// After epoch boundary, simulate from current epoch start
simulatedEpoch = currentEpochId;
}
// Calculate earning balance for current epoch
// Late inflows don't earn until the next checkpoint
uint256 currentEarning = s.balance;
if (s.lateInflowEpoch == currentEpochId && s.lateInflow > 0) {
currentEarning = currentEarning > s.lateInflow ? currentEarning - s.lateInflow : 0;
}
// Simulate current-epoch accrual to now
// After epoch rollover, start from the appropriate point
uint64 now_ = _cappedTimestamp();
uint64 accrualStart = simulatedLastAccrualTime;
// After epoch boundary crossing, eligibility resets (fresh each epoch)
// Also handle the case where user crossed boundary
bool simulatedEligible = s.eligible;
if (s.lastAccruedEpoch > 0 && s.lastAccruedEpoch < currentEpochId && !s.excluded) {
simulatedEligible = true; // Fresh eligibility each epoch
uint64 epochStartCurrent = epochStartTime[currentEpochId];
if (epochStartCurrent > accrualStart) {
accrualStart = epochStartCurrent;
}
}
// If distribution happened since accrualStart, split the simulation.
if (distributedThisEpoch && simulatedEligible && currentEarning > 0 && now_ > accrualStart) {
EpochReport storage currentReport = epochReport[currentEpochId];
if (currentReport.distributionTime > 0 && accrualStart < currentReport.distributionTime) {
// Simulate pre-distribution accrual (finalized at deltaIndex)
uint256 preDistElapsed = uint256(currentReport.distributionTime - accrualStart);
uint256 preDistUnits = currentEarning * preDistElapsed;
if (preDistUnits > 0 && currentReport.deltaIndex > 0) {
reward += Math.mulDiv(preDistUnits, currentReport.deltaIndex, ACC_PRECISION);
}
// Simulate post-distribution accrual
if (now_ > currentReport.distributionTime) {
uint256 postDistElapsed = uint256(now_ - currentReport.distributionTime);
uint256 postDistUnits = currentEarning * postDistElapsed;
simulatedUnits += postDistUnits;
simulatedDebt += Math.mulDiv(postDistUnits, accRewardPerUnit, ACC_PRECISION);
}
} else if (now_ > accrualStart) {
// No distribution crossing, normal accrual
uint256 elapsed = uint256(now_ - accrualStart);
uint256 deltaUnits = currentEarning * elapsed;
simulatedUnits += deltaUnits;
simulatedDebt += Math.mulDiv(deltaUnits, accRewardPerUnit, ACC_PRECISION);
}
} else if (now_ > accrualStart && simulatedEligible && currentEarning > 0) {
uint256 elapsed = uint256(now_ - accrualStart);
uint256 deltaUnits = currentEarning * elapsed;
simulatedUnits += deltaUnits;
simulatedDebt += Math.mulDiv(deltaUnits, accRewardPerUnit, ACC_PRECISION);
}
// Add current epoch contribution: unitsAccrued * accIndex - rewardDebt
if (simulatedUnits > 0 && accRewardPerUnit > 0) {
uint256 currentEpochReward = Math.mulDiv(simulatedUnits, accRewardPerUnit, ACC_PRECISION);
if (currentEpochReward > simulatedDebt) {
reward += currentEpochReward - simulatedDebt;
}
}
}
/// @notice Get accrued units for current epoch (before distribution)
/// @dev Useful to see time-weighted participation before rewards are calculated
function accruedUnitsThisEpoch(address account) external view returns (uint256 units) {
AccountState storage s = _accounts[account];
if (s.excluded || !s.eligible) return 0;
units = s.unitsAccrued;
// Calculate earning balance (exclude late inflows)
uint256 earningBalance = s.balance;
if (s.lateInflowEpoch == currentEpochId && s.lateInflow > 0) {
earningBalance = earningBalance > s.lateInflow ? earningBalance - s.lateInflow : 0;
}
// Simulate accrual to current time
uint64 now_ = _cappedTimestamp();
if (now_ > s.lastAccrualTime && earningBalance > 0) {
units += earningBalance * uint256(now_ - s.lastAccrualTime);
}
}
/// @notice Get full account state for UI/debugging
function getAccountFullState(address account)
external
view
returns (
uint256 balance,
uint64 lastClaimedEpoch,
uint64 lastAccrualTime,
uint64 lastInflow,
uint256 unitsAccrued,
bool excluded,
bool eligible
)
{
AccountState storage s = _accounts[account];
return (
s.balance,
s.lastClaimedEpoch,
s.lastAccrualTime,
s.lastInflow,
s.unitsAccrued,
s.excluded,
s.eligible
);
}
/// @notice Get checkpoint eligibility status for an account
function getEligibilityStatus(address account)
external
view
returns (
bool isEligible,
bool isExcluded,
bool isLateEntry,
uint64 checkpointStart_,
uint64 checkpointEnd_
)
{
AccountState storage s = _accounts[account];
isEligible = s.eligible && !s.excluded;
isExcluded = s.excluded;
// Late entry if lastInflow is after checkpoint start
isLateEntry = s.lastInflow >= checkpointStart && checkpointStart > 0;
checkpointStart_ = checkpointStart;
checkpointEnd_ = checkpointEnd;
}
/// @notice Compact checkpoint status for an account
/// @dev hasFailedThisEpoch reflects late entry (ineligible due to buying after checkpointStart).
/// Pre-checkpoint partial sells do not mark failure in the proportional-breakage model.
function getCheckpointStatus(address account)
external
view
returns (
bool isEligible_,
bool hasFailedThisEpoch,
bool canEarnThisEpoch
)
{
AccountState storage s = _accounts[account];
uint64 now_ = _cappedTimestamp();
bool withinEpoch = (epochEnd == 0 ? false : now_ < epochEnd);
isEligible_ = s.eligible && !s.excluded;
bool lateWindow = (checkpointStart > 0 && now_ >= checkpointStart && now_ < epochEnd);
// Failure here represents late entry only (not pre-checkpoint sells in proportional model)
hasFailedThisEpoch = (!s.eligible && lateWindow);
canEarnThisEpoch = isEligible_ && withinEpoch;
}
/// @notice Get current epoch info
function getEpochInfo()
external
view
returns (
uint64 epochId,
uint64 start,
uint64 end,
uint64 checkpointStart_,
uint64 checkpointEnd_,
bool distributed
)
{
return (
currentEpochId,
epochStart,
epochEnd,
checkpointStart,
checkpointEnd,
distributedThisEpoch
);
}
/// @notice Get the active checkpoint window for the current epoch
function getCheckpointWindow()
external
view
returns (uint64 start, uint64 end, uint64 epochId)
{
return (checkpointStart, checkpointEnd, currentEpochId);
}
/// @notice Get global integrator state for debugging
function getGlobalState()
external
view
returns (
uint256 eligibleUnits,
uint256 eligibleSupply,
uint256 treasuryBreakage,
uint256 futureBreakage,
uint256 totalBreakage,
uint64 lastUpdateTime
)
{
return (
globalEligibleUnits,
currentEligibleSupply,
treasuryUnitsThisEpoch,
futureBreakageUnits,
totalBreakageAllTime,
lastGlobalUpdateTime
);
}
/// @notice Get epoch report for analytics
/// @param epochId The epoch to query
/// @return distributionTime Timestamp when distribute() was called
/// @return denominatorUnits Total units used as denominator (eligible + breakage)
/// @return deltaIndex Reward per unit for this epoch (scaled by ACC_PRECISION)
/// @return tokensAllocated Total tokens allocated this epoch
/// @return dustCarry Dust carried forward from this distribution
function getEpochReport(uint64 epochId)
external
view
returns (
uint64 distributionTime,
uint256 denominatorUnits,
uint256 deltaIndex,
uint256 tokensAllocated,
uint256 dustCarry
)
{
EpochReport storage report = epochReport[epochId];
return (
report.distributionTime,
report.denominatorUnits,
report.deltaIndex,
report.tokensAllocated,
report.dustCarry
);
}
// -------------------------------------------------------------------------
// Internal logic
// -------------------------------------------------------------------------
/// @notice Returns current timestamp capped to epoch boundaries
/// @dev Ensures accrual calculations stay within epoch bounds
function _cappedTimestamp() internal view returns (uint64) {
uint64 now_ = uint64(block.timestamp);
// No epoch configured - return current time
if (epochEnd == 0) {
return now_;
}
// Before epoch start - return epoch start
if (now_ < epochStart) {
return epochStart;
}
// After epoch end - return epoch end
if (now_ > epochEnd) {
return epochEnd;
}
return now_;
}
/// @notice Accrues global eligible units up to current (capped) time
/// @dev Must be called before any operation that changes eligible supply
function _accrueGlobal() internal {
uint64 now_ = _cappedTimestamp();
// Skip if no time has elapsed or epoch not started
if (now_ <= lastGlobalUpdateTime || lastGlobalUpdateTime == 0) {
return;
}
uint256 elapsed = uint256(now_ - lastGlobalUpdateTime);
// Accumulate: eligible supply * seconds elapsed
if (elapsed > 0 && currentEligibleSupply > 0) {
globalEligibleUnits += currentEligibleSupply * elapsed;
}
lastGlobalUpdateTime = now_;
}
// Processes balance decreases (transfers out/redemptions) after settling accrual.
// Keeps units accurate even when balances bounce rapidly within a block.
function _handleOutflow(address account, uint256 amount) internal {
if (amount == 0) return;
// Accrue global units first (order matters!)
_accrueGlobal();
// Settle account's units up to now
_settleAccount(account);
AccountState storage s = _accounts[account];
uint256 balance = s.balance;
if (balance < amount) revert BalanceUnderflow();
// Calculate how much comes from late inflow vs earning balance
// Sell non-earning tokens (lateInflow) first to preserve earning balance
uint256 fromLateInflow = 0;
if (s.lateInflowEpoch == currentEpochId && s.lateInflow > 0) {
fromLateInflow = amount > s.lateInflow ? s.lateInflow : amount;
s.lateInflow -= fromLateInflow;
}
uint256 fromEarning = amount - fromLateInflow;
// Update eligible supply only for the earning portion
// (lateInflow was never added to eligibleSupply, so don't double-subtract)
if (fromEarning > 0 && !s.excluded && s.eligible) {
if (currentEligibleSupply >= fromEarning) {
currentEligibleSupply -= fromEarning;
} else {
currentEligibleSupply = 0; // Safety check
}
}
// Update totalExcludedSupply for excluded accounts
// Keeps excluded supply in sync with actual excluded balances
if (s.excluded) {
if (totalExcludedSupply >= amount) {
totalExcludedSupply -= amount;
} else {
totalExcludedSupply = 0;
}
}
// Breakage logic (only applies to earning portion).
// - Pre-checkpoint: forfeit (fromEarning/earningBalance) of CURRENT-EPOCH accrued units to treasury
// - Post-checkpoint: capture remaining days as future breakage (fromEarning * (epochEnd - now))
{
uint64 now_ = _cappedTimestamp();
// Calculate earning balance for breakage denominator
uint256 earningBalance = balance;
if (s.lateInflowEpoch == currentEpochId) {
// Note: lateInflow was already reduced above, so add fromLateInflow back for accurate calculation
uint256 originalLateInflow = s.lateInflow + fromLateInflow;
earningBalance = earningBalance > originalLateInflow ? earningBalance - originalLateInflow : 0;
}
if (
now_ >= epochStart && now_ < checkpointEnd && s.eligible && !s.excluded && earningBalance > 0 && fromEarning > 0
) {
// Proportional forfeit of current-epoch units based on earning portion sold
uint256 forfeitedUnits = Math.mulDiv(s.unitsAccrued, fromEarning, earningBalance);
if (forfeitedUnits > 0) {
if (forfeitedUnits > s.unitsAccrued) {
forfeitedUnits = s.unitsAccrued; // safety cap
}
s.unitsAccrued -= forfeitedUnits;
treasuryUnitsThisEpoch += forfeitedUnits;
totalBreakageAllTime += forfeitedUnits;
// Scale rewardDebt proportionally to removed units.
// This maintains the invariant: pending = unitsAccrued * accIndex - rewardDebt
uint256 forfeitedDebt = Math.mulDiv(s.rewardDebt, fromEarning, earningBalance);
if (forfeitedDebt > s.rewardDebt) {
forfeitedDebt = s.rewardDebt; // safety cap
}
s.rewardDebt -= forfeitedDebt;
emit ProportionalBreakage(account, fromEarning, forfeitedUnits, currentEpochId);
}
} else if (now_ >= checkpointEnd && now_ < epochEnd && s.eligible && !s.excluded && fromEarning > 0) {
// Future breakage: DAO captures remaining days for the earning portion sold
uint256 remainingSeconds = uint256(epochEnd - now_);
if (remainingSeconds > 0) {
uint256 futureUnits = fromEarning * remainingSeconds;
futureBreakageUnits += futureUnits;
totalBreakageAllTime += futureUnits;
emit FutureBreakage(account, fromEarning, futureUnits, remainingSeconds, currentEpochId);
}
}
}
s.balance = balance - amount;
}
// Processes balance increases (transfers in/mints) after settling accrual.
// Records a fresh inflow timestamp for telemetry (not used for eligibility).
function _handleInflow(address account, uint256 amount) internal {
if (amount == 0) return;
// Accrue global units first (order matters!)
_accrueGlobal();
// Settle account's units up to now
_settleAccount(account);
AccountState storage s = _accounts[account];
// Track inflow timing for telemetry ("when did this account last receive tokens")
// Note: Eligibility is determined by block.timestamp at inflow, not this stored value
s.lastInflow = uint64(block.timestamp);
uint64 now_ = _cappedTimestamp();
// Late entry rule: inflows at/after checkpointStart do not earn this epoch
bool isLateEntry = (checkpointStart > 0 && now_ >= checkpointStart && now_ < epochEnd);
// Update balance
s.balance += amount;
if (isLateEntry) {
// Track late inflows instead of disqualifying
// These tokens don't earn until the next checkpoint
// Lazy reset: if this is a new epoch, reset lateInflow
if (s.lateInflowEpoch != currentEpochId) {
s.lateInflow = 0;
s.lateInflowEpoch = currentEpochId;
}
s.lateInflow += amount;
// Do NOT add to currentEligibleSupply - these tokens can't earn this epoch
// But still mark eligible so they can earn on their pre-checkpoint balance
if (!s.excluded && !s.eligible) {
s.eligible = true;
}
} else {
// Normal inflow before checkpoint - add to eligible supply
if (!s.excluded) {
s.eligible = true;
currentEligibleSupply += amount;
}
}
// Update totalExcludedSupply for excluded accounts
// Keeps excluded supply in sync with actual excluded balances
if (s.excluded) {
totalExcludedSupply += amount;
}
}
/// @notice Settles an account's accrued units up to current (capped) time
/// @dev Called before any balance change to ensure accurate unit tracking
/// @dev Uses epoch-boundary finalization instead of per-epoch reconstruction.
/// - At epoch boundary: credit unitsAccrued * deltaIndex to pendingRewards, reset accumulators
/// - This ensures units are finalized at the exact values tracked during the epoch
function _settleAccount(address account) internal {
AccountState storage s = _accounts[account];
uint64 now_ = _cappedTimestamp();
// Excluded accounts don't accrue
if (s.excluded) {
s.lastAccrualTime = now_;
return;
}
// Initialize epoch tracking on first touch
if (s.lastAccruedEpoch == 0 && currentEpochId > 0) {
s.lastAccruedEpoch = currentEpochId;
if (s.lastAccrualTime == 0) {
s.lastAccrualTime = epochStart > 0 ? epochStart : now_;
}
s.eligible = true;
}
// Epoch-boundary finalization.
// If crossing into a new epoch, finalize prior epoch(s) using epochReport.deltaIndex
if (s.lastAccruedEpoch > 0 && s.lastAccruedEpoch < currentEpochId) {
EpochReport storage report = epochReport[s.lastAccruedEpoch];
// Calculate earning balance for the epoch being finalized
// Late inflows don't earn until the next checkpoint
uint256 priorEpochEarning = s.balance;
if (s.lateInflowEpoch == s.lastAccruedEpoch && s.lateInflow > 0) {
priorEpochEarning = priorEpochEarning > s.lateInflow ? priorEpochEarning - s.lateInflow : 0;
}
// Accrue remaining time in prior epoch up to distribution time (not epoch end)
// Post-distribution time in the prior epoch earns nothing (only one distribution per epoch)
if (s.eligible && priorEpochEarning > 0 && report.distributionTime > 0) {
if (s.lastAccrualTime < report.distributionTime) {
// User has time before distribution - accrue up to distribution
uint256 remainingElapsed = uint256(report.distributionTime - s.lastAccrualTime);
s.unitsAccrued += priorEpochEarning * remainingElapsed;
}
// Time after distribution in the prior epoch earns nothing
}
// Finalize the prior epoch: credit unitsAccrued at that epoch's deltaIndex
if (report.deltaIndex > 0 && s.unitsAccrued > 0) {
s.pendingRewards += Math.mulDiv(s.unitsAccrued, report.deltaIndex, ACC_PRECISION);
}
// Handle multi-epoch gaps (rare: user inactive for multiple epochs)
// For completely missed epochs, use earning balance * (distributionTime - epochStart)
uint64 nextEpoch = s.lastAccruedEpoch + 1;
while (nextEpoch < currentEpochId) {
EpochReport storage gapReport = epochReport[nextEpoch];
// Calculate earning balance for this gap epoch
uint256 gapEarning = s.balance;
if (s.lateInflowEpoch == nextEpoch && s.lateInflow > 0) {
gapEarning = gapEarning > s.lateInflow ? gapEarning - s.lateInflow : 0;
}
if (gapReport.deltaIndex > 0 && gapEarning > 0 && gapReport.distributionTime > 0) {
uint64 gapStart = epochStartTime[nextEpoch];
// Only count time up to distribution (post-distribution earns nothing)
if (gapReport.distributionTime > gapStart) {
uint256 gapUnits = gapEarning * uint256(gapReport.distributionTime - gapStart);
s.pendingRewards += Math.mulDiv(gapUnits, gapReport.deltaIndex, ACC_PRECISION);
}
}
nextEpoch++;
}
// Reset accumulators for the new epoch
s.unitsAccrued = 0;
s.rewardDebt = 0;
s.lastAccruedEpoch = currentEpochId;
s.lastAccrualTime = epochStart > 0 ? epochStart : now_;
s.eligible = true; // Fresh eligibility each epoch
}
// Calculate elapsed time since last accrual
if (now_ <= s.lastAccrualTime) {
return; // No time elapsed
}
// Calculate earning balance for current epoch
// Late inflows don't earn until the next checkpoint
uint256 currentEarning = s.balance;
if (s.lateInflowEpoch == currentEpochId && s.lateInflow > 0) {
currentEarning = currentEarning > s.lateInflow ? currentEarning - s.lateInflow : 0;
}
// If distribution happened since lastAccrualTime, split the accrual.
// Pre-distribution units get finalized at deltaIndex; post-distribution units use rewardDebt
if (distributedThisEpoch && s.eligible && currentEarning > 0) {
EpochReport storage currentReport = epochReport[currentEpochId];
if (currentReport.distributionTime > 0 && s.lastAccrualTime < currentReport.distributionTime) {
// Accrue units from lastAccrualTime to distributionTime
uint256 preDistElapsed = uint256(currentReport.distributionTime - s.lastAccrualTime);
uint256 preDistUnits = currentEarning * preDistElapsed;
// Finalize pre-distribution units at deltaIndex (no rewardDebt - index was 0)
if (preDistUnits > 0 && currentReport.deltaIndex > 0) {
s.pendingRewards += Math.mulDiv(preDistUnits, currentReport.deltaIndex, ACC_PRECISION);
}
// Now accrue post-distribution units (from distributionTime to now)
if (now_ > currentReport.distributionTime) {
uint256 postDistElapsed = uint256(now_ - currentReport.distributionTime);
uint256 postDistUnits = currentEarning * postDistElapsed;
s.unitsAccrued += postDistUnits;
s.rewardDebt += Math.mulDiv(postDistUnits, accRewardPerUnit, ACC_PRECISION);
}
s.lastAccrualTime = now_;
return;
}
}
uint256 elapsed = uint256(now_ - s.lastAccrualTime);
// Accrue current-epoch units: earning balance * elapsed time (only if eligible)
if (elapsed > 0 && s.eligible && currentEarning > 0) {
uint256 deltaUnits = currentEarning * elapsed;
s.unitsAccrued += deltaUnits;
// Baseline rewardDebt at current accRewardPerUnit.
s.rewardDebt += Math.mulDiv(deltaUnits, accRewardPerUnit, ACC_PRECISION);
}
s.lastAccrualTime = now_;
}
// Simple wrapper so we can swap reward token interface in the future.
// Skips work when amount is zero to avoid extra hook calls downstream.
// Note: Token.mint() has access enforcement built-in.
function _mintRewards(address recipient, uint256 amount) internal {
if (amount == 0) return;
address token_ = token;
if (token_ == address(0)) revert InvalidConfig();
IRewardsMintable(token_).mint(recipient, amount);
}
// Admin escape hatch—recover foreign tokens without risking BUCK drain.
// Only whitelisted sinks can receive rescued assets and BUCK is explicitly blocked.
function recoverERC20(address token_, address to, uint256 amount)
external
onlyRole(ADMIN_ROLE)
{
if (token_ == address(0) || to == address(0)) revert ZeroAddress();
if (!isRecoverySink[to]) revert InvalidRecoverySink(to);
if (amount == 0) revert InvalidAmount();
address canonical = token;
if (canonical != address(0) && token_ == canonical) {
revert UnsupportedRecoveryAsset(token_);
}
IERC20(token_).safeTransfer(to, amount);
emit TokensRecovered(msg.sender, token_, to, amount);
}
// -------------------------------------------------------------------------
// Storage Gap
// -------------------------------------------------------------------------
// Reserved storage space to allow for layout changes in the future.
// Trim the gap if future revisions append new state variables.
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
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);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 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 {
using Address for address;
/**
* @dev An operation with an ERC20 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.
*/
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.
*/
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.
*/
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 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).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
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 silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
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 overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
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 division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return 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.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev 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^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + 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^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 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^256 / 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^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
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^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// 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^256. Since the preconditions guarantee that the outcome is
// less than 2^256, 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;
}
}
/**
* @notice 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) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice 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 + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @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;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 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 + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @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 + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @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;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 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 + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @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;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {ERC165Upgradeable} from "../utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.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 AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControl, ERC165Upgradeable {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/// @custom:storage-location erc7201:openzeppelin.storage.AccessControl
struct AccessControlStorage {
mapping(bytes32 role => RoleData) _roles;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControl")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant AccessControlStorageLocation = 0x02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800;
function _getAccessControlStorage() private pure returns (AccessControlStorage storage $) {
assembly {
$.slot := AccessControlStorageLocation
}
}
/**
* @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);
_;
}
function __AccessControl_init() internal onlyInitializing {
}
function __AccessControl_init_unchained() internal onlyInitializing {
}
/**
* @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) {
AccessControlStorage storage $ = _getAccessControlStorage();
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) {
AccessControlStorage storage $ = _getAccessControlStorage();
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 {
AccessControlStorage storage $ = _getAccessControlStorage();
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) {
AccessControlStorage storage $ = _getAccessControlStorage();
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) {
AccessControlStorage storage $ = _getAccessControlStorage();
if (hasRole(role, account)) {
$._roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)
pragma solidity ^0.8.20;
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
/// @custom:storage-location erc7201:openzeppelin.storage.Pausable
struct PausableStorage {
bool _paused;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Pausable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant PausableStorageLocation = 0xcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300;
function _getPausableStorage() private pure returns (PausableStorage storage $) {
assembly {
$.slot := PausableStorageLocation
}
}
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
/**
* @dev The operation failed because the contract is paused.
*/
error EnforcedPause();
/**
* @dev The operation failed because the contract is not paused.
*/
error ExpectedPause();
/**
* @dev Initializes the contract in unpaused state.
*/
function __Pausable_init() internal onlyInitializing {
__Pausable_init_unchained();
}
function __Pausable_init_unchained() internal onlyInitializing {
PausableStorage storage $ = _getPausableStorage();
$._paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
PausableStorage storage $ = _getPausableStorage();
return $._paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
PausableStorage storage $ = _getPausableStorage();
$._paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
PausableStorage storage $ = _getPausableStorage();
$._paused = false;
emit Unpaused(_msgSender());
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Multicall.sol)
pragma solidity ^0.8.20;
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {ContextUpgradeable} from "./ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Provides a function to batch together multiple calls in a single external call.
*
* Consider any assumption about calldata validation performed by the sender may be violated if it's not especially
* careful about sending transactions invoking {multicall}. For example, a relay address that filters function
* selectors won't filter calls nested within a {multicall} operation.
*
* NOTE: Since 5.0.1 and 4.9.4, this contract identifies non-canonical contexts (i.e. `msg.sender` is not {_msgSender}).
* If a non-canonical context is identified, the following self `delegatecall` appends the last bytes of `msg.data`
* to the subcall. This makes it safe to use with {ERC2771Context}. Contexts that don't affect the resolution of
* {_msgSender} are not propagated to subcalls.
*/
abstract contract MulticallUpgradeable is Initializable, ContextUpgradeable {
function __Multicall_init() internal onlyInitializing {
}
function __Multicall_init_unchained() internal onlyInitializing {
}
/**
* @dev Receives and executes a batch of function calls on this contract.
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
bytes memory context = msg.sender == _msgSender()
? new bytes(0)
: msg.data[msg.data.length - _contextSuffixLength():];
results = new bytes[](data.length);
for (uint256 i = 0; i < data.length; i++) {
results[i] = Address.functionDelegateCall(address(this), bytes.concat(data[i], context));
}
return results;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/UUPSUpgradeable.sol)
pragma solidity ^0.8.20;
import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
import {Initializable} from "./Initializable.sol";
/**
* @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
* {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
*
* A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
* reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
* `UUPSUpgradeable` with a custom implementation of upgrades.
*
* The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
*/
abstract contract UUPSUpgradeable is Initializable, IERC1822Proxiable {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address private immutable __self = address(this);
/**
* @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)`
* and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
* while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string.
* If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must
* be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
* during an upgrade.
*/
string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";
/**
* @dev The call is from an unauthorized context.
*/
error UUPSUnauthorizedCallContext();
/**
* @dev The storage `slot` is unsupported as a UUID.
*/
error UUPSUnsupportedProxiableUUID(bytes32 slot);
/**
* @dev Check that the execution is being performed through a delegatecall call and that the execution context is
* a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
* for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
* function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
* fail.
*/
modifier onlyProxy() {
_checkProxy();
_;
}
/**
* @dev Check that the execution is not being performed through a delegate call. This allows a function to be
* callable on the implementing contract but not through proxies.
*/
modifier notDelegated() {
_checkNotDelegated();
_;
}
function __UUPSUpgradeable_init() internal onlyInitializing {
}
function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
}
/**
* @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
* implementation. It is used to validate the implementation's compatibility when performing an upgrade.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
*/
function proxiableUUID() external view virtual notDelegated returns (bytes32) {
return ERC1967Utils.IMPLEMENTATION_SLOT;
}
/**
* @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
* encoded in `data`.
*
* Calls {_authorizeUpgrade}.
*
* Emits an {Upgraded} event.
*
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallUUPS(newImplementation, data);
}
/**
* @dev Reverts if the execution is not performed via delegatecall or the execution
* context is not of a proxy with an ERC1967-compliant implementation pointing to self.
* See {_onlyProxy}.
*/
function _checkProxy() internal view virtual {
if (
address(this) == __self || // Must be called through delegatecall
ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
) {
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Reverts if the execution is performed via delegatecall.
* See {notDelegated}.
*/
function _checkNotDelegated() internal view virtual {
if (address(this) != __self) {
// Must not be called through delegatecall
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
* {upgradeToAndCall}.
*
* Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
*
* ```solidity
* function _authorizeUpgrade(address) internal onlyOwner {}
* ```
*/
function _authorizeUpgrade(address newImplementation) internal virtual;
/**
* @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call.
*
* As a security check, {proxiableUUID} is invoked in the new implementation, and the return value
* is expected to be the implementation slot in ERC1967.
*
* Emits an {IERC1967-Upgraded} event.
*/
function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private {
try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
revert UUPSUnsupportedProxiableUUID(slot);
}
ERC1967Utils.upgradeToAndCall(newImplementation, data);
} catch {
// The implementation is not UUPS
revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @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 AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @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
* {FailedInnerCall} 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 AddressInsufficientBalance(address(this));
}
(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 {FailedInnerCall}) 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 {FailedInnerCall} 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 {FailedInnerCall}.
*/
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
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)
pragma solidity ^0.8.20;
/**
* @dev External interface of AccessControl declared to support ERC165 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, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
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;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @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 ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
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;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 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 ERC165Upgradeable is Initializable, IERC165 {
function __ERC165_init() internal onlyInitializing {
}
function __ERC165_init_unchained() internal onlyInitializing {
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC1822.sol)
pragma solidity ^0.8.20;
/**
* @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
* proxy whose upgrades are fully controlled by the current implementation.
*/
interface IERC1822Proxiable {
/**
* @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
* address.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy.
*/
function proxiableUUID() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Utils.sol)
pragma solidity ^0.8.20;
import {IBeacon} from "../beacon/IBeacon.sol";
import {Address} from "../../utils/Address.sol";
import {StorageSlot} from "../../utils/StorageSlot.sol";
/**
* @dev This abstract contract provides getters and event emitting update functions for
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
*/
library ERC1967Utils {
// We re-declare ERC-1967 events here because they can't be used directly from IERC1967.
// This will be fixed in Solidity 0.8.21. At that point we should remove these events.
/**
* @dev Emitted when the implementation is upgraded.
*/
event Upgraded(address indexed implementation);
/**
* @dev Emitted when the admin account has changed.
*/
event AdminChanged(address previousAdmin, address newAdmin);
/**
* @dev Emitted when the beacon is changed.
*/
event BeaconUpgraded(address indexed beacon);
/**
* @dev Storage slot with the address of the current implementation.
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/**
* @dev The `implementation` of the proxy is invalid.
*/
error ERC1967InvalidImplementation(address implementation);
/**
* @dev The `admin` of the proxy is invalid.
*/
error ERC1967InvalidAdmin(address admin);
/**
* @dev The `beacon` of the proxy is invalid.
*/
error ERC1967InvalidBeacon(address beacon);
/**
* @dev An upgrade function sees `msg.value > 0` that may be lost.
*/
error ERC1967NonPayable();
/**
* @dev Returns the current implementation address.
*/
function getImplementation() internal view returns (address) {
return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 implementation slot.
*/
function _setImplementation(address newImplementation) private {
if (newImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(newImplementation);
}
StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
}
/**
* @dev Performs implementation upgrade with additional setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-Upgraded} event.
*/
function upgradeToAndCall(address newImplementation, bytes memory data) internal {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
if (data.length > 0) {
Address.functionDelegateCall(newImplementation, data);
} else {
_checkNonPayable();
}
}
/**
* @dev Storage slot with the admin of the contract.
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/**
* @dev Returns the current admin.
*
* TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
* the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
*/
function getAdmin() internal view returns (address) {
return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 admin slot.
*/
function _setAdmin(address newAdmin) private {
if (newAdmin == address(0)) {
revert ERC1967InvalidAdmin(address(0));
}
StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
}
/**
* @dev Changes the admin of the proxy.
*
* Emits an {IERC1967-AdminChanged} event.
*/
function changeAdmin(address newAdmin) internal {
emit AdminChanged(getAdmin(), newAdmin);
_setAdmin(newAdmin);
}
/**
* @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
* This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
/**
* @dev Returns the current beacon.
*/
function getBeacon() internal view returns (address) {
return StorageSlot.getAddressSlot(BEACON_SLOT).value;
}
/**
* @dev Stores a new beacon in the EIP1967 beacon slot.
*/
function _setBeacon(address newBeacon) private {
if (newBeacon.code.length == 0) {
revert ERC1967InvalidBeacon(newBeacon);
}
StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;
address beaconImplementation = IBeacon(newBeacon).implementation();
if (beaconImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(beaconImplementation);
}
}
/**
* @dev Change the beacon and trigger a setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-BeaconUpgraded} event.
*
* CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
* it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
* efficiency.
*/
function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
_setBeacon(newBeacon);
emit BeaconUpgraded(newBeacon);
if (data.length > 0) {
Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
} else {
_checkNonPayable();
}
}
/**
* @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
* if an upgrade doesn't perform an initialization call.
*/
function _checkNonPayable() private {
if (msg.value > 0) {
revert ERC1967NonPayable();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* 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[EIP 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);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)
pragma solidity ^0.8.20;
/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/
interface IBeacon {
/**
* @dev Must return an address that can be used as a delegate call target.
*
* {UpgradeableBeacon} will check that this address is a contract.
*/
function implementation() external view returns (address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.20;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```solidity
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(newImplementation.code.length > 0);
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
/**
* @dev Returns an `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
}{
"remappings": [
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"ds-test/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
"murky/=lib/murky/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "none",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": true
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"AlreadyDistributed","type":"error"},{"inputs":[],"name":"BalanceUnderflow","type":"error"},{"inputs":[{"internalType":"uint256","name":"size","type":"uint256"},{"internalType":"uint256","name":"max","type":"uint256"}],"name":"BatchTooLarge","type":"error"},{"inputs":[{"internalType":"uint256","name":"requested","type":"uint256"},{"internalType":"uint256","name":"headroom","type":"uint256"}],"name":"ClaimExceedsHeadroom","type":"error"},{"inputs":[{"internalType":"uint256","name":"claimable","type":"uint256"},{"internalType":"uint256","name":"minRequired","type":"uint256"}],"name":"ClaimTooSmall","type":"error"},{"inputs":[{"internalType":"uint256","name":"capPrice","type":"uint256"}],"name":"DistributionBlockedDuringDepeg","type":"error"},{"inputs":[],"name":"DistributionTooEarly","type":"error"},{"inputs":[{"internalType":"address","name":"implementation","type":"address"}],"name":"ERC1967InvalidImplementation","type":"error"},{"inputs":[],"name":"ERC1967NonPayable","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"EpochNotConfigured","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InvalidAmount","type":"error"},{"inputs":[],"name":"InvalidConfig","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"InvalidMaxTokensPerEpoch","type":"error"},{"inputs":[],"name":"InvalidOraclePrice","type":"error"},{"inputs":[],"name":"InvalidRecipient","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"InvalidRecoverySink","type":"error"},{"inputs":[],"name":"MathOverflowedMulDiv","type":"error"},{"inputs":[{"internalType":"uint256","name":"requested","type":"uint256"},{"internalType":"uint256","name":"maxAllowed","type":"uint256"}],"name":"MaxClaimPerTxExceeded","type":"error"},{"inputs":[{"internalType":"uint256","name":"requested","type":"uint256"},{"internalType":"uint256","name":"maxAllowed","type":"uint256"}],"name":"MaxTokensPerEpochExceeded","type":"error"},{"inputs":[],"name":"MustDistributeBeforeNewEpoch","type":"error"},{"inputs":[],"name":"NoRewardsDeclared","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"NotToken","type":"error"},{"inputs":[],"name":"NothingToClaim","type":"error"},{"inputs":[],"name":"NothingToDistribute","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"StaleAttestationForClaim","type":"error"},{"inputs":[],"name":"UUPSUnauthorizedCallContext","type":"error"},{"inputs":[{"internalType":"bytes32","name":"slot","type":"bytes32"}],"name":"UUPSUnsupportedProxiableUUID","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"UnsupportedRecoveryAsset","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"bool","name":"isExcluded","type":"bool"}],"name":"AccountExcluded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"claimAdmin","type":"address"},{"indexed":false,"internalType":"uint256","name":"usersProcessed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalClaimed","type":"uint256"}],"name":"BatchClaimExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldSink","type":"address"},{"indexed":true,"internalType":"address","name":"newSink","type":"address"}],"name":"BreakageSinkUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"CROnClaimEnforcementUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"claimAdmin","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ClaimForExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"blocked","type":"bool"}],"name":"DepegGuardUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"epochId","type":"uint64"},{"indexed":false,"internalType":"uint256","name":"tokensAllocated","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"denominatorUnits","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"globalEligibleUnits","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"treasuryBreakage","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"futureBreakage","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"deltaIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"dustCarry","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"grossAPYBps","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netAPYBps","type":"uint256"}],"name":"DistributionDeclared","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"epochId","type":"uint64"},{"indexed":false,"internalType":"uint256","name":"couponUsdc","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"capPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokensFromCoupon","type":"uint256"}],"name":"DistributionPriced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"epochId","type":"uint64"},{"indexed":false,"internalType":"uint256","name":"skimUsdc","type":"uint256"},{"indexed":false,"internalType":"uint16","name":"skimBps","type":"uint16"},{"indexed":true,"internalType":"address","name":"treasury","type":"address"}],"name":"DistributionSkimCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"epochId","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"epochStart","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"epochEnd","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"checkpointStart","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"checkpointEnd","type":"uint64"}],"name":"EpochConfigured","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountSold","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"futureUnits","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"remainingSeconds","type":"uint256"},{"indexed":true,"internalType":"uint64","name":"epochId","type":"uint64"}],"name":"FutureBreakage","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"maxTokens","type":"uint256"}],"name":"MaxClaimPerTxUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"maxTokens","type":"uint256"}],"name":"MaxTokensPerEpochUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"minClaimTokens","type":"uint256"}],"name":"MinClaimUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"policyManager","type":"address"}],"name":"PolicyManagerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountSold","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"unitsForfeit","type":"uint256"},{"indexed":true,"internalType":"uint64","name":"epochId","type":"uint64"}],"name":"ProportionalBreakage","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sink","type":"address"},{"indexed":false,"internalType":"bool","name":"allowed","type":"bool"}],"name":"RecoverySinkSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint64","name":"fromEpoch","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"toEpoch","type":"uint64"}],"name":"RewardClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"}],"name":"TokenHookUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokensRecovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"treasury","type":"address"}],"name":"TreasuryUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CLAIM_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DISTRIBUTOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UPGRADE_INTERFACE_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accRewardPerUnit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"accruedUnitsThisEpoch","outputs":[{"internalType":"uint256","name":"units","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"users","type":"address[]"}],"name":"batchClaimFor","outputs":[{"internalType":"uint256","name":"totalClaimed","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"blockDistributeOnDepeg","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"breakageSink","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"checkpointEnd","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"checkpointStart","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"}],"name":"claim","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"recipient","type":"address"}],"name":"claimFor","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"epochId","type":"uint64"},{"internalType":"uint64","name":"epochStart_","type":"uint64"},{"internalType":"uint64","name":"epochEnd_","type":"uint64"},{"internalType":"uint64","name":"checkpointStart_","type":"uint64"},{"internalType":"uint64","name":"checkpointEnd_","type":"uint64"}],"name":"configureEpoch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"currentEligibleSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentEpochId","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentEpochTokensMinted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"couponUsdcAmount","type":"uint256"}],"name":"distribute","outputs":[{"internalType":"uint256","name":"allocated","type":"uint256"},{"internalType":"uint256","name":"newDust","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"distributedThisEpoch","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dust","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"enforceCROnClaim","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"epochEnd","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"","type":"uint64"}],"name":"epochEndTime","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"","type":"uint64"}],"name":"epochReport","outputs":[{"internalType":"uint64","name":"distributionTime","type":"uint64"},{"internalType":"uint256","name":"denominatorUnits","type":"uint256"},{"internalType":"uint256","name":"deltaIndex","type":"uint256"},{"internalType":"uint256","name":"tokensAllocated","type":"uint256"},{"internalType":"uint256","name":"dustCarry","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"epochStart","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"","type":"uint64"}],"name":"epochStartTime","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"futureBreakageUnits","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getAccountFullState","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint64","name":"lastClaimedEpoch","type":"uint64"},{"internalType":"uint64","name":"lastAccrualTime","type":"uint64"},{"internalType":"uint64","name":"lastInflow","type":"uint64"},{"internalType":"uint256","name":"unitsAccrued","type":"uint256"},{"internalType":"bool","name":"excluded","type":"bool"},{"internalType":"bool","name":"eligible","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"users","type":"address[]"}],"name":"getBatchPending","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint256","name":"total","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getCheckpointStatus","outputs":[{"internalType":"bool","name":"isEligible_","type":"bool"},{"internalType":"bool","name":"hasFailedThisEpoch","type":"bool"},{"internalType":"bool","name":"canEarnThisEpoch","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCheckpointWindow","outputs":[{"internalType":"uint64","name":"start","type":"uint64"},{"internalType":"uint64","name":"end","type":"uint64"},{"internalType":"uint64","name":"epochId","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getEligibilityStatus","outputs":[{"internalType":"bool","name":"isEligible","type":"bool"},{"internalType":"bool","name":"isExcluded","type":"bool"},{"internalType":"bool","name":"isLateEntry","type":"bool"},{"internalType":"uint64","name":"checkpointStart_","type":"uint64"},{"internalType":"uint64","name":"checkpointEnd_","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getEpochInfo","outputs":[{"internalType":"uint64","name":"epochId","type":"uint64"},{"internalType":"uint64","name":"start","type":"uint64"},{"internalType":"uint64","name":"end","type":"uint64"},{"internalType":"uint64","name":"checkpointStart_","type":"uint64"},{"internalType":"uint64","name":"checkpointEnd_","type":"uint64"},{"internalType":"bool","name":"distributed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"epochId","type":"uint64"}],"name":"getEpochReport","outputs":[{"internalType":"uint64","name":"distributionTime","type":"uint64"},{"internalType":"uint256","name":"denominatorUnits","type":"uint256"},{"internalType":"uint256","name":"deltaIndex","type":"uint256"},{"internalType":"uint256","name":"tokensAllocated","type":"uint256"},{"internalType":"uint256","name":"dustCarry","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGlobalState","outputs":[{"internalType":"uint256","name":"eligibleUnits","type":"uint256"},{"internalType":"uint256","name":"eligibleSupply","type":"uint256"},{"internalType":"uint256","name":"treasuryBreakage","type":"uint256"},{"internalType":"uint256","name":"futureBreakage","type":"uint256"},{"internalType":"uint256","name":"totalBreakage","type":"uint256"},{"internalType":"uint64","name":"lastUpdateTime","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getPendingForUser","outputs":[{"internalType":"uint256","name":"pending","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"globalEligibleUnits","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"admin","type":"address"},{"internalType":"address","name":"distributor","type":"address"},{"internalType":"uint256","name":"minClaimTokens_","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isRecoverySink","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastDistributedEpochId","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastDistributionCAPPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastGlobalUpdateTime","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastMintEpochId","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidityReserve","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxClaimTokensPerTx","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxTokensToMintPerEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minClaimTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"onBalanceChange","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pauseDistribute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"pendingRewards","outputs":[{"internalType":"uint256","name":"reward","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"policyManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token_","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"recoverERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reserveUSDC","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bool","name":"isExcluded","type":"bool"}],"name":"setAccountExcluded","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"blocked","type":"bool"}],"name":"setBlockDistributeOnDepeg","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sink","type":"address"}],"name":"setBreakageSink","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"enabled","type":"bool"}],"name":"setEnforceCROnClaim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxTokens","type":"uint256"}],"name":"setMaxClaimTokensPerTx","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxTokens","type":"uint256"}],"name":"setMaxTokensToMintPerEpoch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"minClaimTokens_","type":"uint256"}],"name":"setMinClaimTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"policyManager_","type":"address"}],"name":"setPolicyManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sink","type":"address"},{"internalType":"bool","name":"allowed","type":"bool"}],"name":"setRecoverySink","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"liquidityReserve_","type":"address"},{"internalType":"address","name":"reserveUSDC_","type":"address"}],"name":"setReserveAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token_","type":"address"}],"name":"setToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"treasury_","type":"address"}],"name":"setTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBreakageAllTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalExcludedSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalRewardsClaimed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalRewardsDeclared","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"treasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"treasuryUnitsThisEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpauseDistribute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"}]Contract Creation Code
60a080604052346100e857306080527ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005460ff8160401c166100d9576002600160401b03196001600160401b03821601610073575b60405161567190816100ed823960805181818161251901526126050152f35b6001600160401b0319166001600160401b039081177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005581527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a15f80610054565b63f92ee8a960e01b5f5260045ffd5b5f80fdfe6080806040526004361015610012575f80fd5b5f905f3560e01c90816301ffc9a7146133da5750806303107da71461328d57806306f3ec1b146131ee57806308625ddd146131b45780631171bda914613083578063144fa6d71461301b57806315e5a1e514612ff25780631794bb3c14612e245780631e83409a14612a6157806320f21f99146129f9578063248a9ca3146129db5780632f2ff15d146129aa57806331d7a2621461298757806332a542431461296557806336568abe146129215780633921cf63146128a35780633a34d1da14612886578063437427e71461285b578063459002c51461283b578063485bf4d2146128155780634b049116146127f85780634bb6b58f146127cf5780634cca7b82146127955780634f1ef2861461258a5780634fcef9a21461256d57806352d1902d146125075780635369680a146124965780635c975abb146124685780635e7fe9b51461244257806361ce7fb81461241d57806361d027b3146123f55780636c90a590146123cb5780636df15895146123ae5780636eb3eea4146123915780637021669514612350578063715273c81461224f578063743faee2146121ff57806375ab857a1461217557806375b238fc146114d25780637ce64254146120ef578063803b31a9146120c95780638b08ec311461202d57806391c05b0b1461159357806391d148541461153d57806396582cf6146114d7578063a217fddf146114d2578063a34b0f76146114b4578063a37a4d0214611474578063a9fd1a8f14611402578063ab3dbf3b146113d9578063ab515792146113b3578063ab6f92021461137d578063ac9650d8146111d6578063ad3cb1cc1461118d578063adde41e114611125578063b4ba9e1114610f4b578063b5a4eece14610f2d578063b611458114610f0f578063b666701f14610e84578063b739642114610e36578063b753bfe914610e0d578063c09725ac14610def578063c1c32bb214610db5578063c5625d3d14610d8e578063c7084e5d14610d40578063c87b502e14610d17578063cc3629e514610cf9578063cdd0877414610a29578063ce4a6a0a1461099e578063d547741f14610963578063d74ef69414610945578063dacd7e0c14610927578063e5e62958146108e8578063ea880d5014610857578063eacdc5ff14610830578063eba806b4146106d0578063ed2d659a146106b2578063eea670db14610694578063eec7d1361461051b578063eede1247146104b7578063f0bd87cc1461047c578063f0f4426014610403578063fad9aba3146103e55763fc0c546a146103ba575f80fd5b346103e257806003193601126103e2576001546040516001600160a01b039091168152602090f35b80fd5b50346103e257806003193601126103e2576020601954604051908152f35b50346103e25760203660031901126103e25761041d61345d565b610425614048565b6001600160a01b0316801561046d57600380546001600160a01b031916821790557f7dae230f18360d76a040c81f050aa14eb9d6dc7901b20fc5d855e2a20fe814d18280a280f35b63d92e233d60e01b8252600482fd5b50346103e257806003193601126103e25760206040517ffbd454f36a7e1a388bd6fc3ab10d434aa4578f811acbbcf33afb1c697486313c8152f35b50346103e25760203660031901126103e2576004356104d4614048565b801561050c576020817f2a913f34da2f6a7c566c487298da38690e23bd168d591c20732b110bf36f49d192601b55604051908152a180f35b6309be28fb60e31b8252600482fd5b50346103e25760203660031901126103e2576004356001600160401b0381116106905761054c90369060040161342d565b91908161055884613fae565b9361056660405195866134e1565b80855261057281613fae565b602086019390601f190136853784915b8083106105d2575050506040519260408401946040855251809552606084019290945b8086106105ba57505082935060208301520390f35b909260208060019286518152019401950194906105a5565b9091926105eb6105e68584869a999a613585565b6135a9565b6040516318ebd13160e11b81526001600160a01b039091166004820152602081602481305afa90811561068557889161064f575b5060019161064391610631878a613fc5565b5261063c8689613fc5565b51906135bd565b93019190959495610582565b90506020813d821161067d575b81610669602093836134e1565b810103126106795751600161061f565b5f80fd5b3d915061065c565b6040513d8a823e3d90fd5b5080fd5b50346103e257806003193601126103e2576020600654604051908152f35b50346103e257806003193601126103e2576020600a54604051908152f35b50346103e25760203660031901126103e2576106ea61345d565b6106f2614048565b6001600160a01b0381169081156108215761070b614e57565b61071481614443565b8183528260205260408320600581019081549160ff831615610799575b5050601d805468010000000000000000600160e01b03198116604094851b68010000000000000000600160e01b03161790915590911c6001600160a01b031690507fb8c7fd2ff16e2c27ebd140c43d2d1c1fc350619ba97799502139cf27560818c38380a380f35b60ff6107e79360081c1680610817575b6107ef575b6107bb8254600f546135bd565b600f55600161ffff1982541617905584600282015584600482015560016107e0614d44565b9101613dbe565b5f8080610731565b600a5482541161080e576108068254600a546135db565b600a556107ae565b85600a556107ae565b50815415156107a9565b63d92e233d60e01b8352600483fd5b50346103e257806003193601126103e25760206001600160401b0360075416604051908152f35b50346103e25760203660031901126103e25760e0906040906001600160a01b0361087f61345d565b168152806020522060ff81549160018101549060056002820154910154916040519485526001600160401b03811660208601526001600160401b038160401c16604086015260c01c60608501526080840152818116151560a084015260081c16151560c0820152f35b50346103e25760203660031901126103e25760209060ff906040906001600160a01b0361091361345d565b168152601a84522054166040519015158152f35b50346103e257806003193601126103e2576020601154604051908152f35b50346103e257806003193601126103e2576020601e54604051908152f35b50346103e25760403660031901126103e25761099a600435610983613473565b906109956109908261361e565b614097565b614d94565b5080f35b50346103e25760403660031901126103e2576109b861345d565b6109c06134d2565b906109c9614048565b6001600160a01b03169081156108215760207fe30073cb0e1c2b3140284ef8a445d0535bbacc600eea5f320beccb2589fd9f8091838552601a8252610a1d81604087209060ff801983541691151516179055565b6040519015158152a280f35b50346103e25760a03660031901126103e257610a43613531565b6024356001600160401b038116808203610cf557604435916001600160401b03831692838103610cf1576064356001600160401b03811691828203610ced57608435936001600160401b038516809503610ce957610a9f614048565b85871115610cda57600754926001600160401b03808516991698808a1115610ccb5787861115610ccb5788871015610ccb5785871115610ccb57151580610cbe575b610caf5760809290921b67ffffffffffffffff60801b16604091821b67ffffffffffffffff60401b166001600160c01b038a16171760c09290921b6001600160c01b031916919091176007556008805467ffffffffffffffff19908116861790915587895260126020908152828a208054831688179055888a52601390529088208054821687179055600b805490911685179055506009869055600c869055600d8690556010805460ff191690556001546001600160a01b031680610be2575b5090608092917f4a8c9cb1eb0151c0a19fa64ab66db899bef79791050a4a14b6f077e4ffc289fa94604051938452602084015260408301526060820152a280f35b6020600491604051928380926318160ddd60e01b82525afa908115610ca4578791610c4a575b50907f4a8c9cb1eb0151c0a19fa64ab66db899bef79791050a4a14b6f077e4ffc289fa94610c3d6080959493600f54906135db565b600a559450909192610ba1565b93929190506020843d602011610c9c575b81610c68602093836134e1565b81010312610679579251919290917f4a8c9cb1eb0151c0a19fa64ab66db899bef79791050a4a14b6f077e4ffc289fa610c08565b3d9150610c5b565b6040513d89823e3d90fd5b63bbb9aea360e01b8a5260048afd5b5060ff6010541615610ae1565b6306b7c75960e31b8b5260048bfd5b6306b7c75960e31b8952600489fd5b8880fd5b8780fd5b8580fd5b8380fd5b50346103e257806003193601126103e2576020600d54604051908152f35b50346103e257806003193601126103e2576005546040516001600160a01b039091168152602090f35b50346103e25760203660031901126103e2577ff457596945dfcbc1c46c8c7d8beec7de1e2e735eef5bc822fddd54fe3818c95f6020600435610d80614048565b80601e55604051908152a180f35b50346103e257806003193601126103e25760206001600160401b0360155416604051908152f35b50346103e25760203660031901126103e2576001600160401b03604060209282610ddd613531565b16815260138452205416604051908152f35b50346103e257806003193601126103e2576020600c54604051908152f35b50346103e257806003193601126103e2576004546040516001600160a01b039091168152602090f35b50346103e25760203660031901126103e2577f400901beffaac0dcd17e2d6b95775f83c03ca51f4b29ca0aa45e9e760e57e2df6020600435610e76614048565b80600655604051908152a180f35b50346103e25760203660031901126103e2576040906001600160401b03610ea9613531565b1681526014602052206001600160401b03815416610f0b600183015492600281015490600460038201549101549160405195869586919260809396959491966001600160401b0360a08501981684526020840152604083015260608201520152565b0390f35b50346103e257806003193601126103e2576020600954604051908152f35b50346103e257806003193601126103e2576020601c54604051908152f35b50346103e25760403660031901126103e257610f6561345d565b610f6d613473565b610f75613fd9565b6001600160a01b0382168015611116576001600160a01b0382169182156111075781855284602052610faa6040862094614443565b6001600160401b03601554169081156110f8576003850190815495600281018054801515806110ed575b6110b0575b5087156110a1579261104b885f805160206155c5833981519152948b6004868260209f9a8d9b8f9d9a600101996110196001600160401b038c5416613600565b9a6001600160401b038d166001600160401b031982541617905555550155611043826018546135bd565b601855614cdc565b604080518981526001600160401b0392831660208201529290911690820152606090a36040518381527fd9933da664e75d182b59723a8724e7cfe7041e1b543741ef3da7c17750c77ff3853392a4604051908152f35b6312d37ee560e31b8952600489fd5b6011546110bc91614a2b565b6004830154908181116110d0575b50610fd9565b6110e59299916110df916135db565b906135bd565b965f806110ca565b506011541515610fd4565b630788d25d60e41b8652600486fd5b634e46966960e11b8552600485fd5b63d92e233d60e01b8452600484fd5b50346103e25760203660031901126103e25761113f61345d565b611147614048565b600280546001600160a01b0319166001600160a01b039290921691821790557f5106d9fe21e5ccaebfff5e5b6191d80ca628c317ef02f88ec6abbc39e505f6c38280a280f35b50346103e257806003193601126103e25750610f0b6040516111b06040826134e1565b60058152640352e302e360dc1b6020820152604051918291602083526020830190613561565b50346103e25760203660031901126103e2576004356001600160401b0381116106905761120790369060040161342d565b90602060405161121782826134e1565b84815281810191601f19810136843761122f85613fae565b9361123d60405195866134e1565b858552601f1961124c87613fae565b01875b81811061136e57505036819003601e190190875b8781101561130f578060051b8201358381121561130b578201908135916001600160401b0383116113075785018a83360382136103e257806112eb92896112d76001978b8e6040519483869484860198893784019083820190898252519283915e010185815203601f1981018352826134e1565b5190305af46112e46154a1565b9030615566565b6112f5828a613fc5565b526113008189613fc5565b5001611263565b8a80fd5b8980fd5b83898860405191838301848452825180915260408401948060408360051b870101940192955b8287106113425785850386f35b90919293828061135e600193603f198a82030186528851613561565b9601920196019592919092611335565b6060878201850152830161124f565b50346103e25760403660031901126103e2576113b061139a61345d565b6113a26134d2565b906113ab614048565b613de6565b80f35b50346103e257806003193601126103e257602060ff601d5460e01c166040519015158152f35b50346103e257806003193601126103e2576002546040516001600160a01b039091168152602090f35b50346103e257806003193601126103e25760c06007546001600160401b036008541660ff6010541690604051926001600160401b03811684526001600160401b038160401c1660208501526001600160401b038160801c166040850152841c60608401526080830152151560a0820152f35b50346103e25761148336613489565b6001549293929091906001600160a01b031633036114a5576113b09293613d46565b63087cada760e21b8352600483fd5b50346103e257806003193601126103e2576020601854604051908152f35b613547565b50346103e25760203660031901126103e2577ff2e7eb1104617f2b94095bc9a43d8042f56d2ccc6e5cc16773ab56873180aa4160206115146134c3565b61151c614048565b151560105461ff008260081b169061ff00191617601055604051908152a180f35b50346103e25760403660031901126103e2576040611559613473565b9160043581525f80516020615605833981519152602052209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b503461067957602036600319011261067957335f9081527fb2ce6f3a0968c56a08b54f4f0d1d0af4871f2c3340441c6ce862aaa53c9d37c76020526040902054600435919060ff1615611ff6576115e8614e30565b6002546001600160a01b031680611ef7575b5060ff60105416611ee8576001600160401b0360075460801c168015611ed9574210611eca576004546001600160a01b031680158015611eb7575b611ddf576005546040516323b872dd60e01b602082015233602482015260448101929092526064808301859052825261168291906001600160a01b031661167d6084836134e1565b6154d0565b6002545f91839183906001600160a01b031680611dee575b506116a3614e57565b6002546001600160a01b0316938415611ddf5780611d0b575b505060206004936040519485809263505be4d760e01b82525afa928315611cfe578193611cca575b508215611cbc577b0119799812dea11197f27f0f6e885c8ba7eb31f476caf7411a8633878411611cad5764e8d4a5100084029380850464e8d4a510001481151715611c99576117338486614b2e565b6117ad600754916001600160401b038316807fe53366cfaaebe8835eb6e345d074f4d9c6b52f27b9140c05940437d1b010a00a60405180611787868d8d846040919493926060820195825260208201520152565b0390a287601655601d54816001600160401b03821603611c80575b5050601954906135bd565b94601b5480611c47575b508515611c3957600954926117cf600c5480956135bd565b956117dd600d5480986135bd565b98600487948896602060018060a01b0360015416604051948580926318160ddd60e01b82525afa928315611c2e578a93611bfa575b5082151580611bf2575b80611be9575b611ab7575b50508a159350611aad925050505761183f8787614b2e565b9261185461184d858a614a2b565b80986135db565b955b846119eb575b505061186a836011546135bd565b601155611875614d44565b6040519060a082018281106001600160401b038211176119d75792604099926001600160401b037ff41e38855c5f4a0f0fe3492868a26411e18da9755e442a8ecdf2f46815b7c4989693610120968d521681526004898b898b8f6001600160401b03809160208901938c8552818a0195865260608a0196875260808a0197885282600754168152601460205220975116166001600160401b0319875416178655516001860155516002850155516003840155519101558760195561193b896017546135bd565b601755600160ff196010541617601055601b546119c3575b6001600160401b036007541695866001600160401b03196015541617601555600954600c54600d54918d51968d885260208801528d8701526060860152608085015260a08401528760c084015260e0830152610100820152a28060095580600c55600d5582519182526020820152f35b6119cf89601c546135bd565b601c55611953565b634e487b7160e01b87526041600452602487fd5b6119f4916135bd565b83811561185c57611a0491614a2b565b601d5460401c6001600160a01b03168015611a9a57905b6001600160a01b0382169182151580611a91575b611a3c575b50508361185c565b81611a5684935f805160206155c583398151915293614cdc565b611a62816018546135bd565b601855600754604080519283526001600160401b039091166020830181905290820152606090a35f8080611a34565b50811515611a2f565b506003546001600160a01b031690611a1b565b8392849695611856565b6001600160401b03808260801c169160401c16908181115f14611bd6576001600160401b0391611ae69161363c565b1690620151808210611bcb575b611afd9192614a2b565b80611b09575b80611827565b9091939550611b1992945061365c565b9164e8d4a5100081029080820464e8d4a510001481151715611bb7576801b5a660ea44b8000002908082046301e133801490151715611ba357611b66836801b5a660ea44b8000092614bd9565b9302908082046301e133801490151715611b8f5790611b8491614bd9565b905f80808080611b03565b634e487b7160e01b85526011600452602485fd5b634e487b7160e01b86526011600452602486fd5b634e487b7160e01b87526011600452602487fd5b62278d009150611af3565b50506001600160401b0362278d00611ae6565b50841515611822565b50600161181c565b9092506020813d602011611c26575b81611c16602093836134e1565b810103126106795751915f611812565b3d9150611c09565b6040513d8c823e3d90fd5b62598fc960e21b8452600484fd5b601c5481611c5589836135bd565b11611c6057506117b7565b604491611c6e8988936135bd565b631b91490960e01b8352600452602452fd5b86601c556001600160401b03191617601d555f806117a2565b634e487b7160e01b82526011600452602482fd5b63162908e360e11b8152600490fd5b62fc7cad60e51b8152600490fd5b9092506020813d602011611cf6575b81611ce6602093836134e1565b810103126106795751915f6116e4565b3d9150611cd9565b50604051903d90823e3d90fd5b6004546003546001600160a01b03908116969294939116803b156106795760405163bf9f3d3560e01b81526001600160a01b03979097166004880152602487018590525f908790604490829084905af1958615611dd457600496611dbf575b50602092937fef73c2c5904c3b84090d0696e781e58451ee6e5fc77e924d9c300ee94070b67b60406001600160401b03600754169261ffff60018060a01b03600354169583519283521687820152a3936116bc565b602093505f611dcd916134e1565b5f92611d6a565b6040513d5f823e3d90fd5b6306b7c75960e31b5f5260045ffd5b604051632806498b60e01b81529150602090829060049082905afa908115611dd4575f91611e7a575b5061ffff811680151580611e66575b80611e52575b1561169a57909450611e4091935082614abd565b91611e4b83836135db565b935f61169a565b506004546001600160a01b03161515611e2c565b506003546001600160a01b03161515611e26565b90506020813d602011611eaf575b81611e95602093836134e1565b81010312610679575161ffff81168103610679575f611e17565b3d9150611e88565b506005546001600160a01b031615611635565b6333ffc51360e11b5f5260045ffd5b639643df7960e01b5f5260045ffd5b63cce553a960e01b5f5260045ffd5b60205f9160046040518094819363060cf69f60e51b83525af18015611dd457611fbe575b5060ff60105460081c16611f30575b5f6115fa565b60025460405163505be4d760e01b815290602090829060049082906001600160a01b03165afa908115611dd4575f91611f8c575b50670de0b6b3a76400008110611f7a5750611f2a565b63b0df829d60e01b5f5260045260245ffd5b90506020813d602011611fb6575b81611fa7602093836134e1565b8101031261067957515f611f64565b3d9150611f9a565b6020813d602011611fee575b81611fd7602093836134e1565b81010312610679575160ff81168114611f1b575f80fd5b3d9150611fca565b63e2517d3f60e01b5f52336004527ffbd454f36a7e1a388bd6fc3ab10d434aa4578f811acbbcf33afb1c697486313c60245260445ffd5b346106795760403660031901126106795761204661345d565b61204e613473565b90612057614048565b6001600160a01b0316801580156120b8575b6120a9576bffffffffffffffffffffffff60a01b600454161760045560018060a01b03166bffffffffffffffffffffffff60a01b60055416176005555f80f35b63d92e233d60e01b5f5260045ffd5b506001600160a01b03821615612069565b34610679575f3660031901126106795760206001600160401b03600b5416604051908152f35b34610679576020366003190112610679576001600160401b03612110613531565b165f52601460205260405f206001600160401b03815416610f0b600183015492600281015490600460038201549101549160405195869586919260809396959491966001600160401b0360a08501981684526020840152604083015260608201520152565b346106795760203660031901126106795761218e61345d565b6040516318ebd13160e11b81526001600160a01b039091166004820152602081602481305afa8015611dd4575f906121cc575b602090604051908152f35b506020813d6020116121f7575b816121e6602093836134e1565b8101031261067957602090516121c1565b3d91506121d9565b34610679575f3660031901126106795760c0600954600a54600c54600d54600e54916001600160401b03600b541693604051958652602086015260408501526060840152608083015260a0820152f35b34610679576020366003190112610679576001600160a01b0361227061345d565b165f525f602052606060405f20612285614d44565b600754906001600160401b038260801c169283155f1461233c5760055f915b015460ff8160081c1693849182612330575b5094849560c01c93841515948561231c575b5084612309575b5050159182612301575b50826122f9575b5060405192151583521515602083015215156040820152f35b9150846122e0565b9150856122d9565b6001600160401b031610925086806122cf565b6001600160401b03821610159450886122c8565b60ff16159450876122b6565b6005846001600160401b03841610916122a4565b34610679576020366003190112610679576001600160401b03612371613531565b165f52601260205260206001600160401b0360405f205416604051908152f35b34610679575f366003190112610679576020601654604051908152f35b34610679575f366003190112610679576020601b54604051908152f35b34610679575f36600319011261067957601d546040805191901c6001600160a01b03168152602090f35b34610679575f366003190112610679576003546040516001600160a01b039091168152602090f35b34610679575f36600319011261067957602060ff60105460081c166040519015158152f35b34610679575f3660031901126106795760206001600160401b0360085416604051908152f35b34610679575f36600319011261067957602060ff5f8051602061562583398151915254166040519015158152f35b34610679575f366003190112610679576124ae614048565b6124b6614e30565b600160ff195f805160206156258339815191525416175f80516020615625833981519152557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a1005b34610679575f366003190112610679577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316300361255e5760206040515f805160206155e58339815191528152f35b63703e46dd60e11b5f5260045ffd5b34610679575f366003190112610679576020600e54604051908152f35b60403660031901126106795761259e61345d565b602435906001600160401b0382116106795736602383011215610679578160040135906125ca82613516565b916125d860405193846134e1565b8083526020830193366024838301011161067957815f926024602093018737840101526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016308114908115612773575b5061255e5761263d614048565b6040516352d1902d60e01b81526001600160a01b0382169390602081600481885afa5f918161273f575b5061267f5784634c9c8ce360e01b5f5260045260245ffd5b805f805160206155e583398151915286920361272d5750823b1561271b575f805160206155e583398151915280546001600160a01b031916821790557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a2825115612702575f8091612700945190845af46126fa6154a1565b91615566565b005b5050503461270c57005b63b398979f60e01b5f5260045ffd5b634c9c8ce360e01b5f5260045260245ffd5b632a87526960e21b5f5260045260245ffd5b9091506020813d60201161276b575b8161275b602093836134e1565b8101031261067957519086612667565b3d915061274e565b5f805160206155e5833981519152546001600160a01b03161415905084612630565b34610679575f3660031901126106795760606007546001600160401b03806008541691604051928160c01c84526020840152166040820152f35b34610679575f3660031901126106795760206001600160401b0360075460801c16604051908152f35b34610679575f366003190112610679576020601754604051908152f35b34610679575f3660031901126106795760206001600160401b03601d5416604051908152f35b34610679575f36600319011261067957602060075460c01c604051908152f35b3461067957602036600319011261067957602061287e61287961345d565b613c30565b604051908152f35b34610679575f366003190112610679576020600f54604051908152f35b34610679575f366003190112610679576128bb614048565b5f805160206156258339815191525460ff8116156129125760ff19165f80516020615625833981519152557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a1005b638dfc202b60e01b5f5260045ffd5b346106795760403660031901126106795761293a613473565b336001600160a01b038216036129565761270090600435614d94565b63334bd91960e11b5f5260045ffd5b34610679575f36600319011261067957602060ff601054166040519015158152f35b3461067957602036600319011261067957602061287e6129a561345d565b61368c565b34610679576040366003190112610679576127006004356129c9613473565b906129d66109908261361e565b61439f565b3461067957602036600319011261067957602061287e60043561361e565b34610679576020366003190112610679577f109951cb6479ea20e2944b032a8b89fb8baa74ed4e3acb636fc2df505b0fd6726020612a356134c3565b612a3d614048565b1515601d5460ff60e01b8260e01b169060ff60e01b191617601d55604051908152a1005b3461067957602036600319011261067957612a7a61345d565b6001600160a01b0381168015612e1557335f525f60205260405f2090612a9f33614443565b6001600160401b0360155416928315612e065760038301928354936002820190815480151580612dfb575b612dc4575b50600654808710612dae5750601e5480151580612da5575b612d8f575060ff601d5460e01c16612b93575b95612b5d866001600160401b03955f60048782612b88988160209f612b3360015f805160206155c58339815191529e019e8f5416613600565b9d6001600160401b038c166001600160401b031982541617905555550155611043826018546135bd565b604080518781526001600160401b039586166020820152949091169084015233929081906060820190565b0390a3604051908152f35b6002546001600160a01b03168015611ddf57602060049160405192838092632773439960e21b82525afa908115611dd4575f91612d4d575b506001600160a01b03168015611ddf576040516336a90ce560e21b8152602081600481855afa908115611dd4575f91612d1e575b50612d0f5760206004916040519283809263cd377c5360e01b82525afa8015611dd4575f90612cdc575b6001546040516318160ddd60e01b81529250602090839060049082906001600160a01b03165afa918215611dd4575f92612ca6575b50612c699082614a2b565b9080821115612c9e57612c7b916135db565b808711612c885750612afa565b86631ada478f60e31b5f5260045260245260445ffd5b50505f612c7b565b9091506020813d602011612cd4575b81612cc2602093836134e1565b81010312610679575190612c69612c5e565b3d9150612cb5565b506020813d602011612d07575b81612cf6602093836134e1565b810103126106795760049051612c29565b3d9150612ce9565b63410a0e0960e01b5f5260045ffd5b612d40915060203d602011612d46575b612d3881836134e1565b8101906135e8565b89612bff565b503d612d2e565b90506020813d602011612d87575b81612d68602093836134e1565b8101031261067957516001600160a01b03811681036106795788612bcb565b3d9150612d5b565b866348e8d0a560e11b5f5260045260245260445ffd5b50808711612ae7565b86632f74c3e960e01b5f5260045260245260445ffd5b601154612dd091614a2b565b600484015490818111612de4575b50612acf565b612df39297916110df916135db565b948780612dde565b506011541515612aca565b630788d25d60e41b5f5260045ffd5b634e46966960e11b5f5260045ffd5b3461067957612e3236613489565b5f80516020615645833981519152549260ff8460401c1615936001600160401b03811680159081612fea575b6001149081612fe0575b159081612fd7575b50612fc85767ffffffffffffffff1981166001175f805160206156458339815191525584612f9c575b50612ea261553b565b612eaa61553b565b612eb261553b565b60ff195f8051602061562583398151915254165f8051602061562583398151915255612edc61553b565b612ee461553b565b6001600160a01b038116928315611ddf576001600160a01b03811615611ddf57612f10612f1692614224565b506142d3565b506006555f52601a60205260405f20600160ff1982541617905561010061ff00196010541617601055612f4557005b68ff0000000000000000195f8051602061564583398151915254165f80516020615645833981519152557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602060405160018152a1005b68ffffffffffffffffff191668010000000000000001175f805160206156458339815191525584612e99565b63f92ee8a960e01b5f5260045ffd5b90501586612e70565b303b159150612e68565b869150612e5e565b34610679575f3660031901126106795760206001600160401b0360075460401c16604051908152f35b346106795760203660031901126106795761303461345d565b61303c614048565b6001600160a01b031680156120a957600180546001600160a01b031916821790557f525d14bd98892d4337e21b6b2e41978a4af15c5c11a9d8fe41402fa715c8300e5f80a2005b346106795761309136613489565b909161309b614048565b6001600160a01b031690811580156131a3575b6120a9576001600160a01b0383165f818152601a602052604090205490939060ff1615613190578115613181576001546001600160a01b03168015159081613177575b506131645760405163a9059cbb60e01b60208201526001600160a01b0391909116602482015260448082018390528152613136906131306064826134e1565b836154d0565b6040519081527fa2231b10d9b4e4166c8a827c99f97691b05aa88fb04e009a4e499005b5c50fcc60203392a4005b82632888137560e01b5f5260045260245ffd5b90508314856130f1565b63162908e360e11b5f5260045ffd5b83632a05184b60e21b5f5260045260245ffd5b506001600160a01b038316156130ae565b34610679575f3660031901126106795760206040517fb552f1bea17e2734c4b1d253bbc784c04b883a78e93589442d4b5d6e6a2f73bd8152f35b34610679576020366003190112610679576001600160a01b0361320f61345d565b165f525f60205260a060405f20600581015460ff8160081c169182613281575b6001015460c01c60075460c01c8091101580613278575b60ff6001600160401b036008541693604051951515865216151560208501521515604084015260608301526080820152f35b50801515613246565b60ff821615925061322f565b34610679576020366003190112610679576004356001600160401b038111610679576132bd90369060040161342d565b906132c6613fd9565b5f9060c883116133c2575f905f5b848110613318576020848460405190815281838201527fb9c05773bac087c79ca346191a3f49b33e00ab1c6913f7788c9a3ec9391beab260403392a2604051908152f35b6133266105e6828785613585565b6001600160a01b0381169081156133b85780613341916140dd565b80613353575b50506001905b016132d4565b9391909484613361916135bd565b945f1983146133a457806001809401956040519081527fd9933da664e75d182b59723a8724e7cfe7041e1b543741ef3da7c17750c77ff360203392a49086613347565b634e487b7160e01b5f52601160045260245ffd5b505060019061334d565b8263bb1cb70b60e01b5f5260045260c860245260445ffd5b34610679576020366003190112610679576004359063ffffffff60e01b821680920361067957602091637965db0b60e01b811490811561341c575b5015158152f35b6301ffc9a760e01b14905083613415565b9181601f84011215610679578235916001600160401b038311610679576020808501948460051b01011161067957565b600435906001600160a01b038216820361067957565b602435906001600160a01b038216820361067957565b6060906003190112610679576004356001600160a01b038116810361067957906024356001600160a01b0381168103610679579060443590565b60043590811515820361067957565b60243590811515820361067957565b90601f801991011681019081106001600160401b0382111761350257604052565b634e487b7160e01b5f52604160045260245ffd5b6001600160401b03811161350257601f01601f191660200190565b600435906001600160401b038216820361067957565b34610679575f3660031901126106795760206040515f8152f35b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b91908110156135955760051b0190565b634e487b7160e01b5f52603260045260245ffd5b356001600160a01b03811681036106795790565b919082018092116133a457565b9060ff801983541691151516179055565b919082039182116133a457565b90816020910312610679575180151581036106795790565b6001600160401b036001911601906001600160401b0382116133a457565b5f525f80516020615605833981519152602052600160405f20015490565b906001600160401b03809116911603906001600160401b0382116133a457565b818102929181159184041417156133a457565b6001600160401b03166001600160401b0381146133a45760010190565b60018060a01b03165f525f60205260405f2090600582015460ff8116613c2a576003830154926002810154906004810154926001820154916001600160401b038360801c16906001600160401b03829460401c169382151592838094613c16575b6139bf575b508154946001600160401b03600784015416926001600160401b0360075416809414806139b2575b61398b575b50613728614d44565b938360ff839760081c169382613981575b505080613979575b61394c575b5060ff6010541680613945575b8061393c575b80613921575b156138df57505f52601460205260405f206001600160401b0381541692831515806138cd575b1561387d576001600160401b0361379f6137a6928661363c565b168561365c565b9081151580613870575b613854575b5050816001600160401b03821611613812575b5050505b80151580613807575b6137de575b5050565b6011546137ea91614a2b565b8181116137f5575050565b6138049293916110df916135db565b90565b5060115415156137d5565b61383a61384b9495936001600160401b036138336110df956138419561363c565b169061365c565b80956135bd565b9360115490614a2b565b905f80806137c8565b61386892989160026110df92015490614a2b565b955f806137b5565b50600281015415156137b0565b8093506001600160401b039150166001600160401b038216116138a3575b5050506137cc565b61383a6138c49495936001600160401b036138336110df956138419561363c565b905f808061389b565b50836001600160401b03821610613785565b90506001600160401b0383166001600160401b038316119081613919575b5080613910575b6138a3575050506137cc565b50821515613904565b90505f6138fd565b506001600160401b0384166001600160401b0384161161375f565b50841515613759565b5080613753565b9050600190825f5260126020526001600160401b038060405f20541691168111156137465793505f613746565b506001613741565b109050835f613739565b60060154909590808211156139aa576139a3916135db565b945f61371f565b50505f6139a3565b506006810154151561371a565b8091929597505f9996999893949852601460205260405f20988554998a6001600160401b0360078901541699848b1480613c09575b613be3575b60ff8c60081c1680613bda575b80613bc7575b613b88575b5050600201549081151580613b7f575b613b65575b5050613a3190613600565b6001600160401b0360075416905b6001600160401b03811682811015613b3b57805f52601460205260405f208b90828b1480613b2e575b613b07575b60028101549283151580613afe575b80613aeb575b613a99575b50505050613a949061366f565b613a3f565b5f5260126020526001600160401b038060405f20541691541681811115613a875792613adc613a94959b936001600160401b036138336110df95613ae19861363c565b614a2b565b96905f8080613a87565b506001600160401b038254161515613a82565b50821515613a7c565b90506006880154808d115f14613b2757613b21908d6135db565b90613a6d565b505f613b21565b5060068901541515613a68565b505093949750945094905f935f95845f5260126020526001600160401b0360405f205416946136f2565b966110df613b7792613a319499614a2b565b95905f613a26565b50801515613a21565b6001600160401b03835416808210613ba1575b50613a11565b613bbe926001600160401b0361383360029697946110df9461363c565b91905f80613b9b565b506001600160401b038354161515613a0c565b50811515613a06565b9060068901548082115f14613c0157613bfb916135db565b906139f9565b50505f613bfb565b50600689015415156139f4565b506001600160401b036007541681106136ed565b505f9150565b60018060a01b03165f525f60205260405f2090600582015460ff8116908115613d19575b50613d14576002820154918054906001600160401b036007820154166001600160401b03600754161480613d07575b613ce1575b6001600160401b036001613c9a614d44565b92015460401c1690816001600160401b0382161180613cd8575b613cbd57505050565b916001600160401b036138336110df9361380496979561363c565b50821515613cb4565b9060068201548082115f14613cff57613cf9916135db565b90613c88565b50505f613cf9565b5060068101541515613c83565b5f9150565b60ff915060081c16155f613c54565b8115613d32570490565b634e487b7160e01b5f52601260045260245ffd5b9091906001600160a01b0380841691908116808314613db75780849115159081613dac575b50613d9c575b50508015159081613d91575b50613d86575050565b613d8f91615306565b565b90503014155f613d7d565b613da591614ef2565b5f82613d71565b90503014155f613d6b565b5050505050565b9067ffffffffffffffff60401b82549160401b169067ffffffffffffffff60401b1916179055565b60018060a01b03811691825f525f60205260405f206005810160ff815416918315158093151514613fa657613e98602094613ea293613e4d7ff3407d03b594227ddbb9468b57b4ac6fa9c361d9209ed837b15922d1364e56f298613e48614e57565b614443565b8115613ee157835480151580613ed3575b613eab575b50613e718454600f546135bd565b600f555f600285018190556004850155805491151560ff1661ffff19909216919091179055565b60016107e0614d44565b604051908152a2565b600a5410613eca57613ec08454600a546135db565b600a555b5f613e63565b5f600a55613ec4565b5060ff825460081c16613e5e565b600f54845411613f9d57613ef88454600f546135db565b600f555b613f04614d44565b6007548060c01c908115159182613f89575b5081613f6a575b5015613f3957815461ff00191682556135ca9060018601613dbe565b50835480613f48575b506135ca565b613f5490600a546135bd565b600a55805461ff0019166101001781555f613f42565b6001600160401b03915060801c166001600160401b038216105f613f1d565b6001600160401b038416101591505f613f16565b5f600f55613efc565b505050505050565b6001600160401b0381116135025760051b60200190565b80518210156135955760209160051b010190565b335f9081527f55d7f33d0a296b8db4e13bb2940becc7df2ac8a524f74cc457e170afe809c961602052604090205460ff161561401157565b63e2517d3f60e01b5f52336004527fb552f1bea17e2734c4b1d253bbc784c04b883a78e93589442d4b5d6e6a2f73bd60245260445ffd5b335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff161561408057565b63e2517d3f60e01b5f52336004525f60245260445ffd5b5f8181525f805160206156058339815191526020908152604080832033845290915290205460ff16156140c75750565b63e2517d3f60e01b5f523360045260245260445ffd5b6001600160a01b0381165f81815260208190526040902093929161410090614443565b6001600160401b0360155416801561421c57600385019283549560028101805480151580614211575b6141da575b5087156141cf576004825f80938160015f805160206155c5833981519152999897019a6141646001600160401b038d5416613600565b9b6001600160401b038a166001600160401b03198254161790555555015561418e876018546135bd565b60185561419b8782614cdc565b604080518881526001600160401b03968716602082015292909516948201949094526001600160a01b0390931692606090a3565b505f96505050505050565b6011546141e691614a2b565b6004830154908181116141fa575b5061412e565b6142099299916110df916135db565b965f806141f4565b506011541515614129565b505f93505050565b6001600160a01b0381165f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff166142ce576001600160a01b03165f8181527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d60205260408120805460ff191660011790553391907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8180a4600190565b505f90565b6001600160a01b0381165f9081527fb2ce6f3a0968c56a08b54f4f0d1d0af4871f2c3340441c6ce862aaa53c9d37c7602052604090205460ff166142ce576001600160a01b03165f8181527fb2ce6f3a0968c56a08b54f4f0d1d0af4871f2c3340441c6ce862aaa53c9d37c760205260408120805460ff191660011790553391907ffbd454f36a7e1a388bd6fc3ab10d434aa4578f811acbbcf33afb1c697486313c907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9080a4600190565b5f8181525f80516020615605833981519152602090815260408083206001600160a01b038616845290915290205460ff1661443d575f8181525f80516020615605833981519152602090815260408083206001600160a01b0395909516808452949091528120805460ff19166001179055339291907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9080a4600190565b50505f90565b6001600160a01b03165f908152602081905260409020614461614d44565b600582019160ff835416614a1e5760018101906001600160401b03825460801c161580614a0a575b61498d575b81546001600160401b038160801c1680151580614979575b6146b0575b50506001600160401b03825460401c166001600160401b03841681811115613fa6578254916001600160401b036007850154166001600160401b0360075416809114806146a3575b61467d575b60ff601054168061466f575b80614666575b6145a7575b50505093613d8f946001600160401b0361452f81865460401c168761363c565b16918215159081614598575b508061458f575b61454f575b505050613dbe565b61457b6145616145859360049361365c565b600285016145708282546135bd565b905560115490614a2b565b92019182546135bd565b90555f8080614547565b50801515614542565b60ff91505460081c165f61453b565b5f52601460205260405f20906001600160401b038254168015158061465d575b6145d1575061450f565b613d8f98506001600160401b0361379f8194936145ed9361363c565b80151580614650575b61462b575b50541680911161460d57505050613dbe565b61457b614561614585936001600160401b036138336004958a61363c565b600282015461463991614a2b565b614648600387019182546135bd565b90555f6145fb565b50600282015415156145f6565b508082106145c7565b5083151561450a565b5060ff885460081c16614504565b9260068501548082115f1461469b57614695916135db565b926144f8565b50505f614695565b50600685015415156144f3565b80614717915f9793949697526014602052600260405f2087546007890196846001600160401b03895416148061496c575b614946575b60ff875460081c168061493d575b8061492a575b6148d5575b50500154801515806148c8575b6148a3575b50613600565b6001600160401b03600754166001600160401b03821690811015614829579081614784925f52601460205260405f20908754816001600160401b03885416148061481c575b6147f9575b600283015491821515806147f0575b806147dd575b614789575b5050505061366f565b614717565b5f5260126020526001600160401b038060405f205416935416908382111561477b576001600160401b036138336147c395613adc9461363c565b6147d2600388019182546135bd565b90555f80808061477b565b506001600160401b038454161515614776565b50811515614770565b6006890154808211156148145761480f916135db565b614761565b50505f614761565b506006890154151561475c565b50505f600285018190556004850155600754835467ffffffffffffffff60801b191660809190911b67ffffffffffffffff60801b161783559392905060075460401c6001600160401b0316801561489957614885905b83613dbe565b835461ff0019166101001784555f806144ab565b506148858361487f565b6148b1906002880154614a2b565b6148c0600388019182546135bd565b90555f614711565b506002870154151561470c565b6001600160401b039060401c16906001600160401b03835416918281106148fd575b506146ff565b6138336001600160401b03916149129461363c565b614920838a019182546135bd565b90555f80806148f7565b506001600160401b0383541615156146fa565b508115156146f4565b9060068a01548082115f146149645761495e916135db565b906146e6565b50505f61495e565b5060068a015415156146e1565b506001600160401b036007541681106144a6565b600754825467ffffffffffffffff60801b191660809190911b67ffffffffffffffff60801b161782556001600160401b03825460401c16156149dc575b835461ff00191661010017845561448e565b60075460401c6001600160401b03168015614a00576149fb9083613dbe565b6149ca565b506149fb8361487f565b506001600160401b03600754161515614489565b613d8f9250600101613dbe565b9190915f838202915f1985820991838084109303928084039314614aaa5782670de0b6b3a76400001115614a9b57507faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac106699394670de0b6b3a7640000910990828211900360ee1b910360121c170290565b63227bc15360e01b8152600490fd5b505050670de0b6b3a76400009192500490565b9190915f838202915f1985820991838084109303928084039314614b2157826127101115614a9b57507fbc01a36e2eb1c432ca57a786c226809d495182a9930be0ded288ce703afb7e919394612710910990828211900360fc1b910360041c170290565b5050506127109192500490565b90670de0b6b3a76400008202905f19670de0b6b3a7640000840992828085109403938085039414614bcd5783821115614bbe57670de0b6b3a7640000829109815f0382168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b63227bc15360e01b5f5260045ffd5b50906138049250613d28565b906127108202905f19612710840992828085109403938085039414614bcd5783821115614bbe57612710829109815f0382168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b9091828202915f1984820993838086109503948086039514614ccf5784831115614bbe57829109815f0382168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b5050906138049250613d28565b81156137da576001546001600160a01b0316918215611ddf57823b15610679576040516340c10f1960e01b81526001600160a01b039290921660048301526024820152905f908290604490829084905af18015611dd457614d3a5750565b5f613d8f916134e1565b6001600160401b0342166007546001600160401b038160801c16908115614d8f5760401c6001600160401b0316808310614d895750808211614d84575090565b905090565b91505090565b505090565b5f8181525f80516020615605833981519152602090815260408083206001600160a01b038616845290915290205460ff161561443d575f8181525f80516020615605833981519152602090815260408083206001600160a01b0395909516808452949091528120805460ff19169055339291907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9080a4600190565b60ff5f805160206156258339815191525416614e4857565b63d93c066560e01b5f5260045ffd5b614e5f614d44565b600b546001600160401b0381166001600160401b03831692818411801590614eea575b614ee4576001600160401b0391614e989161363c565b1680151580614ed9575b614eb9575b5067ffffffffffffffff191617600b55565b614ec8614ed091600a5461365c565b6009546135bd565b6009555f614ea7565b50600a541515614ea2565b50505050565b508115614e82565b81156137da57614f00614e57565b614f0981614443565b60018060a01b0316805f525f60205260405f209182548181106152f75761501a925f9060078601916001600160401b038354166001600160401b036007541614806152ea575b6152be575b614f5e81866135db565b908115159081806152af575b8061529e575b615278575b600589019060ff825416615252575b614f8c614d44565b916001600160401b038897541693600754926001600160401b038416809614615222575b506001600160401b038416916001600160401b038460401c168310158061520e575b80615200575b806151f4575b806151eb575b806151e4575b156150cd575050505050506002870192835493615008828487614c57565b948561501e575b5050505050506135db565b9055565b8086116150c5575b917f8fbf3369190b6598d95844b965a46ddb5d60bab3af3e4854b6b699dc5d6d22799391615056876040956135db565b905561506486600c546135bd565b600c5561507386600e546135bd565b600e5561509860048b019161508b8354918583614c57565b908082116150bd576135db565b90556001600160401b03600754169482519182526020820152a35f808080808061500f565b9050806135db565b945084615026565b909192939497506001600160401b036008541683101592836151cd575b50826151be575b826151b1575b50816151a9575b5061510e575b50505050506135db565b6001600160401b0391826151259260801c1661363c565b1680615132575b80615104565b8161519d615161837f5898c7d7047da30469bd7b9309dd1dd2e19eb2cccef3100f3322fae8da09f94c9561365c565b9261516e84600d546135bd565b600d5561517d84600e546135bd565b600e55604051938493846040919493926060820195825260208201520152565b0390a35f80808061512c565b90505f6150fe565b5460ff161591505f6150f7565b805460081c60ff1692506150f1565b608085901c6001600160401b03161192505f6150ea565b5081614fea565b50881515614fe4565b5060ff81541615614fde565b5060ff815460081c16614fd8565b506001600160401b03600854168310614fd2565b61523291985060068d01546135bd565b8089111561524b5761524490896135db565b965f614fb0565b505f615244565b600f54881161526f5761526788600f546135db565b600f55614f84565b5f600f55614f84565b600a5483116152955761528d83600a546135db565b600a55614f75565b5f600a55614f75565b5060ff60058a015460081c16614f70565b5060ff60058a01541615614f6a565b5060068601805490818611156152e1576152da825b80936135db565b9055614f54565b6152da866152d3565b5060068701541515614f4f565b6305e72d3960e11b5f5260045ffd5b81156137da57615314614e57565b61531d81614443565b6001600160a01b03165f9081526020819052604090206001810180546001600160c01b03164260c01b6001600160c01b03191617905560ff90600590615361614d44565b600754908160c01c91821515928361548d575b5082615473575b50506153888583546135bd565b82551561543c57600781016001600160401b038154166001600160401b036007541603615413575b50600681016153c08582546135bd565b905581810180548481161580615406575b6153f4575b50505b0154166153e35750565b6153ef90600f546135bd565b600f55565b61ff0019166101001790555f806153d6565b50848160081c16156153d1565b5f60068301556001600160401b038060075416166001600160401b03198254161790555f6153b0565b818101805484811615615451575b50506153d9565b6101009061ff00191617905561546984600a546135bd565b600a555f8061544a565b60801c6001600160401b0390811691161090505f8061537b565b6001600160401b038316101592505f615374565b3d156154cb573d906154b282613516565b916154c060405193846134e1565b82523d5f602084013e565b606090565b5f806154f89260018060a01b03169360208151910182865af16154f16154a1565b9083615566565b8051908115159182615520575b505061550e5750565b635274afe760e01b5f5260045260245ffd5b61553392506020809183010191016135e8565b155f80615505565b60ff5f805160206156458339815191525460401c161561555757565b631afcd79f60e31b5f5260045ffd5b9061558a575080511561557b57805190602001fd5b630a12f52160e11b5f5260045ffd5b815115806155bb575b61559b575090565b639996b31560e01b5f9081526001600160a01b0391909116600452602490fd5b50803b1561559356fe441c9fb962eef2eb58232351b5f5c29aebe2daf5d0496fa1df82d30274eb7982360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a164736f6c634300081a000a
Deployed Bytecode
0x6080806040526004361015610012575f80fd5b5f905f3560e01c90816301ffc9a7146133da5750806303107da71461328d57806306f3ec1b146131ee57806308625ddd146131b45780631171bda914613083578063144fa6d71461301b57806315e5a1e514612ff25780631794bb3c14612e245780631e83409a14612a6157806320f21f99146129f9578063248a9ca3146129db5780632f2ff15d146129aa57806331d7a2621461298757806332a542431461296557806336568abe146129215780633921cf63146128a35780633a34d1da14612886578063437427e71461285b578063459002c51461283b578063485bf4d2146128155780634b049116146127f85780634bb6b58f146127cf5780634cca7b82146127955780634f1ef2861461258a5780634fcef9a21461256d57806352d1902d146125075780635369680a146124965780635c975abb146124685780635e7fe9b51461244257806361ce7fb81461241d57806361d027b3146123f55780636c90a590146123cb5780636df15895146123ae5780636eb3eea4146123915780637021669514612350578063715273c81461224f578063743faee2146121ff57806375ab857a1461217557806375b238fc146114d25780637ce64254146120ef578063803b31a9146120c95780638b08ec311461202d57806391c05b0b1461159357806391d148541461153d57806396582cf6146114d7578063a217fddf146114d2578063a34b0f76146114b4578063a37a4d0214611474578063a9fd1a8f14611402578063ab3dbf3b146113d9578063ab515792146113b3578063ab6f92021461137d578063ac9650d8146111d6578063ad3cb1cc1461118d578063adde41e114611125578063b4ba9e1114610f4b578063b5a4eece14610f2d578063b611458114610f0f578063b666701f14610e84578063b739642114610e36578063b753bfe914610e0d578063c09725ac14610def578063c1c32bb214610db5578063c5625d3d14610d8e578063c7084e5d14610d40578063c87b502e14610d17578063cc3629e514610cf9578063cdd0877414610a29578063ce4a6a0a1461099e578063d547741f14610963578063d74ef69414610945578063dacd7e0c14610927578063e5e62958146108e8578063ea880d5014610857578063eacdc5ff14610830578063eba806b4146106d0578063ed2d659a146106b2578063eea670db14610694578063eec7d1361461051b578063eede1247146104b7578063f0bd87cc1461047c578063f0f4426014610403578063fad9aba3146103e55763fc0c546a146103ba575f80fd5b346103e257806003193601126103e2576001546040516001600160a01b039091168152602090f35b80fd5b50346103e257806003193601126103e2576020601954604051908152f35b50346103e25760203660031901126103e25761041d61345d565b610425614048565b6001600160a01b0316801561046d57600380546001600160a01b031916821790557f7dae230f18360d76a040c81f050aa14eb9d6dc7901b20fc5d855e2a20fe814d18280a280f35b63d92e233d60e01b8252600482fd5b50346103e257806003193601126103e25760206040517ffbd454f36a7e1a388bd6fc3ab10d434aa4578f811acbbcf33afb1c697486313c8152f35b50346103e25760203660031901126103e2576004356104d4614048565b801561050c576020817f2a913f34da2f6a7c566c487298da38690e23bd168d591c20732b110bf36f49d192601b55604051908152a180f35b6309be28fb60e31b8252600482fd5b50346103e25760203660031901126103e2576004356001600160401b0381116106905761054c90369060040161342d565b91908161055884613fae565b9361056660405195866134e1565b80855261057281613fae565b602086019390601f190136853784915b8083106105d2575050506040519260408401946040855251809552606084019290945b8086106105ba57505082935060208301520390f35b909260208060019286518152019401950194906105a5565b9091926105eb6105e68584869a999a613585565b6135a9565b6040516318ebd13160e11b81526001600160a01b039091166004820152602081602481305afa90811561068557889161064f575b5060019161064391610631878a613fc5565b5261063c8689613fc5565b51906135bd565b93019190959495610582565b90506020813d821161067d575b81610669602093836134e1565b810103126106795751600161061f565b5f80fd5b3d915061065c565b6040513d8a823e3d90fd5b5080fd5b50346103e257806003193601126103e2576020600654604051908152f35b50346103e257806003193601126103e2576020600a54604051908152f35b50346103e25760203660031901126103e2576106ea61345d565b6106f2614048565b6001600160a01b0381169081156108215761070b614e57565b61071481614443565b8183528260205260408320600581019081549160ff831615610799575b5050601d805468010000000000000000600160e01b03198116604094851b68010000000000000000600160e01b03161790915590911c6001600160a01b031690507fb8c7fd2ff16e2c27ebd140c43d2d1c1fc350619ba97799502139cf27560818c38380a380f35b60ff6107e79360081c1680610817575b6107ef575b6107bb8254600f546135bd565b600f55600161ffff1982541617905584600282015584600482015560016107e0614d44565b9101613dbe565b5f8080610731565b600a5482541161080e576108068254600a546135db565b600a556107ae565b85600a556107ae565b50815415156107a9565b63d92e233d60e01b8352600483fd5b50346103e257806003193601126103e25760206001600160401b0360075416604051908152f35b50346103e25760203660031901126103e25760e0906040906001600160a01b0361087f61345d565b168152806020522060ff81549160018101549060056002820154910154916040519485526001600160401b03811660208601526001600160401b038160401c16604086015260c01c60608501526080840152818116151560a084015260081c16151560c0820152f35b50346103e25760203660031901126103e25760209060ff906040906001600160a01b0361091361345d565b168152601a84522054166040519015158152f35b50346103e257806003193601126103e2576020601154604051908152f35b50346103e257806003193601126103e2576020601e54604051908152f35b50346103e25760403660031901126103e25761099a600435610983613473565b906109956109908261361e565b614097565b614d94565b5080f35b50346103e25760403660031901126103e2576109b861345d565b6109c06134d2565b906109c9614048565b6001600160a01b03169081156108215760207fe30073cb0e1c2b3140284ef8a445d0535bbacc600eea5f320beccb2589fd9f8091838552601a8252610a1d81604087209060ff801983541691151516179055565b6040519015158152a280f35b50346103e25760a03660031901126103e257610a43613531565b6024356001600160401b038116808203610cf557604435916001600160401b03831692838103610cf1576064356001600160401b03811691828203610ced57608435936001600160401b038516809503610ce957610a9f614048565b85871115610cda57600754926001600160401b03808516991698808a1115610ccb5787861115610ccb5788871015610ccb5785871115610ccb57151580610cbe575b610caf5760809290921b67ffffffffffffffff60801b16604091821b67ffffffffffffffff60401b166001600160c01b038a16171760c09290921b6001600160c01b031916919091176007556008805467ffffffffffffffff19908116861790915587895260126020908152828a208054831688179055888a52601390529088208054821687179055600b805490911685179055506009869055600c869055600d8690556010805460ff191690556001546001600160a01b031680610be2575b5090608092917f4a8c9cb1eb0151c0a19fa64ab66db899bef79791050a4a14b6f077e4ffc289fa94604051938452602084015260408301526060820152a280f35b6020600491604051928380926318160ddd60e01b82525afa908115610ca4578791610c4a575b50907f4a8c9cb1eb0151c0a19fa64ab66db899bef79791050a4a14b6f077e4ffc289fa94610c3d6080959493600f54906135db565b600a559450909192610ba1565b93929190506020843d602011610c9c575b81610c68602093836134e1565b81010312610679579251919290917f4a8c9cb1eb0151c0a19fa64ab66db899bef79791050a4a14b6f077e4ffc289fa610c08565b3d9150610c5b565b6040513d89823e3d90fd5b63bbb9aea360e01b8a5260048afd5b5060ff6010541615610ae1565b6306b7c75960e31b8b5260048bfd5b6306b7c75960e31b8952600489fd5b8880fd5b8780fd5b8580fd5b8380fd5b50346103e257806003193601126103e2576020600d54604051908152f35b50346103e257806003193601126103e2576005546040516001600160a01b039091168152602090f35b50346103e25760203660031901126103e2577ff457596945dfcbc1c46c8c7d8beec7de1e2e735eef5bc822fddd54fe3818c95f6020600435610d80614048565b80601e55604051908152a180f35b50346103e257806003193601126103e25760206001600160401b0360155416604051908152f35b50346103e25760203660031901126103e2576001600160401b03604060209282610ddd613531565b16815260138452205416604051908152f35b50346103e257806003193601126103e2576020600c54604051908152f35b50346103e257806003193601126103e2576004546040516001600160a01b039091168152602090f35b50346103e25760203660031901126103e2577f400901beffaac0dcd17e2d6b95775f83c03ca51f4b29ca0aa45e9e760e57e2df6020600435610e76614048565b80600655604051908152a180f35b50346103e25760203660031901126103e2576040906001600160401b03610ea9613531565b1681526014602052206001600160401b03815416610f0b600183015492600281015490600460038201549101549160405195869586919260809396959491966001600160401b0360a08501981684526020840152604083015260608201520152565b0390f35b50346103e257806003193601126103e2576020600954604051908152f35b50346103e257806003193601126103e2576020601c54604051908152f35b50346103e25760403660031901126103e257610f6561345d565b610f6d613473565b610f75613fd9565b6001600160a01b0382168015611116576001600160a01b0382169182156111075781855284602052610faa6040862094614443565b6001600160401b03601554169081156110f8576003850190815495600281018054801515806110ed575b6110b0575b5087156110a1579261104b885f805160206155c5833981519152948b6004868260209f9a8d9b8f9d9a600101996110196001600160401b038c5416613600565b9a6001600160401b038d166001600160401b031982541617905555550155611043826018546135bd565b601855614cdc565b604080518981526001600160401b0392831660208201529290911690820152606090a36040518381527fd9933da664e75d182b59723a8724e7cfe7041e1b543741ef3da7c17750c77ff3853392a4604051908152f35b6312d37ee560e31b8952600489fd5b6011546110bc91614a2b565b6004830154908181116110d0575b50610fd9565b6110e59299916110df916135db565b906135bd565b965f806110ca565b506011541515610fd4565b630788d25d60e41b8652600486fd5b634e46966960e11b8552600485fd5b63d92e233d60e01b8452600484fd5b50346103e25760203660031901126103e25761113f61345d565b611147614048565b600280546001600160a01b0319166001600160a01b039290921691821790557f5106d9fe21e5ccaebfff5e5b6191d80ca628c317ef02f88ec6abbc39e505f6c38280a280f35b50346103e257806003193601126103e25750610f0b6040516111b06040826134e1565b60058152640352e302e360dc1b6020820152604051918291602083526020830190613561565b50346103e25760203660031901126103e2576004356001600160401b0381116106905761120790369060040161342d565b90602060405161121782826134e1565b84815281810191601f19810136843761122f85613fae565b9361123d60405195866134e1565b858552601f1961124c87613fae565b01875b81811061136e57505036819003601e190190875b8781101561130f578060051b8201358381121561130b578201908135916001600160401b0383116113075785018a83360382136103e257806112eb92896112d76001978b8e6040519483869484860198893784019083820190898252519283915e010185815203601f1981018352826134e1565b5190305af46112e46154a1565b9030615566565b6112f5828a613fc5565b526113008189613fc5565b5001611263565b8a80fd5b8980fd5b83898860405191838301848452825180915260408401948060408360051b870101940192955b8287106113425785850386f35b90919293828061135e600193603f198a82030186528851613561565b9601920196019592919092611335565b6060878201850152830161124f565b50346103e25760403660031901126103e2576113b061139a61345d565b6113a26134d2565b906113ab614048565b613de6565b80f35b50346103e257806003193601126103e257602060ff601d5460e01c166040519015158152f35b50346103e257806003193601126103e2576002546040516001600160a01b039091168152602090f35b50346103e257806003193601126103e25760c06007546001600160401b036008541660ff6010541690604051926001600160401b03811684526001600160401b038160401c1660208501526001600160401b038160801c166040850152841c60608401526080830152151560a0820152f35b50346103e25761148336613489565b6001549293929091906001600160a01b031633036114a5576113b09293613d46565b63087cada760e21b8352600483fd5b50346103e257806003193601126103e2576020601854604051908152f35b613547565b50346103e25760203660031901126103e2577ff2e7eb1104617f2b94095bc9a43d8042f56d2ccc6e5cc16773ab56873180aa4160206115146134c3565b61151c614048565b151560105461ff008260081b169061ff00191617601055604051908152a180f35b50346103e25760403660031901126103e2576040611559613473565b9160043581525f80516020615605833981519152602052209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b503461067957602036600319011261067957335f9081527fb2ce6f3a0968c56a08b54f4f0d1d0af4871f2c3340441c6ce862aaa53c9d37c76020526040902054600435919060ff1615611ff6576115e8614e30565b6002546001600160a01b031680611ef7575b5060ff60105416611ee8576001600160401b0360075460801c168015611ed9574210611eca576004546001600160a01b031680158015611eb7575b611ddf576005546040516323b872dd60e01b602082015233602482015260448101929092526064808301859052825261168291906001600160a01b031661167d6084836134e1565b6154d0565b6002545f91839183906001600160a01b031680611dee575b506116a3614e57565b6002546001600160a01b0316938415611ddf5780611d0b575b505060206004936040519485809263505be4d760e01b82525afa928315611cfe578193611cca575b508215611cbc577b0119799812dea11197f27f0f6e885c8ba7eb31f476caf7411a8633878411611cad5764e8d4a5100084029380850464e8d4a510001481151715611c99576117338486614b2e565b6117ad600754916001600160401b038316807fe53366cfaaebe8835eb6e345d074f4d9c6b52f27b9140c05940437d1b010a00a60405180611787868d8d846040919493926060820195825260208201520152565b0390a287601655601d54816001600160401b03821603611c80575b5050601954906135bd565b94601b5480611c47575b508515611c3957600954926117cf600c5480956135bd565b956117dd600d5480986135bd565b98600487948896602060018060a01b0360015416604051948580926318160ddd60e01b82525afa928315611c2e578a93611bfa575b5082151580611bf2575b80611be9575b611ab7575b50508a159350611aad925050505761183f8787614b2e565b9261185461184d858a614a2b565b80986135db565b955b846119eb575b505061186a836011546135bd565b601155611875614d44565b6040519060a082018281106001600160401b038211176119d75792604099926001600160401b037ff41e38855c5f4a0f0fe3492868a26411e18da9755e442a8ecdf2f46815b7c4989693610120968d521681526004898b898b8f6001600160401b03809160208901938c8552818a0195865260608a0196875260808a0197885282600754168152601460205220975116166001600160401b0319875416178655516001860155516002850155516003840155519101558760195561193b896017546135bd565b601755600160ff196010541617601055601b546119c3575b6001600160401b036007541695866001600160401b03196015541617601555600954600c54600d54918d51968d885260208801528d8701526060860152608085015260a08401528760c084015260e0830152610100820152a28060095580600c55600d5582519182526020820152f35b6119cf89601c546135bd565b601c55611953565b634e487b7160e01b87526041600452602487fd5b6119f4916135bd565b83811561185c57611a0491614a2b565b601d5460401c6001600160a01b03168015611a9a57905b6001600160a01b0382169182151580611a91575b611a3c575b50508361185c565b81611a5684935f805160206155c583398151915293614cdc565b611a62816018546135bd565b601855600754604080519283526001600160401b039091166020830181905290820152606090a35f8080611a34565b50811515611a2f565b506003546001600160a01b031690611a1b565b8392849695611856565b6001600160401b03808260801c169160401c16908181115f14611bd6576001600160401b0391611ae69161363c565b1690620151808210611bcb575b611afd9192614a2b565b80611b09575b80611827565b9091939550611b1992945061365c565b9164e8d4a5100081029080820464e8d4a510001481151715611bb7576801b5a660ea44b8000002908082046301e133801490151715611ba357611b66836801b5a660ea44b8000092614bd9565b9302908082046301e133801490151715611b8f5790611b8491614bd9565b905f80808080611b03565b634e487b7160e01b85526011600452602485fd5b634e487b7160e01b86526011600452602486fd5b634e487b7160e01b87526011600452602487fd5b62278d009150611af3565b50506001600160401b0362278d00611ae6565b50841515611822565b50600161181c565b9092506020813d602011611c26575b81611c16602093836134e1565b810103126106795751915f611812565b3d9150611c09565b6040513d8c823e3d90fd5b62598fc960e21b8452600484fd5b601c5481611c5589836135bd565b11611c6057506117b7565b604491611c6e8988936135bd565b631b91490960e01b8352600452602452fd5b86601c556001600160401b03191617601d555f806117a2565b634e487b7160e01b82526011600452602482fd5b63162908e360e11b8152600490fd5b62fc7cad60e51b8152600490fd5b9092506020813d602011611cf6575b81611ce6602093836134e1565b810103126106795751915f6116e4565b3d9150611cd9565b50604051903d90823e3d90fd5b6004546003546001600160a01b03908116969294939116803b156106795760405163bf9f3d3560e01b81526001600160a01b03979097166004880152602487018590525f908790604490829084905af1958615611dd457600496611dbf575b50602092937fef73c2c5904c3b84090d0696e781e58451ee6e5fc77e924d9c300ee94070b67b60406001600160401b03600754169261ffff60018060a01b03600354169583519283521687820152a3936116bc565b602093505f611dcd916134e1565b5f92611d6a565b6040513d5f823e3d90fd5b6306b7c75960e31b5f5260045ffd5b604051632806498b60e01b81529150602090829060049082905afa908115611dd4575f91611e7a575b5061ffff811680151580611e66575b80611e52575b1561169a57909450611e4091935082614abd565b91611e4b83836135db565b935f61169a565b506004546001600160a01b03161515611e2c565b506003546001600160a01b03161515611e26565b90506020813d602011611eaf575b81611e95602093836134e1565b81010312610679575161ffff81168103610679575f611e17565b3d9150611e88565b506005546001600160a01b031615611635565b6333ffc51360e11b5f5260045ffd5b639643df7960e01b5f5260045ffd5b63cce553a960e01b5f5260045ffd5b60205f9160046040518094819363060cf69f60e51b83525af18015611dd457611fbe575b5060ff60105460081c16611f30575b5f6115fa565b60025460405163505be4d760e01b815290602090829060049082906001600160a01b03165afa908115611dd4575f91611f8c575b50670de0b6b3a76400008110611f7a5750611f2a565b63b0df829d60e01b5f5260045260245ffd5b90506020813d602011611fb6575b81611fa7602093836134e1565b8101031261067957515f611f64565b3d9150611f9a565b6020813d602011611fee575b81611fd7602093836134e1565b81010312610679575160ff81168114611f1b575f80fd5b3d9150611fca565b63e2517d3f60e01b5f52336004527ffbd454f36a7e1a388bd6fc3ab10d434aa4578f811acbbcf33afb1c697486313c60245260445ffd5b346106795760403660031901126106795761204661345d565b61204e613473565b90612057614048565b6001600160a01b0316801580156120b8575b6120a9576bffffffffffffffffffffffff60a01b600454161760045560018060a01b03166bffffffffffffffffffffffff60a01b60055416176005555f80f35b63d92e233d60e01b5f5260045ffd5b506001600160a01b03821615612069565b34610679575f3660031901126106795760206001600160401b03600b5416604051908152f35b34610679576020366003190112610679576001600160401b03612110613531565b165f52601460205260405f206001600160401b03815416610f0b600183015492600281015490600460038201549101549160405195869586919260809396959491966001600160401b0360a08501981684526020840152604083015260608201520152565b346106795760203660031901126106795761218e61345d565b6040516318ebd13160e11b81526001600160a01b039091166004820152602081602481305afa8015611dd4575f906121cc575b602090604051908152f35b506020813d6020116121f7575b816121e6602093836134e1565b8101031261067957602090516121c1565b3d91506121d9565b34610679575f3660031901126106795760c0600954600a54600c54600d54600e54916001600160401b03600b541693604051958652602086015260408501526060840152608083015260a0820152f35b34610679576020366003190112610679576001600160a01b0361227061345d565b165f525f602052606060405f20612285614d44565b600754906001600160401b038260801c169283155f1461233c5760055f915b015460ff8160081c1693849182612330575b5094849560c01c93841515948561231c575b5084612309575b5050159182612301575b50826122f9575b5060405192151583521515602083015215156040820152f35b9150846122e0565b9150856122d9565b6001600160401b031610925086806122cf565b6001600160401b03821610159450886122c8565b60ff16159450876122b6565b6005846001600160401b03841610916122a4565b34610679576020366003190112610679576001600160401b03612371613531565b165f52601260205260206001600160401b0360405f205416604051908152f35b34610679575f366003190112610679576020601654604051908152f35b34610679575f366003190112610679576020601b54604051908152f35b34610679575f36600319011261067957601d546040805191901c6001600160a01b03168152602090f35b34610679575f366003190112610679576003546040516001600160a01b039091168152602090f35b34610679575f36600319011261067957602060ff60105460081c166040519015158152f35b34610679575f3660031901126106795760206001600160401b0360085416604051908152f35b34610679575f36600319011261067957602060ff5f8051602061562583398151915254166040519015158152f35b34610679575f366003190112610679576124ae614048565b6124b6614e30565b600160ff195f805160206156258339815191525416175f80516020615625833981519152557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a1005b34610679575f366003190112610679577f000000000000000000000000291cc0da17bc2ae2cf88c17b38a6b7fae8d49d2a6001600160a01b0316300361255e5760206040515f805160206155e58339815191528152f35b63703e46dd60e11b5f5260045ffd5b34610679575f366003190112610679576020600e54604051908152f35b60403660031901126106795761259e61345d565b602435906001600160401b0382116106795736602383011215610679578160040135906125ca82613516565b916125d860405193846134e1565b8083526020830193366024838301011161067957815f926024602093018737840101526001600160a01b037f000000000000000000000000291cc0da17bc2ae2cf88c17b38a6b7fae8d49d2a16308114908115612773575b5061255e5761263d614048565b6040516352d1902d60e01b81526001600160a01b0382169390602081600481885afa5f918161273f575b5061267f5784634c9c8ce360e01b5f5260045260245ffd5b805f805160206155e583398151915286920361272d5750823b1561271b575f805160206155e583398151915280546001600160a01b031916821790557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a2825115612702575f8091612700945190845af46126fa6154a1565b91615566565b005b5050503461270c57005b63b398979f60e01b5f5260045ffd5b634c9c8ce360e01b5f5260045260245ffd5b632a87526960e21b5f5260045260245ffd5b9091506020813d60201161276b575b8161275b602093836134e1565b8101031261067957519086612667565b3d915061274e565b5f805160206155e5833981519152546001600160a01b03161415905084612630565b34610679575f3660031901126106795760606007546001600160401b03806008541691604051928160c01c84526020840152166040820152f35b34610679575f3660031901126106795760206001600160401b0360075460801c16604051908152f35b34610679575f366003190112610679576020601754604051908152f35b34610679575f3660031901126106795760206001600160401b03601d5416604051908152f35b34610679575f36600319011261067957602060075460c01c604051908152f35b3461067957602036600319011261067957602061287e61287961345d565b613c30565b604051908152f35b34610679575f366003190112610679576020600f54604051908152f35b34610679575f366003190112610679576128bb614048565b5f805160206156258339815191525460ff8116156129125760ff19165f80516020615625833981519152557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a1005b638dfc202b60e01b5f5260045ffd5b346106795760403660031901126106795761293a613473565b336001600160a01b038216036129565761270090600435614d94565b63334bd91960e11b5f5260045ffd5b34610679575f36600319011261067957602060ff601054166040519015158152f35b3461067957602036600319011261067957602061287e6129a561345d565b61368c565b34610679576040366003190112610679576127006004356129c9613473565b906129d66109908261361e565b61439f565b3461067957602036600319011261067957602061287e60043561361e565b34610679576020366003190112610679577f109951cb6479ea20e2944b032a8b89fb8baa74ed4e3acb636fc2df505b0fd6726020612a356134c3565b612a3d614048565b1515601d5460ff60e01b8260e01b169060ff60e01b191617601d55604051908152a1005b3461067957602036600319011261067957612a7a61345d565b6001600160a01b0381168015612e1557335f525f60205260405f2090612a9f33614443565b6001600160401b0360155416928315612e065760038301928354936002820190815480151580612dfb575b612dc4575b50600654808710612dae5750601e5480151580612da5575b612d8f575060ff601d5460e01c16612b93575b95612b5d866001600160401b03955f60048782612b88988160209f612b3360015f805160206155c58339815191529e019e8f5416613600565b9d6001600160401b038c166001600160401b031982541617905555550155611043826018546135bd565b604080518781526001600160401b039586166020820152949091169084015233929081906060820190565b0390a3604051908152f35b6002546001600160a01b03168015611ddf57602060049160405192838092632773439960e21b82525afa908115611dd4575f91612d4d575b506001600160a01b03168015611ddf576040516336a90ce560e21b8152602081600481855afa908115611dd4575f91612d1e575b50612d0f5760206004916040519283809263cd377c5360e01b82525afa8015611dd4575f90612cdc575b6001546040516318160ddd60e01b81529250602090839060049082906001600160a01b03165afa918215611dd4575f92612ca6575b50612c699082614a2b565b9080821115612c9e57612c7b916135db565b808711612c885750612afa565b86631ada478f60e31b5f5260045260245260445ffd5b50505f612c7b565b9091506020813d602011612cd4575b81612cc2602093836134e1565b81010312610679575190612c69612c5e565b3d9150612cb5565b506020813d602011612d07575b81612cf6602093836134e1565b810103126106795760049051612c29565b3d9150612ce9565b63410a0e0960e01b5f5260045ffd5b612d40915060203d602011612d46575b612d3881836134e1565b8101906135e8565b89612bff565b503d612d2e565b90506020813d602011612d87575b81612d68602093836134e1565b8101031261067957516001600160a01b03811681036106795788612bcb565b3d9150612d5b565b866348e8d0a560e11b5f5260045260245260445ffd5b50808711612ae7565b86632f74c3e960e01b5f5260045260245260445ffd5b601154612dd091614a2b565b600484015490818111612de4575b50612acf565b612df39297916110df916135db565b948780612dde565b506011541515612aca565b630788d25d60e41b5f5260045ffd5b634e46966960e11b5f5260045ffd5b3461067957612e3236613489565b5f80516020615645833981519152549260ff8460401c1615936001600160401b03811680159081612fea575b6001149081612fe0575b159081612fd7575b50612fc85767ffffffffffffffff1981166001175f805160206156458339815191525584612f9c575b50612ea261553b565b612eaa61553b565b612eb261553b565b60ff195f8051602061562583398151915254165f8051602061562583398151915255612edc61553b565b612ee461553b565b6001600160a01b038116928315611ddf576001600160a01b03811615611ddf57612f10612f1692614224565b506142d3565b506006555f52601a60205260405f20600160ff1982541617905561010061ff00196010541617601055612f4557005b68ff0000000000000000195f8051602061564583398151915254165f80516020615645833981519152557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602060405160018152a1005b68ffffffffffffffffff191668010000000000000001175f805160206156458339815191525584612e99565b63f92ee8a960e01b5f5260045ffd5b90501586612e70565b303b159150612e68565b869150612e5e565b34610679575f3660031901126106795760206001600160401b0360075460401c16604051908152f35b346106795760203660031901126106795761303461345d565b61303c614048565b6001600160a01b031680156120a957600180546001600160a01b031916821790557f525d14bd98892d4337e21b6b2e41978a4af15c5c11a9d8fe41402fa715c8300e5f80a2005b346106795761309136613489565b909161309b614048565b6001600160a01b031690811580156131a3575b6120a9576001600160a01b0383165f818152601a602052604090205490939060ff1615613190578115613181576001546001600160a01b03168015159081613177575b506131645760405163a9059cbb60e01b60208201526001600160a01b0391909116602482015260448082018390528152613136906131306064826134e1565b836154d0565b6040519081527fa2231b10d9b4e4166c8a827c99f97691b05aa88fb04e009a4e499005b5c50fcc60203392a4005b82632888137560e01b5f5260045260245ffd5b90508314856130f1565b63162908e360e11b5f5260045ffd5b83632a05184b60e21b5f5260045260245ffd5b506001600160a01b038316156130ae565b34610679575f3660031901126106795760206040517fb552f1bea17e2734c4b1d253bbc784c04b883a78e93589442d4b5d6e6a2f73bd8152f35b34610679576020366003190112610679576001600160a01b0361320f61345d565b165f525f60205260a060405f20600581015460ff8160081c169182613281575b6001015460c01c60075460c01c8091101580613278575b60ff6001600160401b036008541693604051951515865216151560208501521515604084015260608301526080820152f35b50801515613246565b60ff821615925061322f565b34610679576020366003190112610679576004356001600160401b038111610679576132bd90369060040161342d565b906132c6613fd9565b5f9060c883116133c2575f905f5b848110613318576020848460405190815281838201527fb9c05773bac087c79ca346191a3f49b33e00ab1c6913f7788c9a3ec9391beab260403392a2604051908152f35b6133266105e6828785613585565b6001600160a01b0381169081156133b85780613341916140dd565b80613353575b50506001905b016132d4565b9391909484613361916135bd565b945f1983146133a457806001809401956040519081527fd9933da664e75d182b59723a8724e7cfe7041e1b543741ef3da7c17750c77ff360203392a49086613347565b634e487b7160e01b5f52601160045260245ffd5b505060019061334d565b8263bb1cb70b60e01b5f5260045260c860245260445ffd5b34610679576020366003190112610679576004359063ffffffff60e01b821680920361067957602091637965db0b60e01b811490811561341c575b5015158152f35b6301ffc9a760e01b14905083613415565b9181601f84011215610679578235916001600160401b038311610679576020808501948460051b01011161067957565b600435906001600160a01b038216820361067957565b602435906001600160a01b038216820361067957565b6060906003190112610679576004356001600160a01b038116810361067957906024356001600160a01b0381168103610679579060443590565b60043590811515820361067957565b60243590811515820361067957565b90601f801991011681019081106001600160401b0382111761350257604052565b634e487b7160e01b5f52604160045260245ffd5b6001600160401b03811161350257601f01601f191660200190565b600435906001600160401b038216820361067957565b34610679575f3660031901126106795760206040515f8152f35b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b91908110156135955760051b0190565b634e487b7160e01b5f52603260045260245ffd5b356001600160a01b03811681036106795790565b919082018092116133a457565b9060ff801983541691151516179055565b919082039182116133a457565b90816020910312610679575180151581036106795790565b6001600160401b036001911601906001600160401b0382116133a457565b5f525f80516020615605833981519152602052600160405f20015490565b906001600160401b03809116911603906001600160401b0382116133a457565b818102929181159184041417156133a457565b6001600160401b03166001600160401b0381146133a45760010190565b60018060a01b03165f525f60205260405f2090600582015460ff8116613c2a576003830154926002810154906004810154926001820154916001600160401b038360801c16906001600160401b03829460401c169382151592838094613c16575b6139bf575b508154946001600160401b03600784015416926001600160401b0360075416809414806139b2575b61398b575b50613728614d44565b938360ff839760081c169382613981575b505080613979575b61394c575b5060ff6010541680613945575b8061393c575b80613921575b156138df57505f52601460205260405f206001600160401b0381541692831515806138cd575b1561387d576001600160401b0361379f6137a6928661363c565b168561365c565b9081151580613870575b613854575b5050816001600160401b03821611613812575b5050505b80151580613807575b6137de575b5050565b6011546137ea91614a2b565b8181116137f5575050565b6138049293916110df916135db565b90565b5060115415156137d5565b61383a61384b9495936001600160401b036138336110df956138419561363c565b169061365c565b80956135bd565b9360115490614a2b565b905f80806137c8565b61386892989160026110df92015490614a2b565b955f806137b5565b50600281015415156137b0565b8093506001600160401b039150166001600160401b038216116138a3575b5050506137cc565b61383a6138c49495936001600160401b036138336110df956138419561363c565b905f808061389b565b50836001600160401b03821610613785565b90506001600160401b0383166001600160401b038316119081613919575b5080613910575b6138a3575050506137cc565b50821515613904565b90505f6138fd565b506001600160401b0384166001600160401b0384161161375f565b50841515613759565b5080613753565b9050600190825f5260126020526001600160401b038060405f20541691168111156137465793505f613746565b506001613741565b109050835f613739565b60060154909590808211156139aa576139a3916135db565b945f61371f565b50505f6139a3565b506006810154151561371a565b8091929597505f9996999893949852601460205260405f20988554998a6001600160401b0360078901541699848b1480613c09575b613be3575b60ff8c60081c1680613bda575b80613bc7575b613b88575b5050600201549081151580613b7f575b613b65575b5050613a3190613600565b6001600160401b0360075416905b6001600160401b03811682811015613b3b57805f52601460205260405f208b90828b1480613b2e575b613b07575b60028101549283151580613afe575b80613aeb575b613a99575b50505050613a949061366f565b613a3f565b5f5260126020526001600160401b038060405f20541691541681811115613a875792613adc613a94959b936001600160401b036138336110df95613ae19861363c565b614a2b565b96905f8080613a87565b506001600160401b038254161515613a82565b50821515613a7c565b90506006880154808d115f14613b2757613b21908d6135db565b90613a6d565b505f613b21565b5060068901541515613a68565b505093949750945094905f935f95845f5260126020526001600160401b0360405f205416946136f2565b966110df613b7792613a319499614a2b565b95905f613a26565b50801515613a21565b6001600160401b03835416808210613ba1575b50613a11565b613bbe926001600160401b0361383360029697946110df9461363c565b91905f80613b9b565b506001600160401b038354161515613a0c565b50811515613a06565b9060068901548082115f14613c0157613bfb916135db565b906139f9565b50505f613bfb565b50600689015415156139f4565b506001600160401b036007541681106136ed565b505f9150565b60018060a01b03165f525f60205260405f2090600582015460ff8116908115613d19575b50613d14576002820154918054906001600160401b036007820154166001600160401b03600754161480613d07575b613ce1575b6001600160401b036001613c9a614d44565b92015460401c1690816001600160401b0382161180613cd8575b613cbd57505050565b916001600160401b036138336110df9361380496979561363c565b50821515613cb4565b9060068201548082115f14613cff57613cf9916135db565b90613c88565b50505f613cf9565b5060068101541515613c83565b5f9150565b60ff915060081c16155f613c54565b8115613d32570490565b634e487b7160e01b5f52601260045260245ffd5b9091906001600160a01b0380841691908116808314613db75780849115159081613dac575b50613d9c575b50508015159081613d91575b50613d86575050565b613d8f91615306565b565b90503014155f613d7d565b613da591614ef2565b5f82613d71565b90503014155f613d6b565b5050505050565b9067ffffffffffffffff60401b82549160401b169067ffffffffffffffff60401b1916179055565b60018060a01b03811691825f525f60205260405f206005810160ff815416918315158093151514613fa657613e98602094613ea293613e4d7ff3407d03b594227ddbb9468b57b4ac6fa9c361d9209ed837b15922d1364e56f298613e48614e57565b614443565b8115613ee157835480151580613ed3575b613eab575b50613e718454600f546135bd565b600f555f600285018190556004850155805491151560ff1661ffff19909216919091179055565b60016107e0614d44565b604051908152a2565b600a5410613eca57613ec08454600a546135db565b600a555b5f613e63565b5f600a55613ec4565b5060ff825460081c16613e5e565b600f54845411613f9d57613ef88454600f546135db565b600f555b613f04614d44565b6007548060c01c908115159182613f89575b5081613f6a575b5015613f3957815461ff00191682556135ca9060018601613dbe565b50835480613f48575b506135ca565b613f5490600a546135bd565b600a55805461ff0019166101001781555f613f42565b6001600160401b03915060801c166001600160401b038216105f613f1d565b6001600160401b038416101591505f613f16565b5f600f55613efc565b505050505050565b6001600160401b0381116135025760051b60200190565b80518210156135955760209160051b010190565b335f9081527f55d7f33d0a296b8db4e13bb2940becc7df2ac8a524f74cc457e170afe809c961602052604090205460ff161561401157565b63e2517d3f60e01b5f52336004527fb552f1bea17e2734c4b1d253bbc784c04b883a78e93589442d4b5d6e6a2f73bd60245260445ffd5b335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff161561408057565b63e2517d3f60e01b5f52336004525f60245260445ffd5b5f8181525f805160206156058339815191526020908152604080832033845290915290205460ff16156140c75750565b63e2517d3f60e01b5f523360045260245260445ffd5b6001600160a01b0381165f81815260208190526040902093929161410090614443565b6001600160401b0360155416801561421c57600385019283549560028101805480151580614211575b6141da575b5087156141cf576004825f80938160015f805160206155c5833981519152999897019a6141646001600160401b038d5416613600565b9b6001600160401b038a166001600160401b03198254161790555555015561418e876018546135bd565b60185561419b8782614cdc565b604080518881526001600160401b03968716602082015292909516948201949094526001600160a01b0390931692606090a3565b505f96505050505050565b6011546141e691614a2b565b6004830154908181116141fa575b5061412e565b6142099299916110df916135db565b965f806141f4565b506011541515614129565b505f93505050565b6001600160a01b0381165f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff166142ce576001600160a01b03165f8181527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d60205260408120805460ff191660011790553391907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8180a4600190565b505f90565b6001600160a01b0381165f9081527fb2ce6f3a0968c56a08b54f4f0d1d0af4871f2c3340441c6ce862aaa53c9d37c7602052604090205460ff166142ce576001600160a01b03165f8181527fb2ce6f3a0968c56a08b54f4f0d1d0af4871f2c3340441c6ce862aaa53c9d37c760205260408120805460ff191660011790553391907ffbd454f36a7e1a388bd6fc3ab10d434aa4578f811acbbcf33afb1c697486313c907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9080a4600190565b5f8181525f80516020615605833981519152602090815260408083206001600160a01b038616845290915290205460ff1661443d575f8181525f80516020615605833981519152602090815260408083206001600160a01b0395909516808452949091528120805460ff19166001179055339291907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9080a4600190565b50505f90565b6001600160a01b03165f908152602081905260409020614461614d44565b600582019160ff835416614a1e5760018101906001600160401b03825460801c161580614a0a575b61498d575b81546001600160401b038160801c1680151580614979575b6146b0575b50506001600160401b03825460401c166001600160401b03841681811115613fa6578254916001600160401b036007850154166001600160401b0360075416809114806146a3575b61467d575b60ff601054168061466f575b80614666575b6145a7575b50505093613d8f946001600160401b0361452f81865460401c168761363c565b16918215159081614598575b508061458f575b61454f575b505050613dbe565b61457b6145616145859360049361365c565b600285016145708282546135bd565b905560115490614a2b565b92019182546135bd565b90555f8080614547565b50801515614542565b60ff91505460081c165f61453b565b5f52601460205260405f20906001600160401b038254168015158061465d575b6145d1575061450f565b613d8f98506001600160401b0361379f8194936145ed9361363c565b80151580614650575b61462b575b50541680911161460d57505050613dbe565b61457b614561614585936001600160401b036138336004958a61363c565b600282015461463991614a2b565b614648600387019182546135bd565b90555f6145fb565b50600282015415156145f6565b508082106145c7565b5083151561450a565b5060ff885460081c16614504565b9260068501548082115f1461469b57614695916135db565b926144f8565b50505f614695565b50600685015415156144f3565b80614717915f9793949697526014602052600260405f2087546007890196846001600160401b03895416148061496c575b614946575b60ff875460081c168061493d575b8061492a575b6148d5575b50500154801515806148c8575b6148a3575b50613600565b6001600160401b03600754166001600160401b03821690811015614829579081614784925f52601460205260405f20908754816001600160401b03885416148061481c575b6147f9575b600283015491821515806147f0575b806147dd575b614789575b5050505061366f565b614717565b5f5260126020526001600160401b038060405f205416935416908382111561477b576001600160401b036138336147c395613adc9461363c565b6147d2600388019182546135bd565b90555f80808061477b565b506001600160401b038454161515614776565b50811515614770565b6006890154808211156148145761480f916135db565b614761565b50505f614761565b506006890154151561475c565b50505f600285018190556004850155600754835467ffffffffffffffff60801b191660809190911b67ffffffffffffffff60801b161783559392905060075460401c6001600160401b0316801561489957614885905b83613dbe565b835461ff0019166101001784555f806144ab565b506148858361487f565b6148b1906002880154614a2b565b6148c0600388019182546135bd565b90555f614711565b506002870154151561470c565b6001600160401b039060401c16906001600160401b03835416918281106148fd575b506146ff565b6138336001600160401b03916149129461363c565b614920838a019182546135bd565b90555f80806148f7565b506001600160401b0383541615156146fa565b508115156146f4565b9060068a01548082115f146149645761495e916135db565b906146e6565b50505f61495e565b5060068a015415156146e1565b506001600160401b036007541681106144a6565b600754825467ffffffffffffffff60801b191660809190911b67ffffffffffffffff60801b161782556001600160401b03825460401c16156149dc575b835461ff00191661010017845561448e565b60075460401c6001600160401b03168015614a00576149fb9083613dbe565b6149ca565b506149fb8361487f565b506001600160401b03600754161515614489565b613d8f9250600101613dbe565b9190915f838202915f1985820991838084109303928084039314614aaa5782670de0b6b3a76400001115614a9b57507faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac106699394670de0b6b3a7640000910990828211900360ee1b910360121c170290565b63227bc15360e01b8152600490fd5b505050670de0b6b3a76400009192500490565b9190915f838202915f1985820991838084109303928084039314614b2157826127101115614a9b57507fbc01a36e2eb1c432ca57a786c226809d495182a9930be0ded288ce703afb7e919394612710910990828211900360fc1b910360041c170290565b5050506127109192500490565b90670de0b6b3a76400008202905f19670de0b6b3a7640000840992828085109403938085039414614bcd5783821115614bbe57670de0b6b3a7640000829109815f0382168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b63227bc15360e01b5f5260045ffd5b50906138049250613d28565b906127108202905f19612710840992828085109403938085039414614bcd5783821115614bbe57612710829109815f0382168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b9091828202915f1984820993838086109503948086039514614ccf5784831115614bbe57829109815f0382168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b5050906138049250613d28565b81156137da576001546001600160a01b0316918215611ddf57823b15610679576040516340c10f1960e01b81526001600160a01b039290921660048301526024820152905f908290604490829084905af18015611dd457614d3a5750565b5f613d8f916134e1565b6001600160401b0342166007546001600160401b038160801c16908115614d8f5760401c6001600160401b0316808310614d895750808211614d84575090565b905090565b91505090565b505090565b5f8181525f80516020615605833981519152602090815260408083206001600160a01b038616845290915290205460ff161561443d575f8181525f80516020615605833981519152602090815260408083206001600160a01b0395909516808452949091528120805460ff19169055339291907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9080a4600190565b60ff5f805160206156258339815191525416614e4857565b63d93c066560e01b5f5260045ffd5b614e5f614d44565b600b546001600160401b0381166001600160401b03831692818411801590614eea575b614ee4576001600160401b0391614e989161363c565b1680151580614ed9575b614eb9575b5067ffffffffffffffff191617600b55565b614ec8614ed091600a5461365c565b6009546135bd565b6009555f614ea7565b50600a541515614ea2565b50505050565b508115614e82565b81156137da57614f00614e57565b614f0981614443565b60018060a01b0316805f525f60205260405f209182548181106152f75761501a925f9060078601916001600160401b038354166001600160401b036007541614806152ea575b6152be575b614f5e81866135db565b908115159081806152af575b8061529e575b615278575b600589019060ff825416615252575b614f8c614d44565b916001600160401b038897541693600754926001600160401b038416809614615222575b506001600160401b038416916001600160401b038460401c168310158061520e575b80615200575b806151f4575b806151eb575b806151e4575b156150cd575050505050506002870192835493615008828487614c57565b948561501e575b5050505050506135db565b9055565b8086116150c5575b917f8fbf3369190b6598d95844b965a46ddb5d60bab3af3e4854b6b699dc5d6d22799391615056876040956135db565b905561506486600c546135bd565b600c5561507386600e546135bd565b600e5561509860048b019161508b8354918583614c57565b908082116150bd576135db565b90556001600160401b03600754169482519182526020820152a35f808080808061500f565b9050806135db565b945084615026565b909192939497506001600160401b036008541683101592836151cd575b50826151be575b826151b1575b50816151a9575b5061510e575b50505050506135db565b6001600160401b0391826151259260801c1661363c565b1680615132575b80615104565b8161519d615161837f5898c7d7047da30469bd7b9309dd1dd2e19eb2cccef3100f3322fae8da09f94c9561365c565b9261516e84600d546135bd565b600d5561517d84600e546135bd565b600e55604051938493846040919493926060820195825260208201520152565b0390a35f80808061512c565b90505f6150fe565b5460ff161591505f6150f7565b805460081c60ff1692506150f1565b608085901c6001600160401b03161192505f6150ea565b5081614fea565b50881515614fe4565b5060ff81541615614fde565b5060ff815460081c16614fd8565b506001600160401b03600854168310614fd2565b61523291985060068d01546135bd565b8089111561524b5761524490896135db565b965f614fb0565b505f615244565b600f54881161526f5761526788600f546135db565b600f55614f84565b5f600f55614f84565b600a5483116152955761528d83600a546135db565b600a55614f75565b5f600a55614f75565b5060ff60058a015460081c16614f70565b5060ff60058a01541615614f6a565b5060068601805490818611156152e1576152da825b80936135db565b9055614f54565b6152da866152d3565b5060068701541515614f4f565b6305e72d3960e11b5f5260045ffd5b81156137da57615314614e57565b61531d81614443565b6001600160a01b03165f9081526020819052604090206001810180546001600160c01b03164260c01b6001600160c01b03191617905560ff90600590615361614d44565b600754908160c01c91821515928361548d575b5082615473575b50506153888583546135bd565b82551561543c57600781016001600160401b038154166001600160401b036007541603615413575b50600681016153c08582546135bd565b905581810180548481161580615406575b6153f4575b50505b0154166153e35750565b6153ef90600f546135bd565b600f55565b61ff0019166101001790555f806153d6565b50848160081c16156153d1565b5f60068301556001600160401b038060075416166001600160401b03198254161790555f6153b0565b818101805484811615615451575b50506153d9565b6101009061ff00191617905561546984600a546135bd565b600a555f8061544a565b60801c6001600160401b0390811691161090505f8061537b565b6001600160401b038316101592505f615374565b3d156154cb573d906154b282613516565b916154c060405193846134e1565b82523d5f602084013e565b606090565b5f806154f89260018060a01b03169360208151910182865af16154f16154a1565b9083615566565b8051908115159182615520575b505061550e5750565b635274afe760e01b5f5260045260245ffd5b61553392506020809183010191016135e8565b155f80615505565b60ff5f805160206156458339815191525460401c161561555757565b631afcd79f60e31b5f5260045ffd5b9061558a575080511561557b57805190602001fd5b630a12f52160e11b5f5260045ffd5b815115806155bb575b61559b575090565b639996b31560e01b5f9081526001600160a01b0391909116600452602490fd5b50803b1561559356fe441c9fb962eef2eb58232351b5f5c29aebe2daf5d0496fa1df82d30274eb7982360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a164736f6c634300081a000a
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.