Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00Latest 13 from a total of 13 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Submit Score To ... | 24365815 | 20 days ago | IN | 0 ETH | 0.00131512 | ||||
| Submit Score To ... | 24365808 | 20 days ago | IN | 0 ETH | 0.00019381 | ||||
| Submit Score To ... | 24365797 | 20 days ago | IN | 0 ETH | 0.00003258 | ||||
| Submit Score To ... | 24365791 | 20 days ago | IN | 0 ETH | 0.0000245 | ||||
| Emergency Set Nu... | 24365764 | 20 days ago | IN | 0 ETH | 0.00005148 | ||||
| Trigger VRF For ... | 24365709 | 20 days ago | IN | 0 ETH | 0.00003809 | ||||
| Set Pool Creatio... | 24365706 | 20 days ago | IN | 0 ETH | 0.00000621 | ||||
| Create Pool | 24365680 | 20 days ago | IN | 0.005 ETH | 0.00054317 | ||||
| Create Pool | 24365672 | 20 days ago | IN | 0.005 ETH | 0.00070064 | ||||
| Transfer Admin | 24365655 | 20 days ago | IN | 0 ETH | 0.00000374 | ||||
| Set Score Admin | 24365655 | 20 days ago | IN | 0 ETH | 0.00000402 | ||||
| Set Aave Address... | 24365648 | 20 days ago | IN | 0 ETH | 0.00001831 | ||||
| Set VRF Funding ... | 24365647 | 20 days ago | IN | 0 ETH | 0.00000386 |
Latest 4 internal transactions
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| Fund Subscriptio... | 24365680 | 20 days ago | 0.005 ETH | ||||
| 0x60e03462 | 24365680 | 20 days ago | Contract Creation | 0 ETH | |||
| Fund Subscriptio... | 24365672 | 20 days ago | 0.005 ETH | ||||
| 0x60e03462 | 24365672 | 20 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0xd573508f...2EeFb5eA3 The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
SquaresFactory
Compiler Version
v0.8.24+commit.e11b9ed9
Optimization Enabled:
Yes with 1 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {SquaresPool} from "./SquaresPool.sol";
import {ISquaresPool} from "./interfaces/ISquaresPool.sol";
import {IVRFCoordinatorV2Plus} from "./interfaces/IVRFCoordinatorV2Plus.sol";
import {SquaresLib} from "./libraries/SquaresLib.sol";
/// @title SquaresFactory
/// @notice Factory for deploying Super Bowl Squares pools with Chainlink VRF
contract SquaresFactory {
// ============ Events ============
event PoolCreated(
address indexed pool,
address indexed creator,
string name,
uint256 squarePrice,
address paymentToken
);
event VRFTriggeredForAllPools(uint256 poolsTriggered);
event EmergencyNumbersSetForAllPools(uint256 poolsSet);
event CreationFeeUpdated(uint256 oldFee, uint256 newFee);
event FeesWithdrawn(address indexed to, uint256 amount);
event AdminTransferred(address indexed oldAdmin, address indexed newAdmin);
event ScoreAdminUpdated(address indexed oldAdmin, address indexed newAdmin);
event VRFSubscriptionCreated(uint256 indexed subscriptionId);
event VRFConsumerAdded(uint256 indexed subscriptionId, address indexed consumer);
event VRFSubscriptionFunded(uint256 indexed subscriptionId, uint256 amount);
event VRFSubscriptionCancelled(uint256 indexed subscriptionId, address indexed fundsRecipient);
event ScoreSubmittedToAllPools(uint8 indexed quarter, uint8 teamAScore, uint8 teamBScore);
event PoolCreationPaused(bool paused);
event AaveAddressesUpdated(address pool, address gateway, address aWETH, address aUSDC);
event YieldWithdrawnFromAllPools(uint256 poolsWithdrawn);
// ============ State ============
address[] public allPools;
mapping(address => address[]) public poolsByCreator;
mapping(bytes32 => bool) public poolNameExists;
// External contract addresses (immutable per chain)
address public immutable vrfCoordinator;
// Default Chainlink VRF configuration
uint256 public defaultVRFSubscriptionId;
bytes32 public defaultVRFKeyHash;
// Pool creation fee (covers VRF costs)
uint256 public creationFee;
// VRF funding amount (ETH to fund VRF per pool)
uint96 public vrfFundingAmount;
// Admin
address public admin;
// Score Admin - can submit scores to all pools
address public scoreAdmin;
// Pool creation pause state
bool public poolCreationPaused;
// Aave V3 configuration
address public aavePool;
address public wethGateway;
address public aWETH;
address public aUSDC;
// ============ Errors ============
error OnlyAdmin();
error Unauthorized();
error InsufficientCreationFee(uint256 sent, uint256 required);
error TransferFailed();
error InvalidAddress();
error PoolCreationIsPaused();
error PoolNameAlreadyExists(string name);
// ============ Modifiers ============
modifier onlyAdmin() {
if (msg.sender != admin) revert OnlyAdmin();
_;
}
// ============ Constructor ============
constructor(
address _vrfCoordinator,
bytes32 _vrfKeyHash,
uint256 _creationFee
) {
if (_vrfCoordinator == address(0)) revert InvalidAddress();
vrfCoordinator = _vrfCoordinator;
defaultVRFKeyHash = _vrfKeyHash;
creationFee = _creationFee;
admin = msg.sender;
scoreAdmin = msg.sender; // Initially deployer is score admin
// Create VRF subscription - factory becomes the owner
defaultVRFSubscriptionId = IVRFCoordinatorV2Plus(_vrfCoordinator).createSubscription();
emit VRFSubscriptionCreated(defaultVRFSubscriptionId);
}
// ============ Admin Functions ============
/// @notice Update VRF subscription
function setVRFSubscription(uint256 subscriptionId) external onlyAdmin {
defaultVRFSubscriptionId = subscriptionId;
}
/// @notice Update VRF key hash
function setVRFKeyHash(bytes32 keyHash) external onlyAdmin {
defaultVRFKeyHash = keyHash;
}
/// @notice Update creation fee
function setCreationFee(uint256 _fee) external onlyAdmin {
creationFee = _fee;
}
/// @notice Update VRF funding amount per pool
function setVRFFundingAmount(uint96 _amount) external onlyAdmin {
vrfFundingAmount = _amount;
}
/// @notice Fund the VRF subscription with additional ETH
/// @dev Use this if the subscription balance is running low
function fundVRFSubscription() external payable onlyAdmin {
if (msg.value == 0) revert InsufficientCreationFee(0, 1);
IVRFCoordinatorV2Plus(vrfCoordinator).fundSubscriptionWithNative{value: msg.value}(
defaultVRFSubscriptionId
);
emit VRFSubscriptionFunded(defaultVRFSubscriptionId, msg.value);
}
/// @notice Cancel the VRF subscription and withdraw remaining funds
function cancelAndWithdrawVRFSubscription(address to) external onlyAdmin {
if (to == address(0)) revert InvalidAddress();
IVRFCoordinatorV2Plus(vrfCoordinator).cancelSubscription(defaultVRFSubscriptionId, to);
emit VRFSubscriptionCancelled(defaultVRFSubscriptionId, to);
defaultVRFSubscriptionId = 0;
}
/// @notice Withdraw accumulated fees
function withdrawFees(address to) external onlyAdmin {
if (to == address(0)) revert InvalidAddress();
uint256 balance = address(this).balance;
(bool success,) = to.call{value: balance}("");
if (!success) revert TransferFailed();
emit FeesWithdrawn(to, balance);
}
/// @notice Transfer admin role
function transferAdmin(address newAdmin) external onlyAdmin {
if (newAdmin == address(0)) revert InvalidAddress();
emit AdminTransferred(admin, newAdmin);
admin = newAdmin;
}
/// @notice Set the score admin who can submit scores to all pools
function setScoreAdmin(address _scoreAdmin) external onlyAdmin {
if (_scoreAdmin == address(0)) revert InvalidAddress();
emit ScoreAdminUpdated(scoreAdmin, _scoreAdmin);
scoreAdmin = _scoreAdmin;
}
/// @notice Pause or unpause pool creation
function setPoolCreationPaused(bool _paused) external onlyAdmin {
poolCreationPaused = _paused;
emit PoolCreationPaused(_paused);
}
/// @notice Set Aave V3 addresses for yield generation
/// @param _pool Aave Pool contract address
/// @param _gateway WETH Gateway contract address
/// @param _aWETH aWETH token address
/// @param _aUSDC aUSDC token address
function setAaveAddresses(
address _pool,
address _gateway,
address _aWETH,
address _aUSDC
) external onlyAdmin {
aavePool = _pool;
wethGateway = _gateway;
aWETH = _aWETH;
aUSDC = _aUSDC;
emit AaveAddressesUpdated(_pool, _gateway, _aWETH, _aUSDC);
}
/// @notice Withdraw yield from all finished pools
function withdrawYieldFromAllPools() external onlyAdmin {
uint256 withdrawn;
uint256 len = allPools.length;
unchecked {
for (uint256 i; i < len; ++i) {
try SquaresPool(payable(allPools[i])).withdrawYieldFromFactory() { ++withdrawn; } catch {}
}
}
emit YieldWithdrawnFromAllPools(withdrawn);
}
/// @notice Update Aave configuration for an existing pool
/// @dev Useful for fixing misconfigured pools or updating to new Aave addresses
/// @param pool Address of the pool to update
/// @param _aavePool Aave Pool contract address
/// @param _wethGateway WETH Gateway contract address
/// @param _aToken aToken address (aWETH for ETH pools, aUSDC for USDC pools)
function updatePoolAaveConfig(
address pool,
address _aavePool,
address _wethGateway,
address _aToken
) external onlyAdmin {
SquaresPool(payable(pool)).setAaveConfig(_aavePool, _wethGateway, _aToken);
}
// ============ Score Admin Functions ============
/// @notice Submit score to all pools that are ready for this quarter
/// @param quarter The quarter to submit (0=Q1, 1=Q2, 2=Q3, 3=Final)
/// @param teamAScore Team A's score
/// @param teamBScore Team B's score
function submitScoreToAllPools(uint8 quarter, uint8 teamAScore, uint8 teamBScore) external {
if (msg.sender != scoreAdmin && msg.sender != admin) revert Unauthorized();
uint256 len = allPools.length;
unchecked {
for (uint256 i; i < len; ++i) {
try SquaresPool(payable(allPools[i])).submitScoreFromFactory(quarter, teamAScore, teamBScore) {} catch {}
}
}
emit ScoreSubmittedToAllPools(quarter, teamAScore, teamBScore);
}
/// @notice Trigger VRF for all pools that are ready
function triggerVRFForAllPools() external {
if (msg.sender != scoreAdmin && msg.sender != admin) revert Unauthorized();
uint256 triggered;
uint256 len = allPools.length;
unchecked {
for (uint256 i; i < len; ++i) {
try SquaresPool(payable(allPools[i])).closePoolAndRequestVRFFromFactory() { ++triggered; } catch {}
}
}
emit VRFTriggeredForAllPools(triggered);
}
/// @notice Emergency: Set numbers for all pools stuck waiting for VRF
function emergencySetNumbersForAllPools() external onlyAdmin {
uint256 set;
uint256 len = allPools.length;
unchecked {
for (uint256 i; i < len; ++i) {
SquaresPool pool = SquaresPool(payable(allPools[i]));
try pool.state() returns (ISquaresPool.PoolState state) {
if (state == ISquaresPool.PoolState.CLOSED && pool.vrfRequested()) {
uint256 seed = uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao, allPools[i], i)));
try pool.emergencySetNumbers(
SquaresLib.fisherYatesShuffle(seed),
SquaresLib.fisherYatesShuffle(uint256(keccak256(abi.encodePacked(seed, uint256(1)))))
) { ++set; } catch {}
}
} catch {}
}
}
emit EmergencyNumbersSetForAllPools(set);
}
// ============ Factory Functions ============
/// @notice Create a new Super Bowl Squares pool
/// @param params Pool configuration parameters
/// @return pool Address of the newly created pool contract
function createPool(ISquaresPool.PoolParams calldata params) external payable returns (address pool) {
if (poolCreationPaused) revert PoolCreationIsPaused();
// Check for duplicate pool name
bytes32 nameHash = keccak256(bytes(params.name));
if (poolNameExists[nameHash]) revert PoolNameAlreadyExists(params.name);
poolNameExists[nameHash] = true;
// Calculate total required (creation fee + VRF funding)
uint256 totalRequired = creationFee + vrfFundingAmount;
if (msg.value < totalRequired) {
revert InsufficientCreationFee(msg.value, totalRequired);
}
// Deploy new pool contract
SquaresPool newPool = new SquaresPool(
vrfCoordinator,
msg.sender // operator
);
// Initialize with parameters
newPool.initialize(params);
// Set VRF config
newPool.setVRFConfig(
defaultVRFSubscriptionId,
defaultVRFKeyHash
);
// Set Aave config if configured
if (aavePool != address(0)) {
// Determine correct aToken based on payment token
address poolAToken = params.paymentToken == address(0) ? aWETH : aUSDC;
newPool.setAaveConfig(aavePool, wethGateway, poolAToken);
}
pool = address(newPool);
// Add pool as VRF consumer (factory is subscription owner, so this works)
IVRFCoordinatorV2Plus(vrfCoordinator).addConsumer(defaultVRFSubscriptionId, pool);
emit VRFConsumerAdded(defaultVRFSubscriptionId, pool);
// Fund the VRF subscription with native ETH
if (vrfFundingAmount > 0) {
IVRFCoordinatorV2Plus(vrfCoordinator).fundSubscriptionWithNative{value: vrfFundingAmount}(
defaultVRFSubscriptionId
);
emit VRFSubscriptionFunded(defaultVRFSubscriptionId, vrfFundingAmount);
}
// Track pool
allPools.push(pool);
poolsByCreator[msg.sender].push(pool);
emit PoolCreated(pool, msg.sender, params.name, params.squarePrice, params.paymentToken);
// Refund excess payment
if (msg.value > totalRequired) {
(bool success,) = msg.sender.call{value: msg.value - totalRequired}("");
if (!success) revert TransferFailed();
}
return pool;
}
// ============ View Functions ============
/// @notice Get all pools created by a specific address
function getPoolsByCreator(address creator) external view returns (address[] memory) {
return poolsByCreator[creator];
}
/// @notice Get all pools with pagination
function getAllPools(uint256 offset, uint256 limit) external view returns (address[] memory pools, uint256 total) {
total = allPools.length;
if (offset >= total) return (new address[](0), total);
uint256 end = offset + limit > total ? total : offset + limit;
pools = new address[](end - offset);
unchecked {
for (uint256 i = offset; i < end; ++i) pools[i - offset] = allPools[i];
}
}
/// @notice Get the total number of pools
function getPoolCount() external view returns (uint256) {
return allPools.length;
}
// ============ Receive ETH ============
receive() external payable {}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {ISquaresPool} from "./interfaces/ISquaresPool.sol";
import {IERC20} from "./interfaces/IERC20.sol";
import {IVRFCoordinatorV2Plus, VRFConsumerBaseV2Plus, VRFV2PlusClient} from "./interfaces/IVRFCoordinatorV2Plus.sol";
import {IPool, IWrappedTokenGatewayV3, IAToken} from "./interfaces/IAaveV3.sol";
import {SquaresLib} from "./libraries/SquaresLib.sol";
/// @notice Minimal interface for getting admin from factory
interface ISquaresFactory {
function admin() external view returns (address);
}
/// @title SquaresPool
/// @notice Super Bowl Squares with Chainlink VRF for randomness and admin score submission
contract SquaresPool is ISquaresPool, VRFConsumerBaseV2Plus {
// ============ Constants ============
uint16 private constant VRF_REQUEST_CONFIRMATIONS = 3;
uint32 private constant VRF_NUM_WORDS = 1;
uint32 private constant VRF_CALLBACK_GAS_LIMIT = 500000;
// ============ Immutables ============
address public immutable factory;
address public immutable operator;
// Aave integration
IPool public aavePool;
IWrappedTokenGatewayV3 public wethGateway;
address public aToken; // aWETH or aUSDC depending on paymentToken
uint256 public totalPrincipalDeposited; // Track principal separately from totalPot
// ============ Pool Configuration ============
string public name;
uint256 public squarePrice;
address public paymentToken;
uint8 public maxSquaresPerUser;
uint8[4] public payoutPercentages;
string public teamAName;
string public teamBName;
uint256 public purchaseDeadline;
uint256 public vrfTriggerTime;
// Chainlink VRF Configuration
uint256 public vrfSubscriptionId;
bytes32 public vrfKeyHash;
// ============ State ============
PoolState public state;
bytes32 public passwordHash;
address[100] public grid;
uint8[10] public rowNumbers;
uint8[10] public colNumbers;
bool public numbersSet;
uint256 public totalPot;
uint256 public squaresSold;
// VRF state
uint256 public vrfRequestId;
bool public vrfRequested;
// Score tracking
mapping(Quarter => Score) public scores;
// User tracking
mapping(address => uint8) public userSquareCount;
mapping(address => mapping(Quarter => bool)) public payoutClaimed;
// Unclaimed winnings tracking
uint256 public unclaimedRolledAmount;
uint256 public finalDistributionPool;
bool public finalDistributionCalculated;
mapping(address => bool) public finalDistributionClaimed;
// ============ Errors ============
error InvalidState(PoolState current, PoolState required);
error SquareAlreadyOwned(uint8 position);
error InvalidPosition(uint8 position);
error MaxSquaresExceeded(address user, uint8 current, uint8 max);
error InsufficientPayment(uint256 sent, uint256 required);
error TransferFailed();
error PurchaseDeadlinePassed();
error VRFTriggerTimeNotReached();
error VRFAlreadyRequested();
error OnlyOperator();
error OnlyFactory();
error PayoutAlreadyClaimed();
error NotWinner();
error ScoreNotSettled();
error InvalidPayoutPercentages();
error InvalidQuarterProgression();
error InvalidPassword();
error NoSquaresSold();
error GameNotFinished();
error NoYieldToWithdraw();
error AaveNotConfigured();
// ============ Modifiers ============
modifier onlyOperator() {
if (msg.sender != operator) revert OnlyOperator();
_;
}
modifier onlyFactory() {
if (msg.sender != factory) revert OnlyFactory();
_;
}
modifier inState(PoolState required) {
if (state != required) revert InvalidState(state, required);
_;
}
// ============ Constructor ============
constructor(
address _vrfCoordinator,
address _operator
) VRFConsumerBaseV2Plus(_vrfCoordinator) {
factory = msg.sender;
operator = _operator;
state = PoolState.OPEN;
}
// ============ Initialization ============
function initialize(PoolParams calldata params) external onlyFactory {
if (!SquaresLib.validatePayoutPercentages(params.payoutPercentages)) {
revert InvalidPayoutPercentages();
}
name = params.name;
squarePrice = params.squarePrice;
paymentToken = params.paymentToken;
maxSquaresPerUser = params.maxSquaresPerUser;
payoutPercentages = params.payoutPercentages;
teamAName = params.teamAName;
teamBName = params.teamBName;
purchaseDeadline = params.purchaseDeadline;
vrfTriggerTime = params.vrfTriggerTime;
passwordHash = params.passwordHash;
}
/// @notice Set Chainlink VRF configuration (called by factory)
function setVRFConfig(
uint256 _subscriptionId,
bytes32 _keyHash
) external onlyFactory {
vrfSubscriptionId = _subscriptionId;
vrfKeyHash = _keyHash;
}
/// @notice Set Aave V3 configuration (called by factory)
function setAaveConfig(
address _aavePool,
address _wethGateway,
address _aToken
) external onlyFactory {
aavePool = IPool(_aavePool);
wethGateway = IWrappedTokenGatewayV3(_wethGateway);
aToken = _aToken;
}
// ============ Player Functions ============
function buySquares(uint8[] calldata positions, string calldata password) external payable inState(PoolState.OPEN) {
if (block.timestamp > purchaseDeadline) revert PurchaseDeadlinePassed();
// Verify password for private pools
if (passwordHash != bytes32(0)) {
if (keccak256(bytes(password)) != passwordHash) revert InvalidPassword();
}
uint256 totalCost = squarePrice * positions.length;
// Always track user square count for final distribution
uint8 newCount = userSquareCount[msg.sender] + uint8(positions.length);
// Check max squares limit if set
if (maxSquaresPerUser > 0 && newCount > maxSquaresPerUser) {
revert MaxSquaresExceeded(msg.sender, userSquareCount[msg.sender], maxSquaresPerUser);
}
userSquareCount[msg.sender] = newCount;
if (paymentToken == address(0)) {
if (msg.value < totalCost) revert InsufficientPayment(msg.value, totalCost);
if (msg.value > totalCost) {
(bool success,) = msg.sender.call{value: msg.value - totalCost}("");
if (!success) revert TransferFailed();
}
} else {
bool success = IERC20(paymentToken).transferFrom(msg.sender, address(this), totalCost);
if (!success) revert TransferFailed();
}
// Deposit to Aave if configured
if (address(aavePool) != address(0)) {
_depositToAave(totalCost);
}
for (uint256 i = 0; i < positions.length; i++) {
uint8 pos = positions[i];
if (pos >= 100) revert InvalidPosition(pos);
if (grid[pos] != address(0)) revert SquareAlreadyOwned(pos);
grid[pos] = msg.sender;
emit SquarePurchased(msg.sender, pos, squarePrice);
}
totalPot += totalCost;
squaresSold += positions.length;
}
function claimPayout(Quarter quarter) external {
if (uint8(quarter) == 0 && state < PoolState.Q1_SCORED) revert ScoreNotSettled();
if (uint8(quarter) == 1 && state < PoolState.Q2_SCORED) revert ScoreNotSettled();
if (uint8(quarter) == 2 && state < PoolState.Q3_SCORED) revert ScoreNotSettled();
if (uint8(quarter) == 3 && state < PoolState.FINAL_SCORED) revert ScoreNotSettled();
if (payoutClaimed[msg.sender][quarter]) revert PayoutAlreadyClaimed();
(address winner, uint256 payout) = getWinner(quarter);
if (msg.sender != winner) revert NotWinner();
payoutClaimed[msg.sender][quarter] = true;
if (paymentToken == address(0)) {
(bool success,) = msg.sender.call{value: payout}("");
if (!success) revert TransferFailed();
} else {
bool success = IERC20(paymentToken).transfer(msg.sender, payout);
if (!success) revert TransferFailed();
}
emit PayoutClaimed(msg.sender, quarter, payout);
}
// ============ VRF Trigger Functions ============
/// @notice Close pool and request VRF (called by factory for batch trigger)
/// @dev Only the factory can call this (triggered by admin/scoreAdmin via triggerVRFForAllPools)
function closePoolAndRequestVRFFromFactory() external onlyFactory inState(PoolState.OPEN) {
if (squaresSold == 0) revert NoSquaresSold();
if (vrfRequested) revert VRFAlreadyRequested();
_closeAndRequestVRF();
}
/// @notice Internal function to close pool and request VRF
function _closeAndRequestVRF() internal {
state = PoolState.CLOSED;
emit PoolClosed(block.timestamp);
// Request VRF randomness with native payment
vrfRequestId = vrfCoordinator.requestRandomWords(
VRFV2PlusClient.RandomWordsRequest({
keyHash: vrfKeyHash,
subId: vrfSubscriptionId,
requestConfirmations: VRF_REQUEST_CONFIRMATIONS,
callbackGasLimit: VRF_CALLBACK_GAS_LIMIT,
numWords: VRF_NUM_WORDS,
extraArgs: VRFV2PlusClient._argsToBytes(
VRFV2PlusClient.ExtraArgsV1({nativePayment: true})
)
})
);
vrfRequested = true;
emit VRFRequested(vrfRequestId);
}
// ============ Chainlink VRF Callback ============
/// @notice VRF callback to assign random numbers
/// @param requestId The VRF request ID
/// @param randomWords The random words from VRF
function fulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) internal override {
if (requestId != vrfRequestId) return;
if (state != PoolState.CLOSED) return;
uint256 randomness = randomWords[0];
// Use randomness for row/column assignment
rowNumbers = SquaresLib.fisherYatesShuffle(randomness);
colNumbers = SquaresLib.fisherYatesShuffle(uint256(keccak256(abi.encodePacked(randomness, uint256(1)))));
numbersSet = true;
state = PoolState.NUMBERS_ASSIGNED;
emit NumbersAssigned(rowNumbers, colNumbers);
}
// ============ Score Submission (Factory Only) ============
/// @notice Submit score from factory (admin score submission)
/// @param quarter The quarter to submit score for (0=Q1, 1=Q2, 2=Q3, 3=Final)
/// @param teamAScore Team A's score
/// @param teamBScore Team B's score
function submitScoreFromFactory(
uint8 quarter,
uint8 teamAScore,
uint8 teamBScore
) external onlyFactory {
// Validate state - must have numbers assigned
if (state < PoolState.NUMBERS_ASSIGNED) revert InvalidState(state, PoolState.NUMBERS_ASSIGNED);
// Validate quarter progression
if (quarter == 0 && state != PoolState.NUMBERS_ASSIGNED) revert InvalidQuarterProgression();
if (quarter == 1 && state != PoolState.Q1_SCORED) revert InvalidQuarterProgression();
if (quarter == 2 && state != PoolState.Q2_SCORED) revert InvalidQuarterProgression();
if (quarter == 3 && state != PoolState.Q3_SCORED) revert InvalidQuarterProgression();
Quarter q = Quarter(quarter);
// Store score
scores[q] = Score({
teamAScore: teamAScore,
teamBScore: teamBScore,
submitted: true,
settled: true,
requestId: bytes32(0)
});
// Update state
if (quarter == 0) state = PoolState.Q1_SCORED;
else if (quarter == 1) state = PoolState.Q2_SCORED;
else if (quarter == 2) state = PoolState.Q3_SCORED;
else if (quarter == 3) state = PoolState.FINAL_SCORED;
// Auto-payout the winner
_settleQuarter(q);
emit ScoreSubmitted(q, teamAScore, teamBScore, bytes32(0));
}
/// @notice Operator can manually submit scores (fallback)
function submitScore(Quarter quarter, uint8 teamAScore, uint8 teamBScore) external {
// Only operator can manually submit (fallback if APIs fail)
if (msg.sender != operator) revert OnlyOperator();
_validateQuarterProgression(quarter);
Score storage score = scores[quarter];
score.teamAScore = teamAScore;
score.teamBScore = teamBScore;
score.submitted = true;
score.settled = true;
_advanceState(quarter);
// Auto-payout or roll forward unclaimed winnings
_settleQuarter(quarter);
emit ScoreSubmitted(quarter, teamAScore, teamBScore, bytes32(0));
}
// ============ Internal Functions ============
function _validateQuarterProgression(Quarter quarter) internal view {
if (quarter == Quarter.Q1 && state != PoolState.NUMBERS_ASSIGNED) {
revert InvalidState(state, PoolState.NUMBERS_ASSIGNED);
}
if (quarter == Quarter.Q2 && state != PoolState.Q1_SCORED) {
revert InvalidState(state, PoolState.Q1_SCORED);
}
if (quarter == Quarter.Q3 && state != PoolState.Q2_SCORED) {
revert InvalidState(state, PoolState.Q2_SCORED);
}
if (quarter == Quarter.FINAL && state != PoolState.Q3_SCORED) {
revert InvalidState(state, PoolState.Q3_SCORED);
}
}
function _advanceState(Quarter quarter) internal {
if (quarter == Quarter.Q1) state = PoolState.Q1_SCORED;
else if (quarter == Quarter.Q2) state = PoolState.Q2_SCORED;
else if (quarter == Quarter.Q3) state = PoolState.Q3_SCORED;
else state = PoolState.FINAL_SCORED;
}
function _settleQuarter(Quarter quarter) internal {
(address winner, uint256 payout) = getWinner(quarter);
// Add any rolled amount from previous quarters
uint256 effectivePayout = payout + unclaimedRolledAmount;
if (winner != address(0) && effectivePayout > 0) {
// Winner exists - pay them base payout + any rolled amount
payoutClaimed[winner][quarter] = true;
unclaimedRolledAmount = 0;
// Determine actual payout amount
uint256 actualPayout = effectivePayout;
// Withdraw from Aave if configured (may return less due to rounding)
if (address(aavePool) != address(0)) {
actualPayout = _withdrawFromAave(effectivePayout);
}
// Only transfer if we have funds
if (actualPayout > 0) {
if (paymentToken == address(0)) {
(bool success,) = winner.call{value: actualPayout}("");
if (!success) revert TransferFailed();
} else {
bool success = IERC20(paymentToken).transfer(winner, actualPayout);
if (!success) revert TransferFailed();
}
}
emit PayoutClaimed(winner, quarter, actualPayout);
} else if (payout > 0) {
// No winner - roll this quarter's payout forward
unclaimedRolledAmount += payout;
emit UnclaimedWinningsRolled(quarter, payout, unclaimedRolledAmount);
}
// After FINAL, any remaining rolled amount gets auto-distributed to all square owners
if (quarter == Quarter.FINAL && unclaimedRolledAmount > 0) {
uint256 totalToDistribute = unclaimedRolledAmount;
finalDistributionPool = totalToDistribute;
finalDistributionCalculated = true;
unclaimedRolledAmount = 0;
// Withdraw from Aave if configured (may return less due to rounding)
uint256 actualDistribute = totalToDistribute;
if (address(aavePool) != address(0)) {
actualDistribute = _withdrawFromAave(totalToDistribute);
}
emit FinalDistributionCalculated(actualDistribute, squaresSold);
// Auto-distribute to all square owners (use actual withdrawn amount)
if (actualDistribute > 0) {
for (uint256 i = 0; i < 100; i++) {
address owner = grid[i];
if (owner != address(0) && !finalDistributionClaimed[owner]) {
uint256 ownerSquares = userSquareCount[owner];
uint256 ownerShare = (actualDistribute * ownerSquares) / squaresSold;
bool success;
if (paymentToken == address(0)) {
(success,) = owner.call{value: ownerShare}("");
} else {
success = IERC20(paymentToken).transfer(owner, ownerShare);
}
// Only mark as claimed if transfer succeeded
if (success) {
finalDistributionClaimed[owner] = true;
emit FinalDistributionClaimed(owner, ownerShare, ownerSquares);
}
// If transfer fails, user can still claim later via claimFinalDistribution
}
}
}
}
emit ScoreSettled(quarter, winner, payout);
}
/// @notice Deposit funds to Aave
function _depositToAave(uint256 amount) internal {
if (paymentToken == address(0)) {
// ETH: deposit via WETHGateway
wethGateway.depositETH{value: amount}(address(aavePool), address(this), 0);
} else {
// ERC20 (USDC): approve and supply to Aave
IERC20(paymentToken).approve(address(aavePool), amount);
aavePool.supply(paymentToken, amount, address(this), 0);
}
totalPrincipalDeposited += amount;
emit DepositedToAave(amount);
}
/// @notice Withdraw funds from Aave
/// @dev Uses min(amount, aTokenBalance) to handle Aave's interest rounding
/// @return actualAmount The actual amount withdrawn (may be less than requested)
function _withdrawFromAave(uint256 amount) internal returns (uint256 actualAmount) {
// Get actual aToken balance to handle Aave's interest rounding
uint256 aTokenBalance = IAToken(aToken).balanceOf(address(this));
actualAmount = amount < aTokenBalance ? amount : aTokenBalance;
if (actualAmount == 0) return 0;
if (paymentToken == address(0)) {
// For ETH, we need to approve the aWETH to the gateway first
IAToken(aToken).approve(address(wethGateway), actualAmount);
wethGateway.withdrawETH(address(aavePool), actualAmount, address(this));
} else {
aavePool.withdraw(paymentToken, actualAmount, address(this));
}
// Update principal tracking, capped to avoid underflow
if (actualAmount >= totalPrincipalDeposited) {
totalPrincipalDeposited = 0;
} else {
totalPrincipalDeposited -= actualAmount;
}
emit WithdrawnFromAave(actualAmount);
}
// ============ Admin Functions ============
/// @notice Withdraw accrued yield (admin only, after game is finished)
function withdrawYield() external {
// Only factory admin can withdraw yield
require(msg.sender == ISquaresFactory(factory).admin(), "Only admin");
_withdrawYieldToAdmin();
}
/// @notice Withdraw accrued yield (called by factory for batch withdrawal)
function withdrawYieldFromFactory() external onlyFactory {
_withdrawYieldToAdmin();
}
/// @notice Emergency function to recover stuck ERC20 tokens (admin only)
/// @dev Used to recover aTokens when wrong aToken address was configured
/// @param token The ERC20 token to recover
/// @param amount Amount to recover (use type(uint256).max for full balance)
function emergencyRecoverToken(address token, uint256 amount) external {
require(msg.sender == ISquaresFactory(factory).admin(), "Only admin");
require(state == PoolState.FINAL_SCORED, "Game not finished");
uint256 balance = IERC20(token).balanceOf(address(this));
uint256 toRecover = amount == type(uint256).max ? balance : amount;
require(toRecover <= balance, "Insufficient balance");
address admin = ISquaresFactory(factory).admin();
bool success = IERC20(token).transfer(admin, toRecover);
require(success, "Transfer failed");
}
/// @notice Update aToken address (admin only)
/// @dev Used to fix misconfigured aToken addresses
function setAToken(address _aToken) external {
require(msg.sender == ISquaresFactory(factory).admin(), "Only admin");
address oldAToken = aToken;
aToken = _aToken;
emit ATokenUpdated(oldAToken, _aToken);
}
/// @notice Emergency function to manually assign numbers if VRF fails (admin only)
/// @dev Only callable when pool is CLOSED and VRF was requested but never fulfilled
/// @param rows Array of 10 unique digits (0-9) for row numbers
/// @param cols Array of 10 unique digits (0-9) for column numbers
function emergencySetNumbers(uint8[10] calldata rows, uint8[10] calldata cols) external {
require(msg.sender == ISquaresFactory(factory).admin() || msg.sender == factory, "Only admin or factory");
require(state == PoolState.CLOSED, "Pool must be CLOSED");
require(vrfRequested, "VRF must have been requested");
// Validate rows - must contain exactly digits 0-9 with no duplicates
uint256 rowBitmap;
for (uint8 i = 0; i < 10; i++) {
require(rows[i] < 10, "Row digit must be 0-9");
uint256 bit = 1 << rows[i];
require(rowBitmap & bit == 0, "Duplicate row digit");
rowBitmap |= bit;
}
// Validate cols - must contain exactly digits 0-9 with no duplicates
uint256 colBitmap;
for (uint8 i = 0; i < 10; i++) {
require(cols[i] < 10, "Col digit must be 0-9");
uint256 bit = 1 << cols[i];
require(colBitmap & bit == 0, "Duplicate col digit");
colBitmap |= bit;
}
// Assign the numbers
rowNumbers = rows;
colNumbers = cols;
numbersSet = true;
state = PoolState.NUMBERS_ASSIGNED;
emit EmergencyNumbersAssigned(rows, cols);
}
/// @notice Internal yield withdrawal implementation
function _withdrawYieldToAdmin() internal {
address admin = ISquaresFactory(factory).admin();
// Game must be finished
if (state != PoolState.FINAL_SCORED) revert GameNotFinished();
// Must have Aave configured
if (address(aavePool) == address(0)) revert AaveNotConfigured();
uint256 aTokenBalance = IAToken(aToken).balanceOf(address(this));
if (aTokenBalance == 0) revert NoYieldToWithdraw();
// Withdraw all remaining aTokens (yield) to admin
if (paymentToken == address(0)) {
IAToken(aToken).approve(address(wethGateway), aTokenBalance);
wethGateway.withdrawETH(address(aavePool), aTokenBalance, admin);
} else {
aavePool.withdraw(paymentToken, aTokenBalance, admin);
}
emit YieldWithdrawn(admin, aTokenBalance);
}
// ============ View Functions ============
function getGrid() external view returns (address[100] memory) {
return grid;
}
function getNumbers() external view returns (uint8[10] memory rows, uint8[10] memory cols) {
return (rowNumbers, colNumbers);
}
function getWinner(Quarter quarter) public view returns (address winner, uint256 payout) {
Score storage score = scores[quarter];
if (!score.settled) return (address(0), 0);
uint8 winningPosition = SquaresLib.getWinningPosition(
score.teamAScore,
score.teamBScore,
rowNumbers,
colNumbers
);
winner = grid[winningPosition];
payout = SquaresLib.calculatePayout(totalPot, payoutPercentages[uint8(quarter)]);
}
function getPoolInfo()
external
view
returns (
string memory _name,
PoolState _state,
uint256 _squarePrice,
address _paymentToken,
uint256 _totalPot,
uint256 _squaresSold,
string memory _teamAName,
string memory _teamBName
)
{
return (name, state, squarePrice, paymentToken, totalPot, squaresSold, teamAName, teamBName);
}
function getScore(Quarter quarter) external view returns (Score memory) {
return scores[quarter];
}
function getPayoutPercentages() external view returns (uint8[4] memory) {
return payoutPercentages;
}
function hasClaimed(address user, Quarter quarter) external view returns (bool) {
return payoutClaimed[user][quarter];
}
function isPrivate() external view returns (bool) {
return passwordHash != bytes32(0);
}
function getVRFStatus() external view returns (
uint256 _vrfTriggerTime,
bool _vrfRequested,
uint256 _vrfRequestId,
bool _numbersAssigned
) {
return (vrfTriggerTime, vrfRequested, vrfRequestId, numbersSet);
}
/// @notice Get user's share of final distribution
function getFinalDistributionShare(address user) external view returns (uint256 share, bool claimed) {
claimed = finalDistributionClaimed[user];
if (!finalDistributionCalculated || finalDistributionPool == 0) return (0, claimed);
uint256 userSquares = userSquareCount[user];
if (userSquares == 0) return (0, claimed);
share = (finalDistributionPool * userSquares) / squaresSold;
}
/// @notice Get unclaimed winnings info
function getUnclaimedInfo() external view returns (
uint256 rolledAmount,
uint256 distributionPool,
bool distributionReady
) {
rolledAmount = unclaimedRolledAmount;
distributionPool = finalDistributionPool;
distributionReady = finalDistributionCalculated && finalDistributionPool > 0;
}
/// @notice Get Aave yield info
function getYieldInfo() external view returns (
uint256 principal,
uint256 aTokenBalance,
uint256 yield,
bool aaveConfigured
) {
principal = totalPrincipalDeposited;
aaveConfigured = address(aavePool) != address(0);
if (aaveConfigured && aToken != address(0)) {
aTokenBalance = IAToken(aToken).balanceOf(address(this));
yield = aTokenBalance > principal ? aTokenBalance - principal : 0;
}
}
// ============ Receive ETH ============
receive() external payable {}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
interface ISquaresPool {
// Enums
enum PoolState {
OPEN, // Squares can be purchased
CLOSED, // No more purchases, awaiting VRF randomness
NUMBERS_ASSIGNED, // VRF fulfilled, game in progress
Q1_SCORED, // Quarter 1 score settled
Q2_SCORED, // Quarter 2 score settled
Q3_SCORED, // Quarter 3 score settled
FINAL_SCORED // Final score settled, game complete
}
enum Quarter {
Q1,
Q2,
Q3,
FINAL
}
// Structs
struct PoolParams {
string name;
uint256 squarePrice;
address paymentToken; // address(0) for ETH
uint8 maxSquaresPerUser; // 0 = unlimited
uint8[4] payoutPercentages; // [Q1, Q2, Q3, Final] must sum to 100
string teamAName;
string teamBName;
uint256 purchaseDeadline;
uint256 vrfTriggerTime; // When VRF should be triggered
bytes32 passwordHash; // keccak256(password) for private pools, bytes32(0) for public
}
struct Score {
uint8 teamAScore;
uint8 teamBScore;
bool submitted;
bool settled;
bytes32 requestId; // Reserved for compatibility
}
// Events
event SquarePurchased(address indexed buyer, uint8 indexed position, uint256 price);
event PoolClosed(uint256 timestamp);
event VRFRequested(uint256 requestId);
event NumbersAssigned(uint8[10] rowNumbers, uint8[10] colNumbers);
event ScoreSubmitted(Quarter indexed quarter, uint8 teamAScore, uint8 teamBScore, bytes32 requestId);
event ScoreSettled(Quarter indexed quarter, address winner, uint256 payout);
event PayoutClaimed(address indexed winner, Quarter indexed quarter, uint256 amount);
event UnclaimedWinningsRolled(Quarter indexed quarter, uint256 amount, uint256 newTotalRolled);
event FinalDistributionCalculated(uint256 totalAmount, uint256 squaresSold);
event FinalDistributionClaimed(address indexed user, uint256 amount, uint256 squaresOwned);
event YieldWithdrawn(address indexed admin, uint256 amount);
event DepositedToAave(uint256 amount);
event WithdrawnFromAave(uint256 amount);
event ATokenUpdated(address indexed oldAToken, address indexed newAToken);
event EmergencyNumbersAssigned(uint8[10] rowNumbers, uint8[10] colNumbers);
// Player functions
function buySquares(uint8[] calldata positions, string calldata password) external payable;
function claimPayout(Quarter quarter) external;
// VRF functions
function closePoolAndRequestVRFFromFactory() external;
// Score functions
function submitScore(Quarter quarter, uint8 teamAScore, uint8 teamBScore) external;
function submitScoreFromFactory(uint8 quarter, uint8 teamAScore, uint8 teamBScore) external;
// View functions
function getGrid() external view returns (address[100] memory);
function getNumbers() external view returns (uint8[10] memory rows, uint8[10] memory cols);
function getWinner(Quarter quarter) external view returns (address winner, uint256 payout);
function getPoolInfo() external view returns (
string memory name,
PoolState state,
uint256 squarePrice,
address paymentToken,
uint256 totalPot,
uint256 squaresSold,
string memory teamAName,
string memory teamBName
);
function getScore(Quarter quarter) external view returns (Score memory);
function getVRFStatus() external view returns (
uint256 vrfTriggerTime,
bool vrfRequested,
uint256 vrfRequestId,
bool numbersAssigned
);
function getFinalDistributionShare(address user) external view returns (uint256 share, bool claimed);
function getUnclaimedInfo() external view returns (
uint256 rolledAmount,
uint256 distributionPool,
bool distributionReady
);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/// @title VRFV2PlusClient
/// @notice Library for VRF V2.5 request encoding
library VRFV2PlusClient {
// extraArgs bytes prefix for V1
bytes4 public constant EXTRA_ARGS_V1_TAG = bytes4(keccak256("VRF ExtraArgsV1"));
struct ExtraArgsV1 {
bool nativePayment;
}
struct RandomWordsRequest {
bytes32 keyHash;
uint256 subId;
uint16 requestConfirmations;
uint32 callbackGasLimit;
uint32 numWords;
bytes extraArgs;
}
function _argsToBytes(ExtraArgsV1 memory extraArgs) internal pure returns (bytes memory bts) {
return abi.encodeWithSelector(EXTRA_ARGS_V1_TAG, extraArgs);
}
}
/// @title IVRFCoordinatorV2Plus
/// @notice Interface for VRF Coordinator V2.5
interface IVRFCoordinatorV2Plus {
/// @notice Request random words
/// @param req The request parameters
/// @return requestId The request ID
function requestRandomWords(
VRFV2PlusClient.RandomWordsRequest calldata req
) external returns (uint256 requestId);
/// @notice Add a consumer to a subscription
/// @param subId The subscription ID
/// @param consumer The consumer address to add
function addConsumer(uint256 subId, address consumer) external;
/// @notice Remove a consumer from a subscription
/// @param subId The subscription ID
/// @param consumer The consumer address to remove
function removeConsumer(uint256 subId, address consumer) external;
/// @notice Get subscription details
/// @param subId The subscription ID
/// @return balance The subscription balance
/// @return nativeBalance The native token balance
/// @return reqCount The request count
/// @return owner The subscription owner
/// @return consumers The consumer addresses
function getSubscription(
uint256 subId
)
external
view
returns (
uint96 balance,
uint96 nativeBalance,
uint64 reqCount,
address owner,
address[] memory consumers
);
/// @notice Check if pending request exists for a consumer
function pendingRequestExists(uint256 subId) external view returns (bool);
/// @notice Create a new subscription
/// @return subId The new subscription ID
function createSubscription() external returns (uint256 subId);
/// @notice Fund a subscription with native tokens (ETH)
/// @param subId The subscription ID to fund
function fundSubscriptionWithNative(uint256 subId) external payable;
/// @notice Cancel a subscription and send remaining funds to a recipient
/// @param subId The subscription ID to cancel
/// @param to The address to send remaining funds to
function cancelSubscription(uint256 subId, address to) external;
}
/// @title VRFConsumerBaseV2Plus
/// @notice Abstract base contract for VRF V2.5 consumers
abstract contract VRFConsumerBaseV2Plus {
error OnlyCoordinatorCanFulfill(address have, address want);
IVRFCoordinatorV2Plus public immutable vrfCoordinator;
constructor(address _vrfCoordinator) {
vrfCoordinator = IVRFCoordinatorV2Plus(_vrfCoordinator);
}
/// @notice Callback function for VRF coordinator to call with random words
/// @param requestId The request ID
/// @param randomWords The array of random words
function rawFulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) external {
if (msg.sender != address(vrfCoordinator)) {
revert OnlyCoordinatorCanFulfill(msg.sender, address(vrfCoordinator));
}
fulfillRandomWords(requestId, randomWords);
}
/// @notice Internal function to be overridden by consumer contracts
/// @param requestId The request ID
/// @param randomWords The array of random words
function fulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) internal virtual;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/// @title SquaresLib
/// @notice Library for Super Bowl Squares game utilities
library SquaresLib {
/// @notice Performs Fisher-Yates shuffle to assign random numbers 0-9 to positions
/// @param seed Random seed from VRF
/// @return numbers Array of 10 numbers (0-9) in shuffled order
function fisherYatesShuffle(uint256 seed) internal pure returns (uint8[10] memory numbers) {
// Initialize array with 0-9
for (uint8 i = 0; i < 10; i++) {
numbers[i] = i;
}
// Fisher-Yates shuffle
for (uint256 i = 9; i > 0; i--) {
// Generate random index from 0 to i
uint256 j = uint256(keccak256(abi.encodePacked(seed, i))) % (i + 1);
// Swap numbers[i] and numbers[j]
uint8 temp = numbers[i];
numbers[i] = numbers[uint8(j)];
numbers[uint8(j)] = temp;
}
return numbers;
}
/// @notice Get the winning square position for a given score
/// @param teamAScore Team A's score (full score, will be modulo 10)
/// @param teamBScore Team B's score (full score, will be modulo 10)
/// @param rowNumbers The random row number assignments (Team A)
/// @param colNumbers The random column number assignments (Team B)
/// @return position The winning square position (0-99)
function getWinningPosition(
uint8 teamAScore,
uint8 teamBScore,
uint8[10] memory rowNumbers,
uint8[10] memory colNumbers
) internal pure returns (uint8) {
uint8 teamALastDigit = teamAScore % 10;
uint8 teamBLastDigit = teamBScore % 10;
// Find row index for Team A's last digit
uint8 rowIndex;
for (uint8 i = 0; i < 10; i++) {
if (rowNumbers[i] == teamALastDigit) {
rowIndex = i;
break;
}
}
// Find column index for Team B's last digit
uint8 colIndex;
for (uint8 i = 0; i < 10; i++) {
if (colNumbers[i] == teamBLastDigit) {
colIndex = i;
break;
}
}
// Position = row * 10 + col
return rowIndex * 10 + colIndex;
}
/// @notice Convert position (0-99) to row and column indices
/// @param position Square position
/// @return row Row index (0-9)
/// @return col Column index (0-9)
function positionToCoords(uint8 position) internal pure returns (uint8 row, uint8 col) {
require(position < 100, "Invalid position");
row = position / 10;
col = position % 10;
}
/// @notice Convert row and column to position
/// @param row Row index (0-9)
/// @param col Column index (0-9)
/// @return position Square position (0-99)
function coordsToPosition(uint8 row, uint8 col) internal pure returns (uint8) {
require(row < 10 && col < 10, "Invalid coordinates");
return row * 10 + col;
}
/// @notice Validate payout percentages sum to 100
/// @param percentages Array of 4 percentages [Q1, Q2, Q3, Final]
/// @return valid True if percentages sum to 100
function validatePayoutPercentages(uint8[4] memory percentages) internal pure returns (bool) {
uint16 sum = uint16(percentages[0]) + uint16(percentages[1]) + uint16(percentages[2]) + uint16(percentages[3]);
return sum == 100;
}
/// @notice Calculate payout amount for a quarter
/// @param totalPot Total pot amount
/// @param percentage Percentage for this quarter (0-100)
/// @return payout Amount to pay out
function calculatePayout(uint256 totalPot, uint8 percentage) internal pure returns (uint256) {
return (totalPot * percentage) / 100;
}
/// @notice Build UMA assertion claim string for a score
/// @param poolName Name of the pool
/// @param quarter Quarter number (1-4)
/// @param teamAName Team A name
/// @param teamBName Team B name
/// @param teamAScore Team A score
/// @param teamBScore Team B score
/// @return claim The assertion claim as bytes
function buildScoreClaim(
string memory poolName,
uint8 quarter,
string memory teamAName,
string memory teamBName,
uint8 teamAScore,
uint8 teamBScore
) internal pure returns (bytes memory) {
string memory quarterName;
if (quarter == 1) quarterName = "Q1";
else if (quarter == 2) quarterName = "Q2";
else if (quarter == 3) quarterName = "Q3";
else quarterName = "Final";
return abi.encodePacked(
"SuperBowl Squares Pool '",
poolName,
"' ",
quarterName,
" score: ",
teamAName,
"=",
uint8ToString(teamAScore),
", ",
teamBName,
"=",
uint8ToString(teamBScore)
);
}
/// @notice Convert uint8 to string
function uint8ToString(uint8 value) internal pure returns (string memory) {
if (value == 0) {
return "0";
}
uint8 temp = value;
uint8 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint8(value % 10)));
value /= 10;
}
return string(buffer);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/// @notice Minimal ERC20 interface for payment tokens
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/// @title IAaveV3
/// @notice Interfaces for Aave V3 protocol integration
interface IPool {
/// @notice Supplies an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
/// @param asset The address of the underlying asset to supply
/// @param amount The amount to be supplied
/// @param onBehalfOf The address that will receive the aTokens
/// @param referralCode Code used to register the integrator originating the operation
function supply(address asset, uint256 amount, address onBehalfOf, uint16 referralCode) external;
/// @notice Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned
/// @param asset The address of the underlying asset to withdraw
/// @param amount The underlying amount to be withdrawn (use type(uint256).max to withdraw all)
/// @param to The address that will receive the underlying
/// @return The final amount withdrawn
function withdraw(address asset, uint256 amount, address to) external returns (uint256);
}
interface IWrappedTokenGatewayV3 {
/// @notice Deposits WETH into the reserve, using native ETH. A corresponding amount of the overlying aWETH is minted.
/// @param pool The address of the Pool contract
/// @param onBehalfOf The address that will receive the aWETH
/// @param referralCode Code used to register the integrator originating the operation
function depositETH(address pool, address onBehalfOf, uint16 referralCode) external payable;
/// @notice Withdraws the WETH _reserves of msg.sender, sending the resulting ETH to the recipient
/// @param pool The address of the Pool contract
/// @param amount The amount of aWETH to withdraw and receive native ETH
/// @param to The address that will receive the native ETH
function withdrawETH(address pool, uint256 amount, address to) external;
}
interface IAToken {
/// @notice Returns the scaled balance of the user
function balanceOf(address account) external view returns (uint256);
/// @notice Returns the address of the underlying asset
function UNDERLYING_ASSET_ADDRESS() external view returns (address);
/// @notice Approve spender to transfer tokens
function approve(address spender, uint256 amount) external returns (bool);
/// @notice Transfer tokens to recipient
function transfer(address to, uint256 amount) external returns (bool);
}{
"remappings": [
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"@chainlink/contracts/=lib/chainlink/contracts/",
"forge-std/=lib/forge-std/src/"
],
"optimizer": {
"enabled": true,
"runs": 1
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"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":[{"internalType":"address","name":"_vrfCoordinator","type":"address"},{"internalType":"bytes32","name":"_vrfKeyHash","type":"bytes32"},{"internalType":"uint256","name":"_creationFee","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"sent","type":"uint256"},{"internalType":"uint256","name":"required","type":"uint256"}],"name":"InsufficientCreationFee","type":"error"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"OnlyAdmin","type":"error"},{"inputs":[],"name":"PoolCreationIsPaused","type":"error"},{"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"PoolNameAlreadyExists","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"pool","type":"address"},{"indexed":false,"internalType":"address","name":"gateway","type":"address"},{"indexed":false,"internalType":"address","name":"aWETH","type":"address"},{"indexed":false,"internalType":"address","name":"aUSDC","type":"address"}],"name":"AaveAddressesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldAdmin","type":"address"},{"indexed":true,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newFee","type":"uint256"}],"name":"CreationFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"poolsSet","type":"uint256"}],"name":"EmergencyNumbersSetForAllPools","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"FeesWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pool","type":"address"},{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"uint256","name":"squarePrice","type":"uint256"},{"indexed":false,"internalType":"address","name":"paymentToken","type":"address"}],"name":"PoolCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"paused","type":"bool"}],"name":"PoolCreationPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldAdmin","type":"address"},{"indexed":true,"internalType":"address","name":"newAdmin","type":"address"}],"name":"ScoreAdminUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"quarter","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"teamAScore","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"teamBScore","type":"uint8"}],"name":"ScoreSubmittedToAllPools","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"subscriptionId","type":"uint256"},{"indexed":true,"internalType":"address","name":"consumer","type":"address"}],"name":"VRFConsumerAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"subscriptionId","type":"uint256"},{"indexed":true,"internalType":"address","name":"fundsRecipient","type":"address"}],"name":"VRFSubscriptionCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"subscriptionId","type":"uint256"}],"name":"VRFSubscriptionCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"subscriptionId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"VRFSubscriptionFunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"poolsTriggered","type":"uint256"}],"name":"VRFTriggeredForAllPools","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"poolsWithdrawn","type":"uint256"}],"name":"YieldWithdrawnFromAllPools","type":"event"},{"inputs":[],"name":"aUSDC","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"aWETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"aavePool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"allPools","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"cancelAndWithdrawVRFSubscription","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"squarePrice","type":"uint256"},{"internalType":"address","name":"paymentToken","type":"address"},{"internalType":"uint8","name":"maxSquaresPerUser","type":"uint8"},{"internalType":"uint8[4]","name":"payoutPercentages","type":"uint8[4]"},{"internalType":"string","name":"teamAName","type":"string"},{"internalType":"string","name":"teamBName","type":"string"},{"internalType":"uint256","name":"purchaseDeadline","type":"uint256"},{"internalType":"uint256","name":"vrfTriggerTime","type":"uint256"},{"internalType":"bytes32","name":"passwordHash","type":"bytes32"}],"internalType":"struct ISquaresPool.PoolParams","name":"params","type":"tuple"}],"name":"createPool","outputs":[{"internalType":"address","name":"pool","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"creationFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultVRFKeyHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultVRFSubscriptionId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"emergencySetNumbersForAllPools","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fundVRFSubscription","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"offset","type":"uint256"},{"internalType":"uint256","name":"limit","type":"uint256"}],"name":"getAllPools","outputs":[{"internalType":"address[]","name":"pools","type":"address[]"},{"internalType":"uint256","name":"total","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPoolCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"creator","type":"address"}],"name":"getPoolsByCreator","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolCreationPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"poolNameExists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"poolsByCreator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"scoreAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_pool","type":"address"},{"internalType":"address","name":"_gateway","type":"address"},{"internalType":"address","name":"_aWETH","type":"address"},{"internalType":"address","name":"_aUSDC","type":"address"}],"name":"setAaveAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_fee","type":"uint256"}],"name":"setCreationFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_paused","type":"bool"}],"name":"setPoolCreationPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_scoreAdmin","type":"address"}],"name":"setScoreAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint96","name":"_amount","type":"uint96"}],"name":"setVRFFundingAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"keyHash","type":"bytes32"}],"name":"setVRFKeyHash","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"subscriptionId","type":"uint256"}],"name":"setVRFSubscription","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"quarter","type":"uint8"},{"internalType":"uint8","name":"teamAScore","type":"uint8"},{"internalType":"uint8","name":"teamBScore","type":"uint8"}],"name":"submitScoreToAllPools","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newAdmin","type":"address"}],"name":"transferAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"triggerVRFForAllPools","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"_aavePool","type":"address"},{"internalType":"address","name":"_wethGateway","type":"address"},{"internalType":"address","name":"_aToken","type":"address"}],"name":"updatePoolAaveConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vrfCoordinator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vrfFundingAmount","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"wethGateway","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"withdrawFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawYieldFromAllPools","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
0x60a060408181523462000165576060826200619e80380380916200002482856200018d565b833981010312620001655781516001600160a01b0381169283820362000165578260208201519101519184156200017c576080526004908155600591909155600680546001600160601b031633606081901b6001600160601b03191691909117909155600780546001600160a01b0319169091179055815163288688f960e21b815292602091849182905f905af191821562000172575f9262000134575b508160035551907fe636347194f3a03c799ea1e7db7e51cb4d89391b1091798a8c87e4f2636e4e465f80a2615fd89081620001c6823960805181818161098801528181610c4c01528181610ed8015281816110c3015281816112cb0152818161132d0152818161151801526115610152f35b9091506020813d60201162000169575b8162000153602093836200018d565b81010312620001655751905f620000c2565b5f80fd5b3d915062000144565b50513d5f823e3d90fd5b835163e6c4247b60e01b8152600490fd5b601f909101601f19168101906001600160401b03821190821017620001b157604052565b634e487b7160e01b5f52604160045260245ffdfe60808060405260043610156200001e575b5036156200001c575f80fd5b005b5f905f3560e01c90816303067a6c14620018f55750806311d9c46c14620018d6578063164e68de146200185357806319d625d114620017de5780631da7d7d114620017b4578063291a749e146200178a57806335a512d71462000fc657806341d1de971462000f7e5780634b884f061462000e93578063501c3fa91462000e6b578063559fda331462000e405780636d2a583a1462000e1557806370c8ea691462000d9457806375829def1462000d0d5780637d03d5ff1462000c245780637e9e0cd21462000b3057806381471bd21462000aa45780638dc44b2b1462000a015780638eec5d7014620009e2578063a03e4bc314620009b7578063a3e56fa81462000970578063a7753f091462000929578063b219762714620007e8578063b7d8622514620007be578063c038bedf146200079e578063c5e10eef1462000773578063c84728ef14620006d6578063dce0b4e414620006b6578063dfe44c4314620003e6578063e054921114620003bc578063e12f454214620002e1578063f5a1b8fb1462000282578063f631b0171462000251578063f851a440146200022e5763fcbf570b036200001057346200022b5760403660031901126200022b57620001e76200192c565b6001600160a01b039081168252600160205260408220805460243593908410156200022b57506020926200021b916200196e565b9190546040519260031b1c168152f35b80fd5b50346200022b57806003193601126200022b57602060065460601c604051908152f35b50346200022b5760203660031901126200022b5760ff60406020926004358152600284522054166040519015158152f35b50346200022b5760203660031901126200022b576004356001600160601b03811690819003620002dd576006548060601c3303620002cb576001600160601b0319161760065580f35b604051634755657960e01b8152600490fd5b5080fd5b50346200022b57806003193601126200022b5760065460601c3303620002cb57808154825b8181106200033d57837f6681ef9bb1d7e9f0d5fb6afdf11a8305d120fde4d23a961fd220bcee0108d8ee602085604051908152a180f35b83620003498262001943565b905460039190911b1c6001600160a01b0316803b15620002dd5781809160046040518095819363b8aa053760e01b83525af19182620003a0575b505062000394575b60010162000306565b6001909201916200038b565b620003ab9062001a21565b620003b857845f62000383565b8480fd5b50346200022b5760203660031901126200022b5760065460601c3303620002cb5760043560045580f35b50346200022b57806003193601126200022b5760065460609060601c3303620002cb5781548291825b8281106200044657847fa83a05390e989a35528cd2943a7571e710c21f546a19e2f314c1feda41ddcf80602086604051908152a180f35b620004518162001943565b905460405163c19d93fb60e01b815291602091600391821b1c6001600160a01b0316908284600481855afa89948162000677575b5062000499575b505050506001016200040f565b60078410156200066357600180941480620005ea575b156200048c57620004c08562001943565b9054604080514287820190815244928201929092529290931b1c871b6001600160601b0319168782015260748082018790528152919060a08301906001600160401b03821184831017620005d6578a936200054e92604052519020620005268162001d1c565b9460405190810191825286604082015260408152620005458162001a35565b51902062001d1c565b92813b15620005d25761028462000583918480946200058f604051988996879563afa3a4bb60e01b8752600487019062001cde565b61014485019062001cde565b5af19182620005b6575b5050620005aa575b8080806200048c565b909301926001620005a1565b620005c19062001a21565b620005ce57865f62000599565b8680fd5b8280fd5b634e487b7160e01b5f52604160045260245ffd5b506040516345366c4360e11b81528381600481865afa90811562000658578a9162000617575b50620004af565b90508381813d831162000650575b62000631818362001a51565b810103126200064c575180151581036200064c575f62000610565b8980fd5b503d62000625565b6040513d8c823e3d90fd5b634e487b7160e01b89526021600452602489fd5b9094508381813d8311620006ae575b62000692818362001a51565b810103126200064c575160078110156200064c57935f62000485565b503d62000686565b50346200022b57806003193601126200022b576020600554604051908152f35b50346200022b57602080600319360112620002dd576001600160a01b039182620006ff6200192c565b168152600192600183526040822060405194858693868454928381520193865286862095905b87838310620007585786906200073e8288038362001a51565b62000754604051928284938452830190620019d4565b0390f35b87548216865296840196899650909401939083019062000725565b50346200022b57806003193601126200022b576009546040516001600160a01b039091168152602090f35b50346200022b57806003193601126200022b576020600354604051908152f35b50346200022b5760203660031901126200022b5760065460601c3303620002cb5760043560055580f35b50346200022b5760603660031901126200022b5760043560ff81168091036200092557602480359060ff821680920362000925576044906044359160ff831680930362000925576007546001600160a01b03929083163314158062000915575b62000904578691825493835b858110620008915784897f4b1986dcbcb78b682e65a229e18c43d8e4a2080518b306cdb8f85b2ee1b14ba160408b8b82519182526020820152a280f35b816200089d8262001943565b90549060031b1c16803b15620009005787868a60648d8380966040519687958694637f92f43160e11b865260048601528d8501528b8401525af1620008e8575b505060010162000854565b620008f39062001a21565b620003b857845f620008dd565b8580fd5b6040516282b42960e81b8152600490fd5b5060065460601c33141562000848565b5f80fd5b50346200022b5760403660031901126200022b57620009666200095160243560043562001be8565b604051928392604084526040840190620019d4565b9060208301520390f35b50346200022b57806003193601126200022b576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b50346200022b57806003193601126200022b576008546040516001600160a01b039091168152602090f35b50346200022b57806003193601126200022b5760209054604051908152f35b50346200022b5762000a133662001984565b92909160065460601c3303620002cb577f1fea32c5c9d09eaea79b4999b471368cb3f599c79e5b4b7174655bd5ff3cbc1d9360809360018060a01b0380941693808060018060a01b0319958787600854161760085516928386600954161760095516928385600a541617600a55168093600b541617600b55604051938452602084015260408301526060820152a180f35b50346200022b5760203660031901126200022b5762000ac26200192c565b60065460601c3303620002cb576001600160a01b0390811690811562000b1e57816007549182167f05c46174528c76376ba492f660f23858b8a0294bfb60484c50afa4cb1863019d8580a36001600160a01b0319161760075580f35b60405163e6c4247b60e01b8152600490fd5b50346200022b57806003193601126200022b576007546001600160a01b039081163314158062000c14575b62000904578182805492815b84811062000b9e57827fef068e541d93ebd27fb844bdc1349aec687f85bc25f0306bf70767ae71df8f74602086604051908152a180f35b8162000baa8262001943565b90549060031b1c16803b1562000c105783808092600460405180958193635793980560e01b83525af1918262000bf8575b505062000bec575b60010162000b67565b60019093019262000be3565b62000c039062001a21565b62000c1057835f62000bdb565b8380fd5b5060065460601c33141562000b5b565b50806003193601126200022b5760065460601c3303620002cb57341562000cee5760035481907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690813b1562000cea57829060246040518094819363256d573f60e21b8352600483015234905af1801562000cdf5762000cc7575b506003545f8051602062005f838339815191526020604051348152a280f35b62000cd29062001a21565b6200022b57805f62000ca8565b6040513d84823e3d90fd5b5050fd5b6044906040519063273cc57560e01b8252600482015260016024820152fd5b50346200022b5760203660031901126200022b5762000d2b6200192c565b600654908160601c803303620002cb576001600160a01b03821690811562000b1e577ff8ccb027dfcd135e000e9d45e6cc2d662578a8825d4c45b5e32e0adf67e79ec68580a36001600160601b039190911660609190911b6001600160601b0319161760065580f35b50346200022b5762000da63662001984565b60069291925460601c3303620002cb5784936001600160a01b0316803b15620003b85762000def938580946040519687958694859363806b456d60e01b85526004850162001b86565b03925af1801562000cdf5762000e025750f35b62000e0d9062001a21565b6200022b5780f35b50346200022b57806003193601126200022b57600a546040516001600160a01b039091168152602090f35b50346200022b57806003193601126200022b57600b546040516001600160a01b039091168152602090f35b50346200022b57806003193601126200022b57602060ff60075460a01c166040519015158152f35b50346200022b5760203660031901126200022b5762000eb16200192c565b60065460601c3303620002cb576001600160a01b0381811691821562000b1e5760035484927f00000000000000000000000000000000000000000000000000000000000000001691823b1562000c105762000f2592849283604051809681958294622b825560e61b84526004840162001ba9565b03925af1801562000cdf5762000f66575b50506003547f834b5a3c46f0f4ca691c9fe03583b86e9f00d43fcfb141642dd9e1674a5c25908380a38060035580f35b62000f719062001a21565b620002dd57815f62000f36565b50346200022b5760203660031901126200022b576004359080548210156200022b57602062000fad8362001943565b905460405160039290921b1c6001600160a01b03168152f35b506003199060203683011262000925576001600160401b03600435811062000925576101a0809360043536030112620009255760ff60075460a01c1662001778576200101760048035018062001ac5565b620010228162001a75565b9062001032604051928362001a51565b808252602082019236828201116200092557815f92602092863783010152519020805f52600260205260ff60405f2054166200173e575f52600260205260405f20600160ff198254161790556200109760055460018060601b03600654169062001b1a565b908134106200171f576040519061416b80830191821183831017620005d657604091839162001e1883397f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681523360208201520301905ff0928315620016ed576001600160a01b0384163b15620009255762001153604051916315f9796d60e21b8352602060048401526200114060043560040160043560040162001b3c565b909160248501526101c484019162001afa565b600435602481013560448481019190915201356001600160a01b0381168103620009255760018060a01b0316606483015260ff6200119660646004350162001a12565b1660848301528160a48101608460043501905f905b60048210620016f85750505080620011f76200121b5f9462001208620011dd6101046004350160043560040162001b3c565b94909260231995610124948789840301868a015262001afa565b916004350160043560040162001b3c565b9061014494868403018587015262001afa565b90610164906004350135818401526101849060043501358184015260043501356101a483015203818360018060a01b0389165af18015620016ed57620016d7575b50600380546004546001600160a01b0386163b15620003b85760405163c1e4996b60e01b8152600481019290925260248201528381604481836001600160a01b038a165af18015620015e357908491620016bf575b50506008546001600160a01b03168062001606575b5080547f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163b1562000c1057604051632fb1302360e21b81529084908290819062001328906001600160a01b038a16906004840162001ba9565b0381837f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af18015620015e357908491620015ee575b50508054604051906001600160a01b038616817fcf382112f05f221144590c76e493ae0dd402c6d5a5556248c342e32023655dea8780a36006546001600160601b0316908162001516575b50505f54600160401b915081811015620005d657806001620013d792015f5562001943565b81546001600160a01b0388811692861b92831b921b191617905533845260016020526040842080549091811015620005d6576200141a916001820181556200196e565b81546001600160a01b038781169290941b91821b9390911b19169190911790556200144a60048035018062001ac5565b90620014736200145f60446004350162001b71565b916040519360608552606085019162001afa565b6004356024013560208401526001600160a01b0391821660408401523392918616917f0c5a5c56d638e96912bd0138c6b9e4c76e13383df2aa09d18bc936c4966c4b449181900390a3803411620014d9575b6040516001600160a01b0384168152602090f35b8180620014e881933462001bc2565b335af1620014f562001a91565b501562001504575f80620014c5565b6040516312171d8360e31b8152600490fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163b15620009005763256d573f60e21b835260048301528490829060249082907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af18015620015e357908491620015cb575b505080545f8051602062005f83833981519152602060018060601b0360065416604051908152a25f8080620013b2565b620015d69062001a21565b620005d257825f6200159b565b6040513d86823e3d90fd5b620015f99062001a21565b620005d257825f62001367565b6001600160a01b036200161e60043560440162001b71565b16620016ac57600a546001600160a01b0316905b6009546001600160a01b03908116919087163b1562000900579085916200166e604051948593849363806b456d60e01b85526004850162001b86565b0381836001600160a01b038a165af18015620015e35790849162001694575b50620012c6565b6200169f9062001a21565b620005d257825f6200168d565b600b546001600160a01b03169062001632565b620016ca9062001a21565b620005d257825f620012b1565b620016e491925062001a21565b5f905f6200125c565b6040513d5f823e3d90fd5b8293506020809160ff6200170f6001959662001a12565b16815201930191018492620011ab565b60405163273cc57560e01b815234600482015260248101839052604490fd5b6200174e60048035018062001ac5565b620017746040519283926315c2072160e11b845260206004850152602484019162001afa565b0390fd5b604051639556375560e01b8152600490fd5b3462000925575f36600319011262000925576006546040516001600160601b039091168152602090f35b3462000925575f36600319011262000925576007546040516001600160a01b039091168152602090f35b34620009255760203660031901126200092557600435801515809103620009255760065460601c3303620002cb576007805460ff60a01b191660a083901b60ff60a01b161790556040519081527f3387d2b55c2294aa390fd99c3e1429775aa0e41d3da9f40db18bb3668d2e7c3390602090a1005b34620009255760203660031901126200092557620018706200192c565b60065460601c3303620002cb576001600160a01b03811690811562000b1e575f8080804780955af1620018a262001a91565b5015620015045760207fc0819c13be868895eb93e40eaceb96de976442fa1d404e5c55f14bb65a8c489a91604051908152a2005b3462000925575f36600319011262000925576020600454604051908152f35b3462000925576020366003190112620009255760065460601c33036200191d57600435600355005b634755657960e01b8152600490fd5b600435906001600160a01b03821682036200092557565b5f548110156200195a575f805260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b80548210156200195a575f5260205f2001905f90565b608090600319011262000925576001600160a01b03906004358281168103620009255791602435818116810362000925579160443582811681036200092557916064359081168103620009255790565b9081518082526020808093019301915f5b828110620019f4575050505090565b83516001600160a01b031685529381019392810192600101620019e5565b359060ff821682036200092557565b6001600160401b038111620005d657604052565b606081019081106001600160401b03821117620005d657604052565b601f909101601f19168101906001600160401b03821190821017620005d657604052565b6001600160401b038111620005d657601f01601f191660200190565b3d1562001ac0573d9062001aa58262001a75565b9162001ab5604051938462001a51565b82523d5f602084013e565b606090565b903590601e19813603018212156200092557018035906001600160401b03821162000925576020019181360383136200092557565b908060209392818452848401375f828201840152601f01601f1916010190565b9190820180921162001b2857565b634e487b7160e01b5f52601160045260245ffd5b9035601e198236030181121562000925570160208101919035906001600160401b038211620009255781360383136200092557565b356001600160a01b0381168103620009255790565b6001600160a01b0391821681529181166020830152909116604082015260600190565b9081526001600160a01b03909116602082015260400190565b9190820391821162001b2857565b6001600160401b038111620005d65760051b60200190565b915f54918284101562001cb1578262001c02828662001b1a565b111562001c9e575081925b62001c19818562001bc2565b9362001c4262001c298662001bd0565b9562001c39604051978862001a51565b80875262001bd0565b60209590601f190136878301378095835b83811062001c62575050505050565b62001c6d8162001943565b9190548682039085518210156200195a576001938591858060a01b039160031b1c169160051b860101520162001c53565b62001caa908462001b1a565b9262001c0d565b5060405191925090602081016001600160401b03811182821017620005d6576040525f81525f3681379190565b5f915b600a831062001cef57505050565b60019060ff8351168152602080910192019201919062001ce1565b90600a8110156200195a5760051b0190565b6040805190926101408083016001600160401b03811184821017620005d6576040523683375f5b60ff8116600a81101562001d6c5760ff918162001d636001938762001d0a565b52011662001d43565b50509290916009805b62001d805750505090565b82516020810190838252828582015284815262001d9d8162001a35565b5190206001820180831162001b2857801562001e035762001df19060ff80918162001dc9878b62001d0a565b51169406169062001ddb828962001d0a565b511662001de9858962001d0a565b528662001d0a565b52801562001b28575f19018062001d75565b634e487b7160e01b5f52601260045260245ffdfe60e0346200010657601f6200416b38819003918201601f19168301916001600160401b038311848410176200010a57808492604094855283398101031262000106576200005a602062000052836200011e565b92016200011e565b6001600160a01b039091166080523360a05260c052600e805460ff191690556040516140379081620001348239608051818181610f01015281816122440152612a79015260a0518181816102fc0152818161066b015281816108a1015281816108e5015281816109c601528181610a5e015281816115ea0152818161166201528181611a6301528181611c29015281816120cb0152613a25015260c051818181611015015261236f0152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b0382168203620001065756fe608060409080825260049182361015610022575b5050503615610020575f80fd5b005b5f915f3560e01c908163041d443e14612be15750806306fdde0314612bc457806311aa09b114612ba7578063134000c014612b1357806318a7ea5f14612af55780631d821a7a14612ad25780631fe543e314612a4657806324b570a914612a285780632f12313f14612a0a5780633013ce29146129e257806336fa0bcd146129be57806349a046c1146129855780634e108c191461295557806351d2cc8f1461291c578063526022c014612895578063537db1da146123c1578063558ba2131461239e578063570ca7351461235b57806357939805146120ae57806357e5e5b414611be55780635bc5980c14611b295780635d6f2f7914611b0b5780635f09d84c14611a2657806360246c881461198357806360751ea01461193f5780636ec773f61461190457806373c77690146118c95780637902019414611637578063806b456d146115a65780638070bb281461158857806381c426e51461156a57806389f915f6146114f25780638a6cd886146114cf57806396871042146112a55780639c82a93814610fe35780639e19b50614610f7f578063a03e4bc314610f58578063a0c1f15e14610f30578063a3e56fa814610eed578063a88d272414610eb2578063ac62c29a14610e70578063afa3a4bb14610a1b578063b0175cc3146109fd578063b8aa0537146109b1578063b947d44a1461080c578063bafa5e8c14610993578063bd3a4aac14610956578063c19d93fb1461092e578063c1e4996b146108d0578063c45a01551461088d578063c5e10eef14610865578063c69a51a414610847578063d338edf31461080c578063dd4ee59c146107ee578063e3e664cd146106f5578063e507a8a41461063c578063e6a44a891461060a578063ea4365c1146105cc578063ed647d21146105ae578063faff660e1461058e5763ff25e86203610013573461058a57606036600319011261058a5782359060ff90818316809303610586576102f2612f95565b6102fa612fa5565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361057657600e91848354169060078210159081610563576002831061053f5787158080610531575b610521576001891491828481610510575b506104bb5760028a1493848181610500575b506104f05760038b14958691826104cb575b50506104bb578b8a10156104a85794879485948a98948e948e9960018f5f80516020613fe28339815191529f9060609f918e9251946103c186612c5a565b169e8f855261040a81602087019b169d8e8c5286019185835260606103f3829189019588875260808a01978852612fc3565b9751169b60ff199c8d895416178855511686613293565b518454915163ffff00001990921690151560101b62ff0000161790151560181b63ff00000016178355519101551561046457505050600391508254161790555b61045387613609565b81519384526020840152820152a280f35b1561047657505082541617905561044a565b919250901561048d5750815416600517905561044a565b610499575b505061044a565b81541660061790558880610492565b634e487b7160e01b8b5260218c5260248bfd5b875163529c679b60e01b81528c90fd5b9091506104dd57600514155f80610383565b634e487b7160e01b8c5260218d5260248cfd5b885163529c679b60e01b81528d90fd5b90506104dd578c86141581610371565b90506104a85760038514158461035f565b865163529c679b60e01b81528b90fd5b505f9250600284141561034e565b8551633bf2e2f960e11b815260449061055a818d0186612f4a565b60026024820152fd5b634e487b7160e01b895260218a52602489fd5b8251630636a15760e11b81528790fd5b5f80fd5b5080fd5b503461058a578160031936011261058a57602090600f5415159051908152f35b503461058a578160031936011261058a57602090600c549051908152f35b5090346106075760203660031901126106075782359283101561060757506105f661060392613367565b929091519283928361301f565b0390f35b80fd5b509034610607576020366003190112610607575061062e610629612f34565b6132fd565b825191825215156020820152f35b5082346106f157826003193601126106f15781516303e1469160e61b81526001600160a01b03916020908290817f000000000000000000000000000000000000000000000000000000000000000086165afa9081156106e4576106aa935084916106b5575b5016331461317a565b6106b2613a03565b80f35b6106d7915060203d6020116106dd575b6106cf8183612ce3565b81019061315b565b846106a1565b503d6106c5565b50505051903d90823e3d90fd5b8280fd5b509034610607578060031936011261060757600354815490926001600160a01b03918216151592918290819085806107e1575b610747575b505060809550815194855260208501528301526060820152f35b90935060209150600254169560248351809881936370a0823160e01b835230908301525afa9182156107d65780926107a1575b6080955084838181111561079a5761079292506130a8565b915f8061072d565b5050610792565b91506020853d6020116107ce575b816107bc60209383612ce3565b8101031261058657608094519161077a565b3d91506107af565b9051903d90823e3d90fd5b5080600254161515610728565b503461058a578160031936011261058a576020906005549051908152f35b503461058a5760ff61083c6020938361082436612ff0565b6001600160a01b039091168352607d87529120612fda565b541690519015158152f35b503461058a578160031936011261058a57602090607e549051908152f35b503461058a578160031936011261058a5760015490516001600160a01b039091168152602090f35b503461058a578160031936011261058a57517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b508290346106f157806003193601126106f1577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163303610921575035600c55602435600d5580f35b51630636a15760e11b8152fd5b503461058a578160031936011261058a5760209061095460ff600e541691518092612f4a565bf35b503461058a57602036600319011261058a5760209160ff9082906001600160a01b03610980612f34565b1681526081855220541690519015158152f35b503461058a578160031936011261058a576020906078549051908152f35b508290346106f157826003193601126106f1577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361092157826106b2613a03565b503461058a578160031936011261058a57602090600b549051908152f35b50913461058a5761028090816003193601126106f15761014493368511610e6c573661028411610e6c5780516303e1469160e61b81526020906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081169183818781865afa908115610e62578891610e45575b50163314908115610e3b575b5015610e0257600e549560ff92838816976007891015610def576001809903610db65784607a541615610d745787808a5b610cb3575b5050888889915b610bed575b5050858890895b8b87600a8310610bd25750505050607455828890895b8b87600a8310610bb157505050509060029160755560ff19908a8260765416176076551617600e5551938488845f925b600a8410610b935750505050506101408401905f915b600a8310610b7657877fadae328c77fa94227f7d61b017c8da7cd84a3e69885efae46b879b9bae3df9b78888a180f35b83808a9287610b8486612fb5565b16815201920192019190610b46565b819088610b9f87612fb5565b16815201930191019091848a91610b30565b90919293610bc99084610bc3876130e4565b91613147565b93019101610b00565b90919293610be49084610bc3876130e4565b93019101610aea565b868216600a80821015610cac5788610c0c610c07846132bb565b6130e4565b161015610c715787610c21610c078e936132bb565b161b90818116610c3857918b01871691178a610ade565b845162461bcd60e51b8152808a018890526013602482015272111d5c1b1a58d85d194818dbdb08191a59da5d606a1b6044820152606490fd5b845162461bcd60e51b8152808a018890526015602482015274436f6c206469676974206d75737420626520302d3960581b6044820152606490fd5b5050610ae3565b868216600a80821015610d6d5788610ccd610c07846132a9565b161015610d325787610ce2610c078e936132a9565b161b90818116610cf957918b01871691178a610ad2565b845162461bcd60e51b8152808a018890526013602482015272111d5c1b1a58d85d19481c9bddc8191a59da5d606a1b6044820152606490fd5b845162461bcd60e51b8152808a018890526015602482015274526f77206469676974206d75737420626520302d3960581b6044820152606490fd5b5050610ad7565b815162461bcd60e51b8152808701859052601c60248201527b159491881b5d5cdd081a185d99481899595b881c995c5d595cdd195960221b6044820152606490fd5b815162461bcd60e51b81528087018590526013602482015272141bdbdb081b5d5cdd0818994810d313d4d151606a1b6044820152606490fd5b634e487b7160e01b885260218652602488fd5b905162461bcd60e51b81529182015260156024820152744f6e6c792061646d696e206f7220666163746f727960581b6044820152606490fd5b905033145f610aa1565b610e5c9150843d86116106dd576106cf8183612ce3565b5f610a95565b85513d8a823e3d90fd5b8380fd5b503461058a578160031936011261058a57608090600b549060ff607a5416906079549060ff607654169281519485521515602085015283015215156060820152f35b50913461058a57602036600319011261058a573590600a821015610607575060ff60209260f88360051c6074015491519360031b161c168152f35b503461058a578160031936011261058a57517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461058a578160031936011261058a5760025490516001600160a01b039091168152602090f35b503461058a578160031936011261058a57905490516001600160a01b039091168152602090f35b509034610607576020366003190112610607578235928310156106075750610fa860a092612fc3565b60ff60018254920154918351938282168552828260081c166020860152828260101c1615159085015260181c16151560608301526080820152f35b5082346106f15760603660031901126106f15780359181831015610e6c57611009612f95565b91611012612fa5565b907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163303611297578415808061126b575b611249576001861490818061121d575b6111f257600287149586806111da575b6111af576003881480611183575b611158578360ff61108a8a612fc3565b92169760ff199289848254161781556110a38882613293565b805463ffff0000191663010100001790555f93156111145750506003919250600e541617600e555b6110d486613609565b611101575091848260ff5f80516020613fe28339815191529560609551948552166020840152820152a280f35b634e487b7160e01b865260219052602485fd5b9092505f935f1461112d5750600e541617600e556110cb565b5f93509091501561114857600590600e541617600e556110cb565b600690600e541617600e556110cb565b600e548651633bf2e2f960e11b815260449161117a908288019060ff16612f4a565b60056024820152fd5b5060ff600e5416600781101561119c576005141561107a565b634e487b7160e01b8a526021855260248afd5b600e548651633bf2e2f960e11b815260449186906111d3908383019060ff16612f4a565b6024820152fd5b5060ff600e5416600781101561119c5784141561106c565b600e548551633bf2e2f960e11b8152604491611214908287019060ff16612f4a565b60036024820152fd5b5060ff600e54166007811015611236576003141561105c565b634e487b7160e01b895260218452602489fd5b600e548451633bf2e2f960e11b815260449161055a908286019060ff16612f4a565b5060ff600e54166007811015611284576002141561104c565b634e487b7160e01b885260218352602488fd5b82516327e1f1e560e01b8152fd5b5082346106f157602080600319360112610e6c57813592828410156114cb5760ff8085168015806114b4575b61147757600181148061149e575b611477576002811480611487575b611477576003148061144d575b61143d57338652607d835261131185838820612fda565b541661142e5761132084613367565b93906001600160a01b03908116330361141f57338752607d845261134686848920612fda565b805460ff19166001179055600654168061139b57508580808087335af161136b6130b5565b501561138d5750905f80516020613fc2833981519152915b519283523392a380f35b90516312171d8360e31b8152fd5b838351809263a9059cbb60e01b8252818a816113ba8b338a840161301f565b03925af19081156114155787916113e8575b501561138d5750905f80516020613fc283398151915291611383565b6114089150843d861161140e575b6114008183612ce3565b810190613090565b876113cc565b503d6113f6565b83513d89823e3d90fd5b5090516330c6392160e11b8152fd5b5163538d585960e11b81529050fd5b8151631d6c64bd60e21b81528490fd5b5080600e54166007811015611464576006116112fa565b634e487b7160e01b875260218552602487fd5b8251631d6c64bd60e21b81528590fd5b5081600e54166007811015610def576005116112ed565b5081600e54166007811015610def5785116112df565b5081600e54166007811015610def576003116112d1565b8480fd5b503461058a578160031936011261058a5760209060ff607a541690519015158152f35b503461058a578160031936011261058a578061095461028092519161151683612cc7565b610140809336903782815161152a81612cc7565b369037805192611539846131b3565b61154284612cc7565b61156282519261155184613223565b61155a84612cc7565b518095612f6b565b830190612f6b565b503461058a578160031936011261058a57602090607f549051908152f35b503461058a578160031936011261058a576020906003549051908152f35b5082346106f15760603660031901126106f1576115c1612f34565b906001600160a01b036024358181169290839003610586576044359482861680960361058657827f000000000000000000000000000000000000000000000000000000000000000016330361092157505060018060a01b0319921682855416178455816001541617600155600254161760025580f35b508290346106f157806003193601126106f157611652612f34565b602491823560018060a01b0392837f0000000000000000000000000000000000000000000000000000000000000000168351946303e1469160e61b9283875260209687818b81875afa9081156118bf576116b79184918d916118a2575016331461317a565b60ff600e541660078110156118905760060361185a5716918451936370a0823160e01b8552308986015286858981875afa948515611850578a95611821575b505f19810361181b5750835b84116117e25785908886518094819382525afa9081156117d85791611747939186938a916117bb575b5089865180968195829463a9059cbb60e01b84528d840161301f565b03925af19081156117b1578691611794575b5015611763578480f35b5162461bcd60e51b815292830152600f908201526e151c985b9cd9995c8819985a5b1959608a1b6044820152606490fd5b6117ab9150833d851161140e576114008183612ce3565b86611759565b82513d88823e3d90fd5b6117d29150843d86116106dd576106cf8183612ce3565b8a61172b565b84513d8a823e3d90fd5b845162461bcd60e51b815280890187905260148189015273496e73756666696369656e742062616c616e636560601b6044820152606490fd5b93611702565b9094508681813d8311611849575b6118398183612ce3565b810103126105865751938a6116f6565b503d61182f565b86513d8c823e3d90fd5b855162461bcd60e51b8152808a018890526011818a01527011d85b59481b9bdd08199a5b9a5cda1959607a1b6044820152606490fd5b634e487b7160e01b8b5260218a52888bfd5b6118b991508a3d8c116106dd576106cf8183612ce3565b8d6106a1565b87513d8d823e3d90fd5b503461058a57602036600319011261058a5760209160ff9082906001600160a01b036118f3612f34565b168152607c85522054169051908152f35b50913461058a57602036600319011261058a573590600a821015610607575060ff60209260f88360051c6075015491519360031b161c168152f35b503461058a578160031936011261058a57606090607e5490607f5460ff608054169081611979575b82519384526020840152151590820152f35b8015159150611967565b503461058a578160031936011261058a576119ee9061060360ff600e541691611a186005549360018060a01b036006541694607754607854906119c4612d06565b976119cd612e1f565b936119d6612da4565b976119f981519c8d9c8d610100908181520190612e9a565b9760208d0190612f4a565b8a01526060890152608088015260a087015285820360c0870152612e9a565b9083820360e0850152612e9a565b5082346106f15760203660031901126106f157611a41612f34565b82516303e1469160e61b815290926001600160a01b03929091906020908290817f000000000000000000000000000000000000000000000000000000000000000087165afa918215611b025750611aa39183918691611ae3575016331461317a565b600280546001600160a01b03198116938316938417909155167f53fab91b0553fa390c7df2daf18c9f2f21878be4f7669c66f13569b32fb1ae2d8380a380f35b611afc915060203d6020116106dd576106cf8183612ce3565b866106a1565b513d86823e3d90fd5b503461058a578160031936011261058a57602090600a549051908152f35b5082346106f15760203660031901126106f1578035908110156106f157611b759060a09360808451611b5a81612c5a565b82815282602082015282868201528260608201520152612fc3565b90805190611b8282612c5a565b82549060ff82169384845260ff60208501818560081c1681526080600185880194848860101c16151586528460608a019860181c161515885201549601958652835196875251166020860152511515908401525115156060830152516080820152f35b50913461058a576020906003198281360112610e6c578135926001600160401b03918285116120aa576101a08585019186360301126120aa576001600160a01b03967f00000000000000000000000000000000000000000000000000000000000000008816330361209b5760848601815191611c6083612c91565b82610104890193368511612097578684915b86831061207f575061ffff91508260ff6060611cac611ca0611cb695848060649a51169187015116906135f3565b838986015116906135f3565b92015116906135f3565b16036120705750611cc783806130ff565b86819b929b1161205d57808a9b89611ce2819c9d9b54612bfb565b89601f9c8d8311612031575b92505050908d8b8411600114611fcd5792611fc2575b50508160011b915f199060031b1c19161788555b60248901356005556044890135908116809103611fbe576006549060ff60a01b611d4460648c016130e4565b60a01b169160018060a81b03191617176006558890895b85898210611fa357505050600755611d7390826130ff565b90848211611f9057611d86600854612bfb565b868111611f63575b508890868311600114611efb57611dc99392918a9183611ef0575b50508160011b915f199060031b1c1916176008555b6101248701906130ff565b9490928511611edd5750611dde600954612bfb565b838111611ea6575b5085928411600114611e3b5750610184939291859183611e30575b50508160011b915f199060031b1c1916176009555b610144810135600a55610164810135600b550135600f5580f35b013590505f80611e01565b91601f198416600987528387209387905b828210611e8e57505091600193918561018497969410611e75575b505050811b01600955611e16565b01355f19600384901b60f8161c191690555f8080611e67565b80600185978294968801358155019601930190611e4c565b611ece90600988528288208580880160051c820192858910611ed4575b0160051c0190613131565b5f611de6565b92508192611ec3565b634e487b7160e01b875260419052602486fd5b013590505f80611da9565b60088a52848a2091601f1984168b5b87828210611f4d575050916001939185611dc997969410611f34575b505050811b01600855611dbe565b01355f19600384901b60f8161c191690555f8080611f26565b6001849682939587013581550195019201611f0a565b611f8a9060088b52858b208880860160051c820192888710611ed4570160051c0190613131565b5f611d8e565b634e487b7160e01b895260418752602489fd5b611fb5839483610bc3600195966130e4565b93019101611d5b565b8980fd5b013590505f80611d04565b925090601f1984168c8452898420935b8a82821061201b575050908460019594939210612002575b505050811b018855611d18565b01355f19600384901b60f8161c191690555f8080611ff5565b6001849682939587013581550195019201611fdd565b612054938152208c80860160051c8201928c8710611ed4570160051c0190613131565b8a5f898f611cee565b634e487b7160e01b8a526041885260248afd5b51632bfdf23b60e11b81528690fd5b819061208a84612fb5565b8152019101908790611c72565b8a80fd5b51630636a15760e11b81528490fd5b8580fd5b5082346106f157826003193601126106f1576001600160a01b03907f00000000000000000000000000000000000000000000000000000000000000008216330361234d57600e549260ff8416600781101561233a57806123165750607854156123095760ff607a54166122fc57600160ff1980951617600e558051924284527f925a19753e677c9dc36a80e0fc824ca0c5b1afde494872b43daccab9ffeaffd460208095a1600d54600c54835190946001600160401b039392909190878201858111838210176122e95786526001825285519163125fa26760e31b898401525115156024830152602482526121a282612cac565b85519460c08601908111868210176122e9579261224089969594938c9388958a528752848701998a5288870160038152606088016207a120815261ffff60808a01926001845260a08b019485528c519d8e9b8c9a8b99634d8e1c2f60e11b8b528a015251602489015251604488015251166064860152519063ffffffff8092166084860152511660a48401525160c060c484015260e4830190612e9a565b03927f0000000000000000000000000000000000000000000000000000000000000000165af19182156122df5785926122ad575b5060017fd28591fd5b8e6c8a0d976474c82e138053b4b2004d8d12bab9c3e5383ad9706b9483607955607a541617607a5551908152a180f35b9391508284813d83116122d8575b6122c58183612ce3565b8101031261058657925190926001612274565b503d6122bb565b81513d87823e3d90fd5b604184634e487b7160e01b5f525260245ffd5b5163029c583d60e01b8152fd5b516387dc625760e01b8152fd5b60449350612332915192633bf2e2f960e11b8452830190612f4a565b5f6024820152fd5b634e487b7160e01b865260218352602486fd5b8251630636a15760e11b8152fd5b503461058a578160031936011261058a57517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461058a578160031936011261058a5760209060ff6076541690519015158152f35b508281600319360112610586576001600160401b03908035828111610586576123ed9036908301612ed8565b936024928335948086116105865736602387011215610586578582013590811161058657368582880101116105865760ff9586600e5416600781101561288357806128605750600a54421161285057600f549182612809575b50505060059061245887600554613055565b95335f52602095607c875261247582865f205416838b169061307c565b8260065460a01c16801515806127fe575b6127cf5750335f52607c885282865f20911660ff1982541617905560018060a01b039182600654168881155f1461276957505088341061274d5788341161271f575b825f5416806125b3575b508a5b8a81106124fe578b6124f88c6124ed8d6077546130f2565b6077556078546130f2565b60785580f35b61250b81871b89016130e4565b8281169060648083101561259d5781101561258b576010018054868116612575576001600160a01b03191633908117909155875489519081526001939291907fd55d45dbc537b82471506213de5e3d6eb436a9048098dc7378087f719b128b9d908d90a3016124d5565b8951630d1a490360e31b81528089018490528690fd5b634e487b7160e01b8e5260328752848efd5b8951634e6153af60e11b81528089018490528690fd5b888a856006541680155f1461266857505050508a836001541684825416813b156106f1578b91606484928b51948593849263474cf53d60e01b84528c840152308a8401528560448401525af1801561265e5761264a575b50505b612619896003546130f2565b6003557fcfd990c8ea9e3bd9e38863886d1bef1edfff9ef9e09b19806edcae848db500c88887518b8152a18b6124d2565b61265390612c33565b612097578a8c61260a565b88513d84823e3d90fd5b612687935f8b5180968195829463095ea7b360e01b84528d840161301f565b03925af180156126f857612702575b50825f54168360065416813b15610586578a60845f92838b51958694859363617ba03760e01b85528c850152898401523060448401528160648401525af180156126f8576126e5575b5061260d565b6126f0919b50612c33565b5f998b6126df565b87513d5f823e3d90fd5b61271890893d8b1161140e576114008183612ce3565b508b612696565b5f80808061272d8d346130a8565b335af16127386130b5565b506124c85785516312171d8360e31b81528490fd5b855163b99e2ab760e01b815234818601528083018a9052604490fd5b5f9160648c8a5194859384926323b872dd60e01b8452338c850152308a85015260448401525af19081156126f8575f916127b257506124c85785516312171d8360e31b81528490fd5b6127c99150893d8b1161140e576114008183612ce3565b8c612738565b84606491888587607c8e335f5252825f205416915193639d69e24960e01b855233908501528301526044820152fd5b508084831611612486565b5f60206128158361303a565b9261282288519485612ce3565b808452808a83860196018637830101525190200361284257878080612446565b90516303e4e47560e21b8152fd5b835163bec2907f60e01b81528390fd5b835f8861287e604494895194633bf2e2f960e11b8652850190612f4a565b820152fd5b86602185634e487b7160e01b5f52525ffd5b508234610586575f36600319011261058657608082516128b481612c91565b36903781516007549060ff9360ff8316825260ff602093818160081c166020850152818160101c168385015260181c1660608301526128f282612c91565b51935f9190855b85841061290557608087f35b8480600192848651168152019301930192916128f9565b50823461058657602036600319011261058657359060648210156105865760109091015490516001600160a01b03919091168152602090f35b5034610586575f3660031901126105865761060390612972612e1f565b9051918291602083526020830190612e9a565b838234610586576020366003190112610586578135918210156105865760ff6129af602093612f08565b92905490519260031b1c168152f35b5034610586575f3660031901126105865760209060ff60065460a01c169051908152f35b5034610586575f3660031901126105865760065490516001600160a01b039091168152602090f35b5034610586575f36600319011261058657602090600f549051908152f35b5034610586575f366003190112610586576020906077549051908152f35b5082346105865781600319360112610586576024356001600160401b03811161058657612a769036908301612ed8565b917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633819003612ab557506100209350356134be565b6044925084519163073e64fd60e21b835233908301526024820152fd5b5034610586575f3660031901126105865760209060ff6080541690519015158152f35b5034610586575f366003190112610586576020906079549051908152f35b5034610586575f36600319011261058657805190612b3082612c75565b610c80809236903780519060105f835b60648210612b8757505050612b5482612c75565b51905f90825b60648310612b6757505050f35b81516001600160a01b031681526001929092019160209182019101612b5a565b82546001600160a01b031681526001928301929190910190602001612b40565b5034610586575f3660031901126105865761060390612972612da4565b5034610586575f3660031901126105865761060390612972612d06565b34610586575f36600319011261058657602090600d548152f35b90600182811c92168015612c29575b6020831014612c1557565b634e487b7160e01b5f52602260045260245ffd5b91607f1691612c0a565b6001600160401b038111612c4657604052565b634e487b7160e01b5f52604160045260245ffd5b60a081019081106001600160401b03821117612c4657604052565b610c8081019081106001600160401b03821117612c4657604052565b608081019081106001600160401b03821117612c4657604052565b606081019081106001600160401b03821117612c4657604052565b61014081019081106001600160401b03821117612c4657604052565b601f909101601f19168101906001600160401b03821190821017612c4657604052565b604051905f8260045491612d1983612bfb565b808352602093600190818116908115612d845750600114612d45575b5050612d4392500383612ce3565b565b9093915060045f52815f20935f915b818310612d6c575050612d4393508201015f80612d35565b85548884018501529485019487945091830191612d54565b915050612d4394925060ff191682840152151560051b8201015f80612d35565b604051905f8260095491612db783612bfb565b808352602093600190818116908115612d845750600114612de0575050612d4392500383612ce3565b9093915060095f52815f20935f915b818310612e07575050612d4393508201015f80612d35565b85548884018501529485019487945091830191612def565b604051905f8260085491612e3283612bfb565b808352602093600190818116908115612d845750600114612e5b575050612d4392500383612ce3565b9093915060085f52815f20935f915b818310612e82575050612d4393508201015f80612d35565b85548884018501529485019487945091830191612e6a565b91908251928382525f5b848110612ec4575050825f602080949584010152601f8019910116010190565b602081830181015184830182015201612ea4565b9181601f84011215610586578235916001600160401b038311610586576020808501948460051b01011161058657565b906004821015612f2057601f8260051c600701921690565b634e487b7160e01b5f52603260045260245ffd5b600435906001600160a01b038216820361058657565b906007821015612f575752565b634e487b7160e01b5f52602160045260245ffd5b5f915b600a8310612f7b57505050565b60019060ff83511681526020809101920192019190612f6e565b6024359060ff8216820361058657565b6044359060ff8216820361058657565b359060ff8216820361058657565b6004811015612f57575f52607b60205260405f2090565b906004811015612f57575f5260205260405f2090565b6040906003190112610586576004356001600160a01b0381168103610586579060243560048110156105865790565b6001600160a01b039091168152602081019190915260400190565b6001600160401b038111612c4657601f01601f191660200190565b8181029291811591840414171561306857565b634e487b7160e01b5f52601160045260245ffd5b9060ff8091169116019060ff821161306857565b90816020910312610586575180151581036105865790565b9190820391821161306857565b3d156130df573d906130c68261303a565b916130d46040519384612ce3565b82523d5f602084013e565b606090565b3560ff811681036105865790565b9190820180921161306857565b903590601e198136030182121561058657018035906001600160401b0382116105865760200191813603831361058657565b81811061313c575050565b5f8155600101613131565b9060ff809160031b9316831b921b19161790565b9081602091031261058657516001600160a01b03811681036105865790565b1561318157565b60405162461bcd60e51b815260206004820152600a60248201526927b7363c9030b236b4b760b11b6044820152606490fd5b61012060745460ff908181168452818160081c166020850152818160101c166040850152818160181c166060850152818160201c166080850152818160281c1660a0850152818160301c1660c0850152818160381c1660e0850152818160401c1661010085015260481c16910152565b61012060755460ff908181168452818160081c166020850152818160101c166040850152818160181c166060850152818160201c166080850152818160281c1660a0850152818160301c1660c0850152818160381c1660e0850152818160401c1661010085015260481c16910152565b9061ff0082549160081b169061ff001916179055565b600a811015612f205760051b60040190565b600a811015612f205760051b6101440190565b90600a811015612f205760051b0190565b81156132e9570490565b634e487b7160e01b5f52601260045260245ffd5b60018060a01b03165f52608160205260ff60405f20541660ff6080541615801561335d575b61335957607c60205260ff60405f20541680156133545761334861335191607f54613055565b607854906132df565b91565b505f91565b5f91565b50607f5415613322565b9061337182612fc3565b549060ff91828160181c16156134b5576040519161338e836131b3565b61339783612cc7565b6040516133a381613223565b6133ac81612cc7565b600a908386168290068616865f805b828116868110156134a657836133d286928c6132ce565b5116146133e35760010182166133bb565b939495969750505050919390935b5f9486600a815f9560081c160616925b8781168381101561349457886134188692856132ce565b511614613429576001018716613401565b94969550600a938693509150505b1602908282169182036130685761344d9161307c565b6064811015612f2057601001546077546001600160a01b0390911693906004821015612f575760649261348261349093612f08565b90549060031b1c1690613055565b0490565b505050505083600a9194939294613437565b505094959392965050506133f1565b505f9250829150565b929192607954036135ee57600e549060ff9060ff8316946007861015612f575760018096036135e65715612f20573593906134f885613cab565b5f905f5b600a81106135c45750506074556135366040519560209660208101918252600160408201526040815261352e81612cac565b519020613cab565b905f955f5b600a81106135a35750505050506002919260755560ff199060018260765416176076551617600e557fb48ed8f2b67f7a4cf1d83936fb4e55a40b7d5bb2b1fb09fe69e91f29a231aa85610280604051613593816131b3565b6135a06101408201613223565ba1565b90919293826135b88299848789511691613147565b9895019392910161353b565b90919360206135da829996848787511691613147565b930191019693966134fc565b505050509050565b509050565b91909161ffff8080941691160191821161306857565b61361281613367565b90607e805461362181856130f2565b6001600160a01b039184831690811515806139d7575b156139795750805f52607d60205260409161365488845f20612fda565b600160ff198254161790555f855580845f5416613969575b50806138c0575b6004881015612f575760205f80516020613fc283398151915291899451908152a35b6004906004861015612f575760038614806138b6575b6136ea575b505050907f4c68367ddbd34d3d0144e5b73c2842cda488332a1acacf11debff28fd03c346b916136e56040519283928361301f565b0390a2565b82549283607f556001935f60ff199260018460805416176080555580835f54166138a6575b50607892607854916040927fd24fb2570481d3406d54ce976d298a890625e4fb3a3451648580154b4294b53b84805184815260209384820152a181613759575b50505050506136b0565b5f5b60648110613769575061374f565b80848a92601001541680151580613891575b613787575b500161375b565b805f52607c845260ff875f205416876137ab6137a38389613055565b8c54906132df565b60065487908a168e81613821575050505f80808084885af16137cb6130b5565b505b6137da575b505050613780565b7f057130b033aff37ce94779177f5a0aba5e46cc901337064f7734906ff0afab6f92845f5260818852825f20878d825416179055825191825287820152a25f8087816137d2565b86979850955f84969761384a96959495519687958694859363a9059cbb60e01b8552840161301f565b03925af1908115613887579189918e969594935f9161386a575b506137cd565b6138819150883d8a1161140e576114008183612ce3565b5f613864565b89513d5f823e3d90fd5b50805f526081845260ff875f2054161561377b565b6138b09150613d70565b5f61370f565b50825415156136ab565b80846006541680155f146138f557505f808080938a5af16138df6130b5565b506136735782516312171d8360e31b8152600490fd5b61391991602091895f885180968195829463a9059cbb60e01b84526004840161301f565b03925af190811561395f575f9161394057506136735782516312171d8360e31b8152600490fd5b613959915060203d60201161140e576114008183612ce3565b5f6138df565b84513d5f823e3d90fd5b6139739150613d70565b5f61366c565b915050848061398a575b5050613695565b613993916130f2565b8083556004861015612f57577f60fc94fd6b7dad2e0cb4f5562c81be8ce4874e7806a06b53bae21e2a0a21471c604087928151908882526020820152a25f84613983565b50821515613637565b6001600160a01b0391821681526020810192909252909116604082015260600190565b604080516303e1469160e61b8152909160206001600160a01b036004828582817f000000000000000000000000000000000000000000000000000000000000000086165afa948515613ca1575f95613c82575b5060ff600e54166007811015613c6f57600603613c6157815f5416958615613c545760249495968484600254168351978880926370a0823160e01b825230888301525afa958615613b8d575f96613c25575b508515613c1757600654841680613b97575050613ae58486856002541686600154165f865180968195829463095ea7b360e01b84528b840161301f565b03925af18015613b8d57613b70575b50826001541691835f541690833b156105865788935f92838993613b2b87519d8e9687958694630402806960e51b865285016139e0565b03925af1968715613b66575f80516020613fa2833981519152959697613b57575b505b519485521692a2565b613b6090612c33565b5f613b4c565b50513d5f823e3d90fd5b613b8690853d871161140e576114008183612ce3565b505f613af4565b82513d5f823e3d90fd5b95859183975f8b96613bbf859c9d975197889687958694631a4ca37b60e21b865285016139e0565b03925af18015613c0d57613be3575b505f80516020613fa283398151915293613b4e565b8390813d8311613c06575b613bf88183612ce3565b81010312610586575f613bce565b503d613bee565b85513d5f823e3d90fd5b50516325b6fdd160e21b8152fd5b9095508481813d8311613c4d575b613c3d8183612ce3565b810103126105865751945f613aa8565b503d613c33565b51631d3b721560e11b8152fd5b855163b7c3a5b360e01b8152fd5b602182634e487b7160e01b5f525260245ffd5b613c9a919550833d85116106dd576106cf8183612ce3565b935f613a56565b86513d5f823e3d90fd5b60409160405190613cbb82612cc7565b6101403683375f5b60ff8116600a811015613ce85760ff9181613ce0600193876132ce565b520116613cc3565b50509290916009805b613cfb5750505090565b825160208101908382528285820152848152613d1681612cac565b519020600182018083116130685780156132e957613d609060ff809181613d3d878b6132ce565b511694061690613d4d82896132ce565b5116613d5985896132ce565b52866132ce565b528015613068575f190180613cf1565b600254604080516370a0823160e01b81523060048201526001600160a01b03945f94909360209392908490829060249082908b165afa908115613ebe575f91613f54575b5080821015613f4c5750935b8415613f4257600654861680613ec85750613dfc8386886002541689600154165f875180968195829463095ea7b360e01b84526004840161301f565b03925af18015613ebe57613ea1575b506001545f5487169616803b1561058657613e425f979188928551998a80948193630402806960e51b83528c3091600485016139e0565b03925af1958615613b8d575f80516020613f82833981519152949596613e90575b505b6003548690808210613e7e5750506003555b51848152a1565b613e8892506130a8565b600355613e77565b613e9a9150612c33565b5f80613e63565b613eb790843d861161140e576114008183612ce3565b505f613e0b565b83513d5f823e3d90fd5b613ef484915f93969798845416908551948580948193631a4ca37b60e21b83528d3091600485016139e0565b03925af18015613b8d57613f18575b505f80516020613f8283398151915292613e65565b8290813d8311613f3b575b613f2d8183612ce3565b81010312610586575f613f03565b503d613f23565b5050509150505f90565b905093613dc0565b90508381813d8311613f7a575b613f6b8183612ce3565b8101031261058657515f613db4565b503d613f6156fe60a52561bffd64573086f5280c1c9cd1bc6ab49cc478f8ccfe689dacec8677eeaed495d70193dd915b96156dc90595c95aa5cc4f6985a2cd79286232d96ae8e264c7f0ad3eacf9f60d2a7e70eca5b5a740b09c3132541056bf6ca27abb6f516e08a8313d2d7c3eae8e87faafbdf295a58197eb2e3767d54d2ae0cce7c3a48292a26469706673582212205df68c8c12fe9a2467be773dad0051a2c57e46708aa193c4f83eb4ec56f23e5564736f6c63430008180033bfe2b363f53fad720f333b704f83a37d87c0db5ab9059f3f446d13d1c1918429a2646970667358221220d0a1b62510f468f6381c50b2bef1327ba3a663a098fd0967c4e041e7509bfd4a64736f6c63430008180033000000000000000000000000d7f86b4b8cae7d942340ff628f82735b7a20893a8077df514608a09f83e4e8d300645594e5d7234665448ba83f51a50f842bd3d90000000000000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x60808060405260043610156200001e575b5036156200001c575f80fd5b005b5f905f3560e01c90816303067a6c14620018f55750806311d9c46c14620018d6578063164e68de146200185357806319d625d114620017de5780631da7d7d114620017b4578063291a749e146200178a57806335a512d71462000fc657806341d1de971462000f7e5780634b884f061462000e93578063501c3fa91462000e6b578063559fda331462000e405780636d2a583a1462000e1557806370c8ea691462000d9457806375829def1462000d0d5780637d03d5ff1462000c245780637e9e0cd21462000b3057806381471bd21462000aa45780638dc44b2b1462000a015780638eec5d7014620009e2578063a03e4bc314620009b7578063a3e56fa81462000970578063a7753f091462000929578063b219762714620007e8578063b7d8622514620007be578063c038bedf146200079e578063c5e10eef1462000773578063c84728ef14620006d6578063dce0b4e414620006b6578063dfe44c4314620003e6578063e054921114620003bc578063e12f454214620002e1578063f5a1b8fb1462000282578063f631b0171462000251578063f851a440146200022e5763fcbf570b036200001057346200022b5760403660031901126200022b57620001e76200192c565b6001600160a01b039081168252600160205260408220805460243593908410156200022b57506020926200021b916200196e565b9190546040519260031b1c168152f35b80fd5b50346200022b57806003193601126200022b57602060065460601c604051908152f35b50346200022b5760203660031901126200022b5760ff60406020926004358152600284522054166040519015158152f35b50346200022b5760203660031901126200022b576004356001600160601b03811690819003620002dd576006548060601c3303620002cb576001600160601b0319161760065580f35b604051634755657960e01b8152600490fd5b5080fd5b50346200022b57806003193601126200022b5760065460601c3303620002cb57808154825b8181106200033d57837f6681ef9bb1d7e9f0d5fb6afdf11a8305d120fde4d23a961fd220bcee0108d8ee602085604051908152a180f35b83620003498262001943565b905460039190911b1c6001600160a01b0316803b15620002dd5781809160046040518095819363b8aa053760e01b83525af19182620003a0575b505062000394575b60010162000306565b6001909201916200038b565b620003ab9062001a21565b620003b857845f62000383565b8480fd5b50346200022b5760203660031901126200022b5760065460601c3303620002cb5760043560045580f35b50346200022b57806003193601126200022b5760065460609060601c3303620002cb5781548291825b8281106200044657847fa83a05390e989a35528cd2943a7571e710c21f546a19e2f314c1feda41ddcf80602086604051908152a180f35b620004518162001943565b905460405163c19d93fb60e01b815291602091600391821b1c6001600160a01b0316908284600481855afa89948162000677575b5062000499575b505050506001016200040f565b60078410156200066357600180941480620005ea575b156200048c57620004c08562001943565b9054604080514287820190815244928201929092529290931b1c871b6001600160601b0319168782015260748082018790528152919060a08301906001600160401b03821184831017620005d6578a936200054e92604052519020620005268162001d1c565b9460405190810191825286604082015260408152620005458162001a35565b51902062001d1c565b92813b15620005d25761028462000583918480946200058f604051988996879563afa3a4bb60e01b8752600487019062001cde565b61014485019062001cde565b5af19182620005b6575b5050620005aa575b8080806200048c565b909301926001620005a1565b620005c19062001a21565b620005ce57865f62000599565b8680fd5b8280fd5b634e487b7160e01b5f52604160045260245ffd5b506040516345366c4360e11b81528381600481865afa90811562000658578a9162000617575b50620004af565b90508381813d831162000650575b62000631818362001a51565b810103126200064c575180151581036200064c575f62000610565b8980fd5b503d62000625565b6040513d8c823e3d90fd5b634e487b7160e01b89526021600452602489fd5b9094508381813d8311620006ae575b62000692818362001a51565b810103126200064c575160078110156200064c57935f62000485565b503d62000686565b50346200022b57806003193601126200022b576020600554604051908152f35b50346200022b57602080600319360112620002dd576001600160a01b039182620006ff6200192c565b168152600192600183526040822060405194858693868454928381520193865286862095905b87838310620007585786906200073e8288038362001a51565b62000754604051928284938452830190620019d4565b0390f35b87548216865296840196899650909401939083019062000725565b50346200022b57806003193601126200022b576009546040516001600160a01b039091168152602090f35b50346200022b57806003193601126200022b576020600354604051908152f35b50346200022b5760203660031901126200022b5760065460601c3303620002cb5760043560055580f35b50346200022b5760603660031901126200022b5760043560ff81168091036200092557602480359060ff821680920362000925576044906044359160ff831680930362000925576007546001600160a01b03929083163314158062000915575b62000904578691825493835b858110620008915784897f4b1986dcbcb78b682e65a229e18c43d8e4a2080518b306cdb8f85b2ee1b14ba160408b8b82519182526020820152a280f35b816200089d8262001943565b90549060031b1c16803b15620009005787868a60648d8380966040519687958694637f92f43160e11b865260048601528d8501528b8401525af1620008e8575b505060010162000854565b620008f39062001a21565b620003b857845f620008dd565b8580fd5b6040516282b42960e81b8152600490fd5b5060065460601c33141562000848565b5f80fd5b50346200022b5760403660031901126200022b57620009666200095160243560043562001be8565b604051928392604084526040840190620019d4565b9060208301520390f35b50346200022b57806003193601126200022b576040517f000000000000000000000000d7f86b4b8cae7d942340ff628f82735b7a20893a6001600160a01b03168152602090f35b50346200022b57806003193601126200022b576008546040516001600160a01b039091168152602090f35b50346200022b57806003193601126200022b5760209054604051908152f35b50346200022b5762000a133662001984565b92909160065460601c3303620002cb577f1fea32c5c9d09eaea79b4999b471368cb3f599c79e5b4b7174655bd5ff3cbc1d9360809360018060a01b0380941693808060018060a01b0319958787600854161760085516928386600954161760095516928385600a541617600a55168093600b541617600b55604051938452602084015260408301526060820152a180f35b50346200022b5760203660031901126200022b5762000ac26200192c565b60065460601c3303620002cb576001600160a01b0390811690811562000b1e57816007549182167f05c46174528c76376ba492f660f23858b8a0294bfb60484c50afa4cb1863019d8580a36001600160a01b0319161760075580f35b60405163e6c4247b60e01b8152600490fd5b50346200022b57806003193601126200022b576007546001600160a01b039081163314158062000c14575b62000904578182805492815b84811062000b9e57827fef068e541d93ebd27fb844bdc1349aec687f85bc25f0306bf70767ae71df8f74602086604051908152a180f35b8162000baa8262001943565b90549060031b1c16803b1562000c105783808092600460405180958193635793980560e01b83525af1918262000bf8575b505062000bec575b60010162000b67565b60019093019262000be3565b62000c039062001a21565b62000c1057835f62000bdb565b8380fd5b5060065460601c33141562000b5b565b50806003193601126200022b5760065460601c3303620002cb57341562000cee5760035481907f000000000000000000000000d7f86b4b8cae7d942340ff628f82735b7a20893a6001600160a01b031690813b1562000cea57829060246040518094819363256d573f60e21b8352600483015234905af1801562000cdf5762000cc7575b506003545f8051602062005f838339815191526020604051348152a280f35b62000cd29062001a21565b6200022b57805f62000ca8565b6040513d84823e3d90fd5b5050fd5b6044906040519063273cc57560e01b8252600482015260016024820152fd5b50346200022b5760203660031901126200022b5762000d2b6200192c565b600654908160601c803303620002cb576001600160a01b03821690811562000b1e577ff8ccb027dfcd135e000e9d45e6cc2d662578a8825d4c45b5e32e0adf67e79ec68580a36001600160601b039190911660609190911b6001600160601b0319161760065580f35b50346200022b5762000da63662001984565b60069291925460601c3303620002cb5784936001600160a01b0316803b15620003b85762000def938580946040519687958694859363806b456d60e01b85526004850162001b86565b03925af1801562000cdf5762000e025750f35b62000e0d9062001a21565b6200022b5780f35b50346200022b57806003193601126200022b57600a546040516001600160a01b039091168152602090f35b50346200022b57806003193601126200022b57600b546040516001600160a01b039091168152602090f35b50346200022b57806003193601126200022b57602060ff60075460a01c166040519015158152f35b50346200022b5760203660031901126200022b5762000eb16200192c565b60065460601c3303620002cb576001600160a01b0381811691821562000b1e5760035484927f000000000000000000000000d7f86b4b8cae7d942340ff628f82735b7a20893a1691823b1562000c105762000f2592849283604051809681958294622b825560e61b84526004840162001ba9565b03925af1801562000cdf5762000f66575b50506003547f834b5a3c46f0f4ca691c9fe03583b86e9f00d43fcfb141642dd9e1674a5c25908380a38060035580f35b62000f719062001a21565b620002dd57815f62000f36565b50346200022b5760203660031901126200022b576004359080548210156200022b57602062000fad8362001943565b905460405160039290921b1c6001600160a01b03168152f35b506003199060203683011262000925576001600160401b03600435811062000925576101a0809360043536030112620009255760ff60075460a01c1662001778576200101760048035018062001ac5565b620010228162001a75565b9062001032604051928362001a51565b808252602082019236828201116200092557815f92602092863783010152519020805f52600260205260ff60405f2054166200173e575f52600260205260405f20600160ff198254161790556200109760055460018060601b03600654169062001b1a565b908134106200171f576040519061416b80830191821183831017620005d657604091839162001e1883397f000000000000000000000000d7f86b4b8cae7d942340ff628f82735b7a20893a6001600160a01b031681523360208201520301905ff0928315620016ed576001600160a01b0384163b15620009255762001153604051916315f9796d60e21b8352602060048401526200114060043560040160043560040162001b3c565b909160248501526101c484019162001afa565b600435602481013560448481019190915201356001600160a01b0381168103620009255760018060a01b0316606483015260ff6200119660646004350162001a12565b1660848301528160a48101608460043501905f905b60048210620016f85750505080620011f76200121b5f9462001208620011dd6101046004350160043560040162001b3c565b94909260231995610124948789840301868a015262001afa565b916004350160043560040162001b3c565b9061014494868403018587015262001afa565b90610164906004350135818401526101849060043501358184015260043501356101a483015203818360018060a01b0389165af18015620016ed57620016d7575b50600380546004546001600160a01b0386163b15620003b85760405163c1e4996b60e01b8152600481019290925260248201528381604481836001600160a01b038a165af18015620015e357908491620016bf575b50506008546001600160a01b03168062001606575b5080547f000000000000000000000000d7f86b4b8cae7d942340ff628f82735b7a20893a6001600160a01b03163b1562000c1057604051632fb1302360e21b81529084908290819062001328906001600160a01b038a16906004840162001ba9565b0381837f000000000000000000000000d7f86b4b8cae7d942340ff628f82735b7a20893a6001600160a01b03165af18015620015e357908491620015ee575b50508054604051906001600160a01b038616817fcf382112f05f221144590c76e493ae0dd402c6d5a5556248c342e32023655dea8780a36006546001600160601b0316908162001516575b50505f54600160401b915081811015620005d657806001620013d792015f5562001943565b81546001600160a01b0388811692861b92831b921b191617905533845260016020526040842080549091811015620005d6576200141a916001820181556200196e565b81546001600160a01b038781169290941b91821b9390911b19169190911790556200144a60048035018062001ac5565b90620014736200145f60446004350162001b71565b916040519360608552606085019162001afa565b6004356024013560208401526001600160a01b0391821660408401523392918616917f0c5a5c56d638e96912bd0138c6b9e4c76e13383df2aa09d18bc936c4966c4b449181900390a3803411620014d9575b6040516001600160a01b0384168152602090f35b8180620014e881933462001bc2565b335af1620014f562001a91565b501562001504575f80620014c5565b6040516312171d8360e31b8152600490fd5b7f000000000000000000000000d7f86b4b8cae7d942340ff628f82735b7a20893a6001600160a01b03163b15620009005763256d573f60e21b835260048301528490829060249082907f000000000000000000000000d7f86b4b8cae7d942340ff628f82735b7a20893a6001600160a01b03165af18015620015e357908491620015cb575b505080545f8051602062005f83833981519152602060018060601b0360065416604051908152a25f8080620013b2565b620015d69062001a21565b620005d257825f6200159b565b6040513d86823e3d90fd5b620015f99062001a21565b620005d257825f62001367565b6001600160a01b036200161e60043560440162001b71565b16620016ac57600a546001600160a01b0316905b6009546001600160a01b03908116919087163b1562000900579085916200166e604051948593849363806b456d60e01b85526004850162001b86565b0381836001600160a01b038a165af18015620015e35790849162001694575b50620012c6565b6200169f9062001a21565b620005d257825f6200168d565b600b546001600160a01b03169062001632565b620016ca9062001a21565b620005d257825f620012b1565b620016e491925062001a21565b5f905f6200125c565b6040513d5f823e3d90fd5b8293506020809160ff6200170f6001959662001a12565b16815201930191018492620011ab565b60405163273cc57560e01b815234600482015260248101839052604490fd5b6200174e60048035018062001ac5565b620017746040519283926315c2072160e11b845260206004850152602484019162001afa565b0390fd5b604051639556375560e01b8152600490fd5b3462000925575f36600319011262000925576006546040516001600160601b039091168152602090f35b3462000925575f36600319011262000925576007546040516001600160a01b039091168152602090f35b34620009255760203660031901126200092557600435801515809103620009255760065460601c3303620002cb576007805460ff60a01b191660a083901b60ff60a01b161790556040519081527f3387d2b55c2294aa390fd99c3e1429775aa0e41d3da9f40db18bb3668d2e7c3390602090a1005b34620009255760203660031901126200092557620018706200192c565b60065460601c3303620002cb576001600160a01b03811690811562000b1e575f8080804780955af1620018a262001a91565b5015620015045760207fc0819c13be868895eb93e40eaceb96de976442fa1d404e5c55f14bb65a8c489a91604051908152a2005b3462000925575f36600319011262000925576020600454604051908152f35b3462000925576020366003190112620009255760065460601c33036200191d57600435600355005b634755657960e01b8152600490fd5b600435906001600160a01b03821682036200092557565b5f548110156200195a575f805260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b80548210156200195a575f5260205f2001905f90565b608090600319011262000925576001600160a01b03906004358281168103620009255791602435818116810362000925579160443582811681036200092557916064359081168103620009255790565b9081518082526020808093019301915f5b828110620019f4575050505090565b83516001600160a01b031685529381019392810192600101620019e5565b359060ff821682036200092557565b6001600160401b038111620005d657604052565b606081019081106001600160401b03821117620005d657604052565b601f909101601f19168101906001600160401b03821190821017620005d657604052565b6001600160401b038111620005d657601f01601f191660200190565b3d1562001ac0573d9062001aa58262001a75565b9162001ab5604051938462001a51565b82523d5f602084013e565b606090565b903590601e19813603018212156200092557018035906001600160401b03821162000925576020019181360383136200092557565b908060209392818452848401375f828201840152601f01601f1916010190565b9190820180921162001b2857565b634e487b7160e01b5f52601160045260245ffd5b9035601e198236030181121562000925570160208101919035906001600160401b038211620009255781360383136200092557565b356001600160a01b0381168103620009255790565b6001600160a01b0391821681529181166020830152909116604082015260600190565b9081526001600160a01b03909116602082015260400190565b9190820391821162001b2857565b6001600160401b038111620005d65760051b60200190565b915f54918284101562001cb1578262001c02828662001b1a565b111562001c9e575081925b62001c19818562001bc2565b9362001c4262001c298662001bd0565b9562001c39604051978862001a51565b80875262001bd0565b60209590601f190136878301378095835b83811062001c62575050505050565b62001c6d8162001943565b9190548682039085518210156200195a576001938591858060a01b039160031b1c169160051b860101520162001c53565b62001caa908462001b1a565b9262001c0d565b5060405191925090602081016001600160401b03811182821017620005d6576040525f81525f3681379190565b5f915b600a831062001cef57505050565b60019060ff8351168152602080910192019201919062001ce1565b90600a8110156200195a5760051b0190565b6040805190926101408083016001600160401b03811184821017620005d6576040523683375f5b60ff8116600a81101562001d6c5760ff918162001d636001938762001d0a565b52011662001d43565b50509290916009805b62001d805750505090565b82516020810190838252828582015284815262001d9d8162001a35565b5190206001820180831162001b2857801562001e035762001df19060ff80918162001dc9878b62001d0a565b51169406169062001ddb828962001d0a565b511662001de9858962001d0a565b528662001d0a565b52801562001b28575f19018062001d75565b634e487b7160e01b5f52601260045260245ffdfe60e0346200010657601f6200416b38819003918201601f19168301916001600160401b038311848410176200010a57808492604094855283398101031262000106576200005a602062000052836200011e565b92016200011e565b6001600160a01b039091166080523360a05260c052600e805460ff191690556040516140379081620001348239608051818181610f01015281816122440152612a79015260a0518181816102fc0152818161066b015281816108a1015281816108e5015281816109c601528181610a5e015281816115ea0152818161166201528181611a6301528181611c29015281816120cb0152613a25015260c051818181611015015261236f0152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b0382168203620001065756fe608060409080825260049182361015610022575b5050503615610020575f80fd5b005b5f915f3560e01c908163041d443e14612be15750806306fdde0314612bc457806311aa09b114612ba7578063134000c014612b1357806318a7ea5f14612af55780631d821a7a14612ad25780631fe543e314612a4657806324b570a914612a285780632f12313f14612a0a5780633013ce29146129e257806336fa0bcd146129be57806349a046c1146129855780634e108c191461295557806351d2cc8f1461291c578063526022c014612895578063537db1da146123c1578063558ba2131461239e578063570ca7351461235b57806357939805146120ae57806357e5e5b414611be55780635bc5980c14611b295780635d6f2f7914611b0b5780635f09d84c14611a2657806360246c881461198357806360751ea01461193f5780636ec773f61461190457806373c77690146118c95780637902019414611637578063806b456d146115a65780638070bb281461158857806381c426e51461156a57806389f915f6146114f25780638a6cd886146114cf57806396871042146112a55780639c82a93814610fe35780639e19b50614610f7f578063a03e4bc314610f58578063a0c1f15e14610f30578063a3e56fa814610eed578063a88d272414610eb2578063ac62c29a14610e70578063afa3a4bb14610a1b578063b0175cc3146109fd578063b8aa0537146109b1578063b947d44a1461080c578063bafa5e8c14610993578063bd3a4aac14610956578063c19d93fb1461092e578063c1e4996b146108d0578063c45a01551461088d578063c5e10eef14610865578063c69a51a414610847578063d338edf31461080c578063dd4ee59c146107ee578063e3e664cd146106f5578063e507a8a41461063c578063e6a44a891461060a578063ea4365c1146105cc578063ed647d21146105ae578063faff660e1461058e5763ff25e86203610013573461058a57606036600319011261058a5782359060ff90818316809303610586576102f2612f95565b6102fa612fa5565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361057657600e91848354169060078210159081610563576002831061053f5787158080610531575b610521576001891491828481610510575b506104bb5760028a1493848181610500575b506104f05760038b14958691826104cb575b50506104bb578b8a10156104a85794879485948a98948e948e9960018f5f80516020613fe28339815191529f9060609f918e9251946103c186612c5a565b169e8f855261040a81602087019b169d8e8c5286019185835260606103f3829189019588875260808a01978852612fc3565b9751169b60ff199c8d895416178855511686613293565b518454915163ffff00001990921690151560101b62ff0000161790151560181b63ff00000016178355519101551561046457505050600391508254161790555b61045387613609565b81519384526020840152820152a280f35b1561047657505082541617905561044a565b919250901561048d5750815416600517905561044a565b610499575b505061044a565b81541660061790558880610492565b634e487b7160e01b8b5260218c5260248bfd5b875163529c679b60e01b81528c90fd5b9091506104dd57600514155f80610383565b634e487b7160e01b8c5260218d5260248cfd5b885163529c679b60e01b81528d90fd5b90506104dd578c86141581610371565b90506104a85760038514158461035f565b865163529c679b60e01b81528b90fd5b505f9250600284141561034e565b8551633bf2e2f960e11b815260449061055a818d0186612f4a565b60026024820152fd5b634e487b7160e01b895260218a52602489fd5b8251630636a15760e11b81528790fd5b5f80fd5b5080fd5b503461058a578160031936011261058a57602090600f5415159051908152f35b503461058a578160031936011261058a57602090600c549051908152f35b5090346106075760203660031901126106075782359283101561060757506105f661060392613367565b929091519283928361301f565b0390f35b80fd5b509034610607576020366003190112610607575061062e610629612f34565b6132fd565b825191825215156020820152f35b5082346106f157826003193601126106f15781516303e1469160e61b81526001600160a01b03916020908290817f000000000000000000000000000000000000000000000000000000000000000086165afa9081156106e4576106aa935084916106b5575b5016331461317a565b6106b2613a03565b80f35b6106d7915060203d6020116106dd575b6106cf8183612ce3565b81019061315b565b846106a1565b503d6106c5565b50505051903d90823e3d90fd5b8280fd5b509034610607578060031936011261060757600354815490926001600160a01b03918216151592918290819085806107e1575b610747575b505060809550815194855260208501528301526060820152f35b90935060209150600254169560248351809881936370a0823160e01b835230908301525afa9182156107d65780926107a1575b6080955084838181111561079a5761079292506130a8565b915f8061072d565b5050610792565b91506020853d6020116107ce575b816107bc60209383612ce3565b8101031261058657608094519161077a565b3d91506107af565b9051903d90823e3d90fd5b5080600254161515610728565b503461058a578160031936011261058a576020906005549051908152f35b503461058a5760ff61083c6020938361082436612ff0565b6001600160a01b039091168352607d87529120612fda565b541690519015158152f35b503461058a578160031936011261058a57602090607e549051908152f35b503461058a578160031936011261058a5760015490516001600160a01b039091168152602090f35b503461058a578160031936011261058a57517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b508290346106f157806003193601126106f1577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163303610921575035600c55602435600d5580f35b51630636a15760e11b8152fd5b503461058a578160031936011261058a5760209061095460ff600e541691518092612f4a565bf35b503461058a57602036600319011261058a5760209160ff9082906001600160a01b03610980612f34565b1681526081855220541690519015158152f35b503461058a578160031936011261058a576020906078549051908152f35b508290346106f157826003193601126106f1577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361092157826106b2613a03565b503461058a578160031936011261058a57602090600b549051908152f35b50913461058a5761028090816003193601126106f15761014493368511610e6c573661028411610e6c5780516303e1469160e61b81526020906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081169183818781865afa908115610e62578891610e45575b50163314908115610e3b575b5015610e0257600e549560ff92838816976007891015610def576001809903610db65784607a541615610d745787808a5b610cb3575b5050888889915b610bed575b5050858890895b8b87600a8310610bd25750505050607455828890895b8b87600a8310610bb157505050509060029160755560ff19908a8260765416176076551617600e5551938488845f925b600a8410610b935750505050506101408401905f915b600a8310610b7657877fadae328c77fa94227f7d61b017c8da7cd84a3e69885efae46b879b9bae3df9b78888a180f35b83808a9287610b8486612fb5565b16815201920192019190610b46565b819088610b9f87612fb5565b16815201930191019091848a91610b30565b90919293610bc99084610bc3876130e4565b91613147565b93019101610b00565b90919293610be49084610bc3876130e4565b93019101610aea565b868216600a80821015610cac5788610c0c610c07846132bb565b6130e4565b161015610c715787610c21610c078e936132bb565b161b90818116610c3857918b01871691178a610ade565b845162461bcd60e51b8152808a018890526013602482015272111d5c1b1a58d85d194818dbdb08191a59da5d606a1b6044820152606490fd5b845162461bcd60e51b8152808a018890526015602482015274436f6c206469676974206d75737420626520302d3960581b6044820152606490fd5b5050610ae3565b868216600a80821015610d6d5788610ccd610c07846132a9565b161015610d325787610ce2610c078e936132a9565b161b90818116610cf957918b01871691178a610ad2565b845162461bcd60e51b8152808a018890526013602482015272111d5c1b1a58d85d19481c9bddc8191a59da5d606a1b6044820152606490fd5b845162461bcd60e51b8152808a018890526015602482015274526f77206469676974206d75737420626520302d3960581b6044820152606490fd5b5050610ad7565b815162461bcd60e51b8152808701859052601c60248201527b159491881b5d5cdd081a185d99481899595b881c995c5d595cdd195960221b6044820152606490fd5b815162461bcd60e51b81528087018590526013602482015272141bdbdb081b5d5cdd0818994810d313d4d151606a1b6044820152606490fd5b634e487b7160e01b885260218652602488fd5b905162461bcd60e51b81529182015260156024820152744f6e6c792061646d696e206f7220666163746f727960581b6044820152606490fd5b905033145f610aa1565b610e5c9150843d86116106dd576106cf8183612ce3565b5f610a95565b85513d8a823e3d90fd5b8380fd5b503461058a578160031936011261058a57608090600b549060ff607a5416906079549060ff607654169281519485521515602085015283015215156060820152f35b50913461058a57602036600319011261058a573590600a821015610607575060ff60209260f88360051c6074015491519360031b161c168152f35b503461058a578160031936011261058a57517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461058a578160031936011261058a5760025490516001600160a01b039091168152602090f35b503461058a578160031936011261058a57905490516001600160a01b039091168152602090f35b509034610607576020366003190112610607578235928310156106075750610fa860a092612fc3565b60ff60018254920154918351938282168552828260081c166020860152828260101c1615159085015260181c16151560608301526080820152f35b5082346106f15760603660031901126106f15780359181831015610e6c57611009612f95565b91611012612fa5565b907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163303611297578415808061126b575b611249576001861490818061121d575b6111f257600287149586806111da575b6111af576003881480611183575b611158578360ff61108a8a612fc3565b92169760ff199289848254161781556110a38882613293565b805463ffff0000191663010100001790555f93156111145750506003919250600e541617600e555b6110d486613609565b611101575091848260ff5f80516020613fe28339815191529560609551948552166020840152820152a280f35b634e487b7160e01b865260219052602485fd5b9092505f935f1461112d5750600e541617600e556110cb565b5f93509091501561114857600590600e541617600e556110cb565b600690600e541617600e556110cb565b600e548651633bf2e2f960e11b815260449161117a908288019060ff16612f4a565b60056024820152fd5b5060ff600e5416600781101561119c576005141561107a565b634e487b7160e01b8a526021855260248afd5b600e548651633bf2e2f960e11b815260449186906111d3908383019060ff16612f4a565b6024820152fd5b5060ff600e5416600781101561119c5784141561106c565b600e548551633bf2e2f960e11b8152604491611214908287019060ff16612f4a565b60036024820152fd5b5060ff600e54166007811015611236576003141561105c565b634e487b7160e01b895260218452602489fd5b600e548451633bf2e2f960e11b815260449161055a908286019060ff16612f4a565b5060ff600e54166007811015611284576002141561104c565b634e487b7160e01b885260218352602488fd5b82516327e1f1e560e01b8152fd5b5082346106f157602080600319360112610e6c57813592828410156114cb5760ff8085168015806114b4575b61147757600181148061149e575b611477576002811480611487575b611477576003148061144d575b61143d57338652607d835261131185838820612fda565b541661142e5761132084613367565b93906001600160a01b03908116330361141f57338752607d845261134686848920612fda565b805460ff19166001179055600654168061139b57508580808087335af161136b6130b5565b501561138d5750905f80516020613fc2833981519152915b519283523392a380f35b90516312171d8360e31b8152fd5b838351809263a9059cbb60e01b8252818a816113ba8b338a840161301f565b03925af19081156114155787916113e8575b501561138d5750905f80516020613fc283398151915291611383565b6114089150843d861161140e575b6114008183612ce3565b810190613090565b876113cc565b503d6113f6565b83513d89823e3d90fd5b5090516330c6392160e11b8152fd5b5163538d585960e11b81529050fd5b8151631d6c64bd60e21b81528490fd5b5080600e54166007811015611464576006116112fa565b634e487b7160e01b875260218552602487fd5b8251631d6c64bd60e21b81528590fd5b5081600e54166007811015610def576005116112ed565b5081600e54166007811015610def5785116112df565b5081600e54166007811015610def576003116112d1565b8480fd5b503461058a578160031936011261058a5760209060ff607a541690519015158152f35b503461058a578160031936011261058a578061095461028092519161151683612cc7565b610140809336903782815161152a81612cc7565b369037805192611539846131b3565b61154284612cc7565b61156282519261155184613223565b61155a84612cc7565b518095612f6b565b830190612f6b565b503461058a578160031936011261058a57602090607f549051908152f35b503461058a578160031936011261058a576020906003549051908152f35b5082346106f15760603660031901126106f1576115c1612f34565b906001600160a01b036024358181169290839003610586576044359482861680960361058657827f000000000000000000000000000000000000000000000000000000000000000016330361092157505060018060a01b0319921682855416178455816001541617600155600254161760025580f35b508290346106f157806003193601126106f157611652612f34565b602491823560018060a01b0392837f0000000000000000000000000000000000000000000000000000000000000000168351946303e1469160e61b9283875260209687818b81875afa9081156118bf576116b79184918d916118a2575016331461317a565b60ff600e541660078110156118905760060361185a5716918451936370a0823160e01b8552308986015286858981875afa948515611850578a95611821575b505f19810361181b5750835b84116117e25785908886518094819382525afa9081156117d85791611747939186938a916117bb575b5089865180968195829463a9059cbb60e01b84528d840161301f565b03925af19081156117b1578691611794575b5015611763578480f35b5162461bcd60e51b815292830152600f908201526e151c985b9cd9995c8819985a5b1959608a1b6044820152606490fd5b6117ab9150833d851161140e576114008183612ce3565b86611759565b82513d88823e3d90fd5b6117d29150843d86116106dd576106cf8183612ce3565b8a61172b565b84513d8a823e3d90fd5b845162461bcd60e51b815280890187905260148189015273496e73756666696369656e742062616c616e636560601b6044820152606490fd5b93611702565b9094508681813d8311611849575b6118398183612ce3565b810103126105865751938a6116f6565b503d61182f565b86513d8c823e3d90fd5b855162461bcd60e51b8152808a018890526011818a01527011d85b59481b9bdd08199a5b9a5cda1959607a1b6044820152606490fd5b634e487b7160e01b8b5260218a52888bfd5b6118b991508a3d8c116106dd576106cf8183612ce3565b8d6106a1565b87513d8d823e3d90fd5b503461058a57602036600319011261058a5760209160ff9082906001600160a01b036118f3612f34565b168152607c85522054169051908152f35b50913461058a57602036600319011261058a573590600a821015610607575060ff60209260f88360051c6075015491519360031b161c168152f35b503461058a578160031936011261058a57606090607e5490607f5460ff608054169081611979575b82519384526020840152151590820152f35b8015159150611967565b503461058a578160031936011261058a576119ee9061060360ff600e541691611a186005549360018060a01b036006541694607754607854906119c4612d06565b976119cd612e1f565b936119d6612da4565b976119f981519c8d9c8d610100908181520190612e9a565b9760208d0190612f4a565b8a01526060890152608088015260a087015285820360c0870152612e9a565b9083820360e0850152612e9a565b5082346106f15760203660031901126106f157611a41612f34565b82516303e1469160e61b815290926001600160a01b03929091906020908290817f000000000000000000000000000000000000000000000000000000000000000087165afa918215611b025750611aa39183918691611ae3575016331461317a565b600280546001600160a01b03198116938316938417909155167f53fab91b0553fa390c7df2daf18c9f2f21878be4f7669c66f13569b32fb1ae2d8380a380f35b611afc915060203d6020116106dd576106cf8183612ce3565b866106a1565b513d86823e3d90fd5b503461058a578160031936011261058a57602090600a549051908152f35b5082346106f15760203660031901126106f1578035908110156106f157611b759060a09360808451611b5a81612c5a565b82815282602082015282868201528260608201520152612fc3565b90805190611b8282612c5a565b82549060ff82169384845260ff60208501818560081c1681526080600185880194848860101c16151586528460608a019860181c161515885201549601958652835196875251166020860152511515908401525115156060830152516080820152f35b50913461058a576020906003198281360112610e6c578135926001600160401b03918285116120aa576101a08585019186360301126120aa576001600160a01b03967f00000000000000000000000000000000000000000000000000000000000000008816330361209b5760848601815191611c6083612c91565b82610104890193368511612097578684915b86831061207f575061ffff91508260ff6060611cac611ca0611cb695848060649a51169187015116906135f3565b838986015116906135f3565b92015116906135f3565b16036120705750611cc783806130ff565b86819b929b1161205d57808a9b89611ce2819c9d9b54612bfb565b89601f9c8d8311612031575b92505050908d8b8411600114611fcd5792611fc2575b50508160011b915f199060031b1c19161788555b60248901356005556044890135908116809103611fbe576006549060ff60a01b611d4460648c016130e4565b60a01b169160018060a81b03191617176006558890895b85898210611fa357505050600755611d7390826130ff565b90848211611f9057611d86600854612bfb565b868111611f63575b508890868311600114611efb57611dc99392918a9183611ef0575b50508160011b915f199060031b1c1916176008555b6101248701906130ff565b9490928511611edd5750611dde600954612bfb565b838111611ea6575b5085928411600114611e3b5750610184939291859183611e30575b50508160011b915f199060031b1c1916176009555b610144810135600a55610164810135600b550135600f5580f35b013590505f80611e01565b91601f198416600987528387209387905b828210611e8e57505091600193918561018497969410611e75575b505050811b01600955611e16565b01355f19600384901b60f8161c191690555f8080611e67565b80600185978294968801358155019601930190611e4c565b611ece90600988528288208580880160051c820192858910611ed4575b0160051c0190613131565b5f611de6565b92508192611ec3565b634e487b7160e01b875260419052602486fd5b013590505f80611da9565b60088a52848a2091601f1984168b5b87828210611f4d575050916001939185611dc997969410611f34575b505050811b01600855611dbe565b01355f19600384901b60f8161c191690555f8080611f26565b6001849682939587013581550195019201611f0a565b611f8a9060088b52858b208880860160051c820192888710611ed4570160051c0190613131565b5f611d8e565b634e487b7160e01b895260418752602489fd5b611fb5839483610bc3600195966130e4565b93019101611d5b565b8980fd5b013590505f80611d04565b925090601f1984168c8452898420935b8a82821061201b575050908460019594939210612002575b505050811b018855611d18565b01355f19600384901b60f8161c191690555f8080611ff5565b6001849682939587013581550195019201611fdd565b612054938152208c80860160051c8201928c8710611ed4570160051c0190613131565b8a5f898f611cee565b634e487b7160e01b8a526041885260248afd5b51632bfdf23b60e11b81528690fd5b819061208a84612fb5565b8152019101908790611c72565b8a80fd5b51630636a15760e11b81528490fd5b8580fd5b5082346106f157826003193601126106f1576001600160a01b03907f00000000000000000000000000000000000000000000000000000000000000008216330361234d57600e549260ff8416600781101561233a57806123165750607854156123095760ff607a54166122fc57600160ff1980951617600e558051924284527f925a19753e677c9dc36a80e0fc824ca0c5b1afde494872b43daccab9ffeaffd460208095a1600d54600c54835190946001600160401b039392909190878201858111838210176122e95786526001825285519163125fa26760e31b898401525115156024830152602482526121a282612cac565b85519460c08601908111868210176122e9579261224089969594938c9388958a528752848701998a5288870160038152606088016207a120815261ffff60808a01926001845260a08b019485528c519d8e9b8c9a8b99634d8e1c2f60e11b8b528a015251602489015251604488015251166064860152519063ffffffff8092166084860152511660a48401525160c060c484015260e4830190612e9a565b03927f0000000000000000000000000000000000000000000000000000000000000000165af19182156122df5785926122ad575b5060017fd28591fd5b8e6c8a0d976474c82e138053b4b2004d8d12bab9c3e5383ad9706b9483607955607a541617607a5551908152a180f35b9391508284813d83116122d8575b6122c58183612ce3565b8101031261058657925190926001612274565b503d6122bb565b81513d87823e3d90fd5b604184634e487b7160e01b5f525260245ffd5b5163029c583d60e01b8152fd5b516387dc625760e01b8152fd5b60449350612332915192633bf2e2f960e11b8452830190612f4a565b5f6024820152fd5b634e487b7160e01b865260218352602486fd5b8251630636a15760e11b8152fd5b503461058a578160031936011261058a57517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461058a578160031936011261058a5760209060ff6076541690519015158152f35b508281600319360112610586576001600160401b03908035828111610586576123ed9036908301612ed8565b936024928335948086116105865736602387011215610586578582013590811161058657368582880101116105865760ff9586600e5416600781101561288357806128605750600a54421161285057600f549182612809575b50505060059061245887600554613055565b95335f52602095607c875261247582865f205416838b169061307c565b8260065460a01c16801515806127fe575b6127cf5750335f52607c885282865f20911660ff1982541617905560018060a01b039182600654168881155f1461276957505088341061274d5788341161271f575b825f5416806125b3575b508a5b8a81106124fe578b6124f88c6124ed8d6077546130f2565b6077556078546130f2565b60785580f35b61250b81871b89016130e4565b8281169060648083101561259d5781101561258b576010018054868116612575576001600160a01b03191633908117909155875489519081526001939291907fd55d45dbc537b82471506213de5e3d6eb436a9048098dc7378087f719b128b9d908d90a3016124d5565b8951630d1a490360e31b81528089018490528690fd5b634e487b7160e01b8e5260328752848efd5b8951634e6153af60e11b81528089018490528690fd5b888a856006541680155f1461266857505050508a836001541684825416813b156106f1578b91606484928b51948593849263474cf53d60e01b84528c840152308a8401528560448401525af1801561265e5761264a575b50505b612619896003546130f2565b6003557fcfd990c8ea9e3bd9e38863886d1bef1edfff9ef9e09b19806edcae848db500c88887518b8152a18b6124d2565b61265390612c33565b612097578a8c61260a565b88513d84823e3d90fd5b612687935f8b5180968195829463095ea7b360e01b84528d840161301f565b03925af180156126f857612702575b50825f54168360065416813b15610586578a60845f92838b51958694859363617ba03760e01b85528c850152898401523060448401528160648401525af180156126f8576126e5575b5061260d565b6126f0919b50612c33565b5f998b6126df565b87513d5f823e3d90fd5b61271890893d8b1161140e576114008183612ce3565b508b612696565b5f80808061272d8d346130a8565b335af16127386130b5565b506124c85785516312171d8360e31b81528490fd5b855163b99e2ab760e01b815234818601528083018a9052604490fd5b5f9160648c8a5194859384926323b872dd60e01b8452338c850152308a85015260448401525af19081156126f8575f916127b257506124c85785516312171d8360e31b81528490fd5b6127c99150893d8b1161140e576114008183612ce3565b8c612738565b84606491888587607c8e335f5252825f205416915193639d69e24960e01b855233908501528301526044820152fd5b508084831611612486565b5f60206128158361303a565b9261282288519485612ce3565b808452808a83860196018637830101525190200361284257878080612446565b90516303e4e47560e21b8152fd5b835163bec2907f60e01b81528390fd5b835f8861287e604494895194633bf2e2f960e11b8652850190612f4a565b820152fd5b86602185634e487b7160e01b5f52525ffd5b508234610586575f36600319011261058657608082516128b481612c91565b36903781516007549060ff9360ff8316825260ff602093818160081c166020850152818160101c168385015260181c1660608301526128f282612c91565b51935f9190855b85841061290557608087f35b8480600192848651168152019301930192916128f9565b50823461058657602036600319011261058657359060648210156105865760109091015490516001600160a01b03919091168152602090f35b5034610586575f3660031901126105865761060390612972612e1f565b9051918291602083526020830190612e9a565b838234610586576020366003190112610586578135918210156105865760ff6129af602093612f08565b92905490519260031b1c168152f35b5034610586575f3660031901126105865760209060ff60065460a01c169051908152f35b5034610586575f3660031901126105865760065490516001600160a01b039091168152602090f35b5034610586575f36600319011261058657602090600f549051908152f35b5034610586575f366003190112610586576020906077549051908152f35b5082346105865781600319360112610586576024356001600160401b03811161058657612a769036908301612ed8565b917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633819003612ab557506100209350356134be565b6044925084519163073e64fd60e21b835233908301526024820152fd5b5034610586575f3660031901126105865760209060ff6080541690519015158152f35b5034610586575f366003190112610586576020906079549051908152f35b5034610586575f36600319011261058657805190612b3082612c75565b610c80809236903780519060105f835b60648210612b8757505050612b5482612c75565b51905f90825b60648310612b6757505050f35b81516001600160a01b031681526001929092019160209182019101612b5a565b82546001600160a01b031681526001928301929190910190602001612b40565b5034610586575f3660031901126105865761060390612972612da4565b5034610586575f3660031901126105865761060390612972612d06565b34610586575f36600319011261058657602090600d548152f35b90600182811c92168015612c29575b6020831014612c1557565b634e487b7160e01b5f52602260045260245ffd5b91607f1691612c0a565b6001600160401b038111612c4657604052565b634e487b7160e01b5f52604160045260245ffd5b60a081019081106001600160401b03821117612c4657604052565b610c8081019081106001600160401b03821117612c4657604052565b608081019081106001600160401b03821117612c4657604052565b606081019081106001600160401b03821117612c4657604052565b61014081019081106001600160401b03821117612c4657604052565b601f909101601f19168101906001600160401b03821190821017612c4657604052565b604051905f8260045491612d1983612bfb565b808352602093600190818116908115612d845750600114612d45575b5050612d4392500383612ce3565b565b9093915060045f52815f20935f915b818310612d6c575050612d4393508201015f80612d35565b85548884018501529485019487945091830191612d54565b915050612d4394925060ff191682840152151560051b8201015f80612d35565b604051905f8260095491612db783612bfb565b808352602093600190818116908115612d845750600114612de0575050612d4392500383612ce3565b9093915060095f52815f20935f915b818310612e07575050612d4393508201015f80612d35565b85548884018501529485019487945091830191612def565b604051905f8260085491612e3283612bfb565b808352602093600190818116908115612d845750600114612e5b575050612d4392500383612ce3565b9093915060085f52815f20935f915b818310612e82575050612d4393508201015f80612d35565b85548884018501529485019487945091830191612e6a565b91908251928382525f5b848110612ec4575050825f602080949584010152601f8019910116010190565b602081830181015184830182015201612ea4565b9181601f84011215610586578235916001600160401b038311610586576020808501948460051b01011161058657565b906004821015612f2057601f8260051c600701921690565b634e487b7160e01b5f52603260045260245ffd5b600435906001600160a01b038216820361058657565b906007821015612f575752565b634e487b7160e01b5f52602160045260245ffd5b5f915b600a8310612f7b57505050565b60019060ff83511681526020809101920192019190612f6e565b6024359060ff8216820361058657565b6044359060ff8216820361058657565b359060ff8216820361058657565b6004811015612f57575f52607b60205260405f2090565b906004811015612f57575f5260205260405f2090565b6040906003190112610586576004356001600160a01b0381168103610586579060243560048110156105865790565b6001600160a01b039091168152602081019190915260400190565b6001600160401b038111612c4657601f01601f191660200190565b8181029291811591840414171561306857565b634e487b7160e01b5f52601160045260245ffd5b9060ff8091169116019060ff821161306857565b90816020910312610586575180151581036105865790565b9190820391821161306857565b3d156130df573d906130c68261303a565b916130d46040519384612ce3565b82523d5f602084013e565b606090565b3560ff811681036105865790565b9190820180921161306857565b903590601e198136030182121561058657018035906001600160401b0382116105865760200191813603831361058657565b81811061313c575050565b5f8155600101613131565b9060ff809160031b9316831b921b19161790565b9081602091031261058657516001600160a01b03811681036105865790565b1561318157565b60405162461bcd60e51b815260206004820152600a60248201526927b7363c9030b236b4b760b11b6044820152606490fd5b61012060745460ff908181168452818160081c166020850152818160101c166040850152818160181c166060850152818160201c166080850152818160281c1660a0850152818160301c1660c0850152818160381c1660e0850152818160401c1661010085015260481c16910152565b61012060755460ff908181168452818160081c166020850152818160101c166040850152818160181c166060850152818160201c166080850152818160281c1660a0850152818160301c1660c0850152818160381c1660e0850152818160401c1661010085015260481c16910152565b9061ff0082549160081b169061ff001916179055565b600a811015612f205760051b60040190565b600a811015612f205760051b6101440190565b90600a811015612f205760051b0190565b81156132e9570490565b634e487b7160e01b5f52601260045260245ffd5b60018060a01b03165f52608160205260ff60405f20541660ff6080541615801561335d575b61335957607c60205260ff60405f20541680156133545761334861335191607f54613055565b607854906132df565b91565b505f91565b5f91565b50607f5415613322565b9061337182612fc3565b549060ff91828160181c16156134b5576040519161338e836131b3565b61339783612cc7565b6040516133a381613223565b6133ac81612cc7565b600a908386168290068616865f805b828116868110156134a657836133d286928c6132ce565b5116146133e35760010182166133bb565b939495969750505050919390935b5f9486600a815f9560081c160616925b8781168381101561349457886134188692856132ce565b511614613429576001018716613401565b94969550600a938693509150505b1602908282169182036130685761344d9161307c565b6064811015612f2057601001546077546001600160a01b0390911693906004821015612f575760649261348261349093612f08565b90549060031b1c1690613055565b0490565b505050505083600a9194939294613437565b505094959392965050506133f1565b505f9250829150565b929192607954036135ee57600e549060ff9060ff8316946007861015612f575760018096036135e65715612f20573593906134f885613cab565b5f905f5b600a81106135c45750506074556135366040519560209660208101918252600160408201526040815261352e81612cac565b519020613cab565b905f955f5b600a81106135a35750505050506002919260755560ff199060018260765416176076551617600e557fb48ed8f2b67f7a4cf1d83936fb4e55a40b7d5bb2b1fb09fe69e91f29a231aa85610280604051613593816131b3565b6135a06101408201613223565ba1565b90919293826135b88299848789511691613147565b9895019392910161353b565b90919360206135da829996848787511691613147565b930191019693966134fc565b505050509050565b509050565b91909161ffff8080941691160191821161306857565b61361281613367565b90607e805461362181856130f2565b6001600160a01b039184831690811515806139d7575b156139795750805f52607d60205260409161365488845f20612fda565b600160ff198254161790555f855580845f5416613969575b50806138c0575b6004881015612f575760205f80516020613fc283398151915291899451908152a35b6004906004861015612f575760038614806138b6575b6136ea575b505050907f4c68367ddbd34d3d0144e5b73c2842cda488332a1acacf11debff28fd03c346b916136e56040519283928361301f565b0390a2565b82549283607f556001935f60ff199260018460805416176080555580835f54166138a6575b50607892607854916040927fd24fb2570481d3406d54ce976d298a890625e4fb3a3451648580154b4294b53b84805184815260209384820152a181613759575b50505050506136b0565b5f5b60648110613769575061374f565b80848a92601001541680151580613891575b613787575b500161375b565b805f52607c845260ff875f205416876137ab6137a38389613055565b8c54906132df565b60065487908a168e81613821575050505f80808084885af16137cb6130b5565b505b6137da575b505050613780565b7f057130b033aff37ce94779177f5a0aba5e46cc901337064f7734906ff0afab6f92845f5260818852825f20878d825416179055825191825287820152a25f8087816137d2565b86979850955f84969761384a96959495519687958694859363a9059cbb60e01b8552840161301f565b03925af1908115613887579189918e969594935f9161386a575b506137cd565b6138819150883d8a1161140e576114008183612ce3565b5f613864565b89513d5f823e3d90fd5b50805f526081845260ff875f2054161561377b565b6138b09150613d70565b5f61370f565b50825415156136ab565b80846006541680155f146138f557505f808080938a5af16138df6130b5565b506136735782516312171d8360e31b8152600490fd5b61391991602091895f885180968195829463a9059cbb60e01b84526004840161301f565b03925af190811561395f575f9161394057506136735782516312171d8360e31b8152600490fd5b613959915060203d60201161140e576114008183612ce3565b5f6138df565b84513d5f823e3d90fd5b6139739150613d70565b5f61366c565b915050848061398a575b5050613695565b613993916130f2565b8083556004861015612f57577f60fc94fd6b7dad2e0cb4f5562c81be8ce4874e7806a06b53bae21e2a0a21471c604087928151908882526020820152a25f84613983565b50821515613637565b6001600160a01b0391821681526020810192909252909116604082015260600190565b604080516303e1469160e61b8152909160206001600160a01b036004828582817f000000000000000000000000000000000000000000000000000000000000000086165afa948515613ca1575f95613c82575b5060ff600e54166007811015613c6f57600603613c6157815f5416958615613c545760249495968484600254168351978880926370a0823160e01b825230888301525afa958615613b8d575f96613c25575b508515613c1757600654841680613b97575050613ae58486856002541686600154165f865180968195829463095ea7b360e01b84528b840161301f565b03925af18015613b8d57613b70575b50826001541691835f541690833b156105865788935f92838993613b2b87519d8e9687958694630402806960e51b865285016139e0565b03925af1968715613b66575f80516020613fa2833981519152959697613b57575b505b519485521692a2565b613b6090612c33565b5f613b4c565b50513d5f823e3d90fd5b613b8690853d871161140e576114008183612ce3565b505f613af4565b82513d5f823e3d90fd5b95859183975f8b96613bbf859c9d975197889687958694631a4ca37b60e21b865285016139e0565b03925af18015613c0d57613be3575b505f80516020613fa283398151915293613b4e565b8390813d8311613c06575b613bf88183612ce3565b81010312610586575f613bce565b503d613bee565b85513d5f823e3d90fd5b50516325b6fdd160e21b8152fd5b9095508481813d8311613c4d575b613c3d8183612ce3565b810103126105865751945f613aa8565b503d613c33565b51631d3b721560e11b8152fd5b855163b7c3a5b360e01b8152fd5b602182634e487b7160e01b5f525260245ffd5b613c9a919550833d85116106dd576106cf8183612ce3565b935f613a56565b86513d5f823e3d90fd5b60409160405190613cbb82612cc7565b6101403683375f5b60ff8116600a811015613ce85760ff9181613ce0600193876132ce565b520116613cc3565b50509290916009805b613cfb5750505090565b825160208101908382528285820152848152613d1681612cac565b519020600182018083116130685780156132e957613d609060ff809181613d3d878b6132ce565b511694061690613d4d82896132ce565b5116613d5985896132ce565b52866132ce565b528015613068575f190180613cf1565b600254604080516370a0823160e01b81523060048201526001600160a01b03945f94909360209392908490829060249082908b165afa908115613ebe575f91613f54575b5080821015613f4c5750935b8415613f4257600654861680613ec85750613dfc8386886002541689600154165f875180968195829463095ea7b360e01b84526004840161301f565b03925af18015613ebe57613ea1575b506001545f5487169616803b1561058657613e425f979188928551998a80948193630402806960e51b83528c3091600485016139e0565b03925af1958615613b8d575f80516020613f82833981519152949596613e90575b505b6003548690808210613e7e5750506003555b51848152a1565b613e8892506130a8565b600355613e77565b613e9a9150612c33565b5f80613e63565b613eb790843d861161140e576114008183612ce3565b505f613e0b565b83513d5f823e3d90fd5b613ef484915f93969798845416908551948580948193631a4ca37b60e21b83528d3091600485016139e0565b03925af18015613b8d57613f18575b505f80516020613f8283398151915292613e65565b8290813d8311613f3b575b613f2d8183612ce3565b81010312610586575f613f03565b503d613f23565b5050509150505f90565b905093613dc0565b90508381813d8311613f7a575b613f6b8183612ce3565b8101031261058657515f613db4565b503d613f6156fe60a52561bffd64573086f5280c1c9cd1bc6ab49cc478f8ccfe689dacec8677eeaed495d70193dd915b96156dc90595c95aa5cc4f6985a2cd79286232d96ae8e264c7f0ad3eacf9f60d2a7e70eca5b5a740b09c3132541056bf6ca27abb6f516e08a8313d2d7c3eae8e87faafbdf295a58197eb2e3767d54d2ae0cce7c3a48292a26469706673582212205df68c8c12fe9a2467be773dad0051a2c57e46708aa193c4f83eb4ec56f23e5564736f6c63430008180033bfe2b363f53fad720f333b704f83a37d87c0db5ab9059f3f446d13d1c1918429a2646970667358221220d0a1b62510f468f6381c50b2bef1327ba3a663a098fd0967c4e041e7509bfd4a64736f6c63430008180033
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
[ Download: CSV Export ]
[ Download: CSV Export ]
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.