Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- PLSHoldem
- Optimization enabled
- false
- Compiler version
- v0.8.28+commit.7893614a
- EVM Version
- Verified at
- 2026-08-20T06:39:16.685564Z
Constructor Arguments
0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000fcafcc30cdfcda7b95e8466a8776d55b9c9963aa
Arg [0] (uint16) : 100
Arg [1] (address) : 0xfcafcc30cdfcda7b95e8466a8776d55b9c9963aa
contracts/PLSHoldem.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {SRA} from "./lib/SRA.sol";
import {CardMerkle} from "./lib/CardMerkle.sol";
import {HandEval} from "./lib/HandEval.sol";
import {BettingLib} from "./lib/BettingLib.sol";
import "./GameTypes.sol";
/// @title PLSHoldem — trustless heads-up No-Limit Texas Hold'em
/// @notice Fully on-chain game arbiter for two-player NLHE with SRA mental
/// poker dealing. The contract enforces turn order, betting legality,
/// blinds, timeouts, showdown evaluation, cheat detection (blame
/// replay) and settlement. Card privacy lives entirely client-side:
/// the contract only ever sees ciphertexts and selectively revealed
/// per-card exponents.
/// @dev Non-upgradeable; no pause; no admin power over live tables. The only
/// owner capability is configuring rake (≤ MAX_RAKE_BPS) and the fee
/// account for FUTURE tables. See SPEC.md / README.md for the protocol.
contract PLSHoldem is ReentrancyGuard {
// ------------------------------------------------------------------
// Storage
// ------------------------------------------------------------------
uint16 public constant MAX_RAKE_BPS = 500; // hard cap: 5%
uint32 public constant MIN_TIMEOUT = 60;
uint32 public constant MAX_TIMEOUT = 7 days;
uint32 public constant MAX_BUYIN_CHIPS = 1_000_000_000;
address public owner;
address public feeAccount; // rake destination for future tables
uint16 public rakeBps; // rake for future tables
uint256 public tableCount;
mapping(uint256 => Table) public tables;
mapping(uint256 => Hand) internal hands;
mapping(uint256 => address) public tableFeeAccount; // frozen at creation
mapping(address => uint256) public withdrawable;
// ------------------------------------------------------------------
// Events
// ------------------------------------------------------------------
event TableCreated(uint256 indexed tableId, address indexed creator, uint96 chipValue,
uint32 buyInChips, uint32 sbChips, uint32 bbChips, uint32 actTimeout,
uint32 cryptoTimeout, uint32 penaltyChips, uint16 rakeBps, uint8 avatar);
event TableJoined(uint256 indexed tableId, address indexed joiner, uint8 avatar);
event TableCancelled(uint256 indexed tableId);
event HandStarted(uint256 indexed tableId, uint32 indexed handId, uint8 btn);
event DeckPosted(uint256 indexed tableId, uint32 indexed handId, uint8 stage, bytes32 root);
event HoleExpPosted(uint256 indexed tableId, uint32 indexed handId, uint8 seat);
event ActionTaken(uint256 indexed tableId, uint32 indexed handId, uint8 seat, uint8 action, uint32 amount);
event PhaseAdvanced(uint256 indexed tableId, uint32 indexed handId, uint8 phase, uint64 deadline);
event BoardRevealed(uint256 indexed tableId, uint32 indexed handId, uint8 street, uint8[5] board);
event ShowdownRevealed(uint256 indexed tableId, uint32 indexed handId, uint8 seat, uint8 c0, uint8 c1, uint32 score);
event Mucked(uint256 indexed tableId, uint32 indexed handId, uint8 seat);
event PotAwarded(uint256 indexed tableId, uint32 indexed handId, uint8 winner, uint32 amount, uint32 rake);
event HandSettled(uint256 indexed tableId, uint32 indexed handId, uint32 stackC, uint32 stackJ);
event TimeoutClaimed(uint256 indexed tableId, uint32 indexed handId, uint8 phase, uint8 staller);
event NoShowClaimed(uint256 indexed tableId, uint32 indexed handId, uint32 chips);
event BlameOpened(uint256 indexed tableId, uint32 indexed handId, uint8 pos, uint8 claimBy, uint8 claimIdx);
event BlameJSubmitted(uint256 indexed tableId, uint32 indexed handId, uint8 j);
event CheatSettled(uint256 indexed tableId, uint8 cheater);
event PlayerLeft(uint256 indexed tableId, uint8 seat);
event SessionSettled(uint256 indexed tableId, uint256 creatorWei, uint256 joinerWei, uint256 rakeWei);
event Withdrawn(address indexed to, uint256 amount);
event RakeConfigured(uint16 rakeBps, address feeAccount);
// ------------------------------------------------------------------
// Construction & admin (future tables only)
// ------------------------------------------------------------------
constructor(uint16 _rakeBps, address _feeAccount) {
if (_rakeBps > MAX_RAKE_BPS) revert RakeCap();
if (_feeAccount == address(0)) revert BadParam();
owner = msg.sender;
rakeBps = _rakeBps;
feeAccount = _feeAccount;
}
/// @notice Configure rake for FUTURE tables only (hard-capped). The only
/// admin capability in the contract.
function setRake(uint16 _rakeBps, address _feeAccount) external {
if (msg.sender != owner) revert NotOwner();
if (_rakeBps > MAX_RAKE_BPS) revert RakeCap();
if (_feeAccount == address(0)) revert BadParam();
rakeBps = _rakeBps;
feeAccount = _feeAccount;
emit RakeConfigured(_rakeBps, _feeAccount);
}
/// @notice Transfer ownership (address(0) permanently renounces rake
/// reconfiguration; existing tables are unaffected either way).
function transferOwnership(address newOwner) external {
if (msg.sender != owner) revert NotOwner();
owner = newOwner;
}
// ------------------------------------------------------------------
// Lobby
// ------------------------------------------------------------------
/// @notice Create a table and escrow the buy-in (exact native tPLS value).
/// @param chipValue wei per chip (fixed conversion both directions)
/// @param buyInChips whole chips both players buy in for
/// @param sbChips small blind in chips (bb >= sb >= 1)
/// @param bbChips big blind in chips (<= buyIn/10)
/// @param actTimeoutS betting-action window, seconds
/// @param cryptoTimeoutS crypto-step / reveal / next-hand window, seconds
/// @param penaltyChips griefing compensation moved to a timeout claimer
/// @param avatar avatar id (0..7) shown at the creator's seat
function createTable(
uint96 chipValue,
uint32 buyInChips,
uint32 sbChips,
uint32 bbChips,
uint32 actTimeoutS,
uint32 cryptoTimeoutS,
uint32 penaltyChips,
uint8 avatar
) external payable returns (uint256 tableId) {
if (chipValue == 0 || buyInChips == 0 || buyInChips > MAX_BUYIN_CHIPS) revert BadParam();
if (sbChips < 1 || bbChips < sbChips || bbChips > buyInChips / 10) revert BadParam();
if (actTimeoutS < MIN_TIMEOUT || actTimeoutS > MAX_TIMEOUT) revert BadParam();
if (cryptoTimeoutS < MIN_TIMEOUT || cryptoTimeoutS > MAX_TIMEOUT) revert BadParam();
if (penaltyChips < 1 || penaltyChips > bbChips) revert BadParam();
if (avatar >= 8) revert BadParam();
if (msg.value != uint256(chipValue) * buyInChips) revert WrongDeposit();
tableId = ++tableCount;
Table storage t = tables[tableId];
t.creator = msg.sender;
t.chipValue = chipValue;
t.status = uint8(TableStatus.OPEN);
t.creatorAvatar = avatar;
t.rakeBps = rakeBps;
t.buyInChips = buyInChips;
t.sbChips = sbChips;
t.bbChips = bbChips;
t.penaltyChips = penaltyChips;
t.actTimeout = actTimeoutS;
t.cryptoTimeout = cryptoTimeoutS;
t.stackC = buyInChips;
t.stackJ = buyInChips;
tableFeeAccount[tableId] = feeAccount;
emit TableCreated(tableId, msg.sender, chipValue, buyInChips, sbChips, bbChips,
actTimeoutS, cryptoTimeoutS, penaltyChips, rakeBps, avatar);
}
/// @notice Join an open table, escrowing the identical buy-in. Atomic:
/// a join racing a cancel simply reverts for whoever lands second.
function joinTable(uint256 tableId, uint8 avatar) external payable {
Table storage t = tables[tableId];
if (t.status != uint8(TableStatus.OPEN)) revert NotOpen();
if (msg.sender == t.creator) revert BadParam();
if (avatar >= 8) revert BadParam();
if (msg.value != uint256(t.chipValue) * t.buyInChips) revert WrongDeposit();
t.joiner = msg.sender;
t.joinerAvatar = avatar;
t.status = uint8(TableStatus.PLAYING);
t.lastSettleTime = uint64(block.timestamp);
emit TableJoined(tableId, msg.sender, avatar);
}
/// @notice Cancel an open (unjoined) table; refunds the creator in full.
function cancelTable(uint256 tableId) external {
Table storage t = tables[tableId];
if (t.status != uint8(TableStatus.OPEN)) revert NotOpen();
if (msg.sender != t.creator) revert NotCreator();
t.status = uint8(TableStatus.CANCELLED);
withdrawable[t.creator] += uint256(t.chipValue) * t.buyInChips;
emit TableCancelled(tableId);
}
// ------------------------------------------------------------------
// Deck stages (SPEC 2.2)
// ------------------------------------------------------------------
/// @notice Post a deck stage (52 × 128-byte values). Stage 1 (creator)
/// starts the hand and auto-posts the blinds; stage 2 (joiner),
/// 3 (creator), 4 (joiner) follow. The contract validates range
/// and distinctness and stores a Merkle root per stage.
function postDeck(uint256 tableId, uint32 handId, uint8 stage, bytes calldata deck) external {
Table storage t = tables[tableId];
Hand storage h = hands[tableId];
if (t.status != uint8(TableStatus.PLAYING)) revert NotPlaying();
if (handId != t.handId) revert WrongHand();
if (stage == 1) {
if (msg.sender != t.creator) revert NotCreator();
if (h.phase != uint8(Phase.NONE)) revert BadPhase();
if (t.stackC == 0 || t.stackJ == 0) revert Bust();
h.stack[0] = t.stackC;
h.stack[1] = t.stackJ;
BettingLib.initHand(h, uint8(t.handId % 2), t.sbChips, t.bbChips);
emit HandStarted(tableId, t.handId, h.btn);
h.phase = uint8(Phase.SHUF2);
} else if (stage == 2) {
if (msg.sender != t.joiner) revert NotJoiner();
if (h.phase != uint8(Phase.SHUF2)) revert BadPhase();
h.phase = uint8(Phase.LOCK1);
} else if (stage == 3) {
if (msg.sender != t.creator) revert NotCreator();
if (h.phase != uint8(Phase.LOCK1)) revert BadPhase();
h.phase = uint8(Phase.LOCK2);
} else if (stage == 4) {
if (msg.sender != t.joiner) revert NotJoiner();
if (h.phase != uint8(Phase.LOCK2)) revert BadPhase();
h.phase = uint8(Phase.DEAL);
} else {
revert BadParam();
}
SRA.checkDeck(deck);
bytes32 root = CardMerkle.computeRoot(deck, stage);
h.roots[stage - 1] = root;
_setDeadline(tableId, t, h, false);
emit DeckPosted(tableId, handId, stage, root);
}
// ------------------------------------------------------------------
// Hole-card dealing
// ------------------------------------------------------------------
/// @notice Post the per-card exponents unlocking the OPPONENT's hole
/// cards (joiner posts positions 0,1; creator posts 2,3). Not
/// verifiable at post time by design — a bad key is caught by
/// accuseHole + blame replay.
function postHoleExp(uint256 tableId, uint32 handId, uint256 e0, uint256 e1) external {
Table storage t = tables[tableId];
Hand storage h = hands[tableId];
if (handId != t.handId) revert WrongHand();
if (h.phase != uint8(Phase.DEAL)) revert BadPhase();
if (!SRA.validScalar(e0) || !SRA.validScalar(e1)) revert BadScalar();
uint8 seat = _seatOf(t, msg.sender);
uint8 bit = seat == 1 ? 1 : 2; // joiner bit0, creator bit1
if (h.dealMask & bit != 0) revert AlreadyPosted();
h.dealMask |= bit;
if (seat == 1) { h.holeExp[0] = e0; h.holeExp[1] = e1; }
else { h.holeExp[2] = e0; h.holeExp[3] = e1; }
emit HoleExpPosted(tableId, handId, seat);
if (h.dealMask == 3) {
h.phase = h.allIn ? uint8(Phase.REVEAL_FLOP) : uint8(Phase.BET_PREFLOP);
_setDeadline(tableId, t, h, !h.allIn);
}
}
// ------------------------------------------------------------------
// Betting
// ------------------------------------------------------------------
/// @notice Take a betting action. `amount` = total street bet for
/// BET/RAISE ("raise to"); ignored otherwise. Rules (min-raise,
/// short all-in, uncalled returns) live in BettingLib.
function act(uint256 tableId, uint32 handId, Action action, uint32 amount) external {
Table storage t = tables[tableId];
Hand storage h = hands[tableId];
if (handId != t.handId) revert WrongHand();
uint8 ph = h.phase;
if (ph != uint8(Phase.BET_PREFLOP) && ph != uint8(Phase.BET_FLOP)
&& ph != uint8(Phase.BET_TURN) && ph != uint8(Phase.BET_RIVER)) revert BadPhase();
uint8 me = _seatOf(t, msg.sender);
if (me != h.toAct) revert NotYourTurn();
uint8 outcome = BettingLib.applyAction(h, me, action, amount, t.bbChips);
emit ActionTaken(tableId, handId, me, uint8(action), amount);
if (outcome == BettingLib.OUT_FOLDED) {
_awardPotAndEndHand(tableId, t, h, 1 - me);
} else if (outcome == BettingLib.OUT_CLOSED) {
_advanceAfterBetting(tableId, t, h);
} else {
_setDeadline(tableId, t, h, true);
}
}
function _advanceAfterBetting(uint256 tableId, Table storage t, Hand storage h) private {
if (h.street == 0) h.phase = uint8(Phase.REVEAL_FLOP);
else if (h.street == 1) h.phase = uint8(Phase.REVEAL_TURN);
else if (h.street == 2) h.phase = uint8(Phase.REVEAL_RIVER);
else {
h.phase = uint8(Phase.SHOWDOWN_1);
h.firstShower = h.riverAggressor != 255 ? h.riverAggressor : 1 - h.btn;
}
_setDeadline(tableId, t, h, false);
}
// ------------------------------------------------------------------
// Board reveals
// ------------------------------------------------------------------
function _streetPositions(uint8 phase) private pure returns (uint8 base, uint8 n) {
if (phase == uint8(Phase.REVEAL_FLOP)) return (4, 3);
if (phase == uint8(Phase.REVEAL_TURN)) return (7, 1);
return (8, 1); // REVEAL_RIVER
}
/// @notice Post your per-card exponents for the current board street.
/// The FIRST poster passes empty claim/value/proof arrays. The
/// SECOND poster (who can decrypt locally once both exponents are
/// known) must pass claimed card indices, the Merkle-proven D4
/// values (concatenated 128-byte blobs) and flat proofs (6 nodes
/// per card); the contract verifies D0[claim]^(eC*eJ) == D4[pos].
/// claim = 255 asserts the position opens to garbage; it must be
/// accompanied by 128-byte inverse exponents (invMine/invTheirs)
/// and opens blame replay when verified.
function postBoardExp(
uint256 tableId,
uint32 handId,
uint256[] calldata exps,
uint8[] calldata claims,
bytes calldata d4vals,
bytes32[] calldata proofs, // flat: 6 nodes per revealed card
bytes calldata invMine,
bytes calldata invTheirs
) external {
Table storage t = tables[tableId];
Hand storage h = hands[tableId];
if (handId != t.handId) revert WrongHand();
uint8 ph = h.phase;
if (ph != uint8(Phase.REVEAL_FLOP) && ph != uint8(Phase.REVEAL_TURN)
&& ph != uint8(Phase.REVEAL_RIVER)) revert BadPhase();
uint8 me = _seatOf(t, msg.sender);
(uint8 base, uint8 n) = _streetPositions(ph);
if (exps.length != n) revert BadData();
for (uint256 i = 0; i < n; i++) {
if (!SRA.validScalar(exps[i])) revert BadScalar();
}
if (h.pendBy == 255) {
// first poster: stash exponents; opponent completes
for (uint256 i = 0; i < n; i++) h.pendExp[i] = exps[i];
h.pendBy = me;
emit PhaseAdvanced(tableId, handId, ph, h.deadline);
return;
}
if (h.pendBy == me) revert AlreadyPosted();
if (claims.length != n || proofs.length != uint256(n) * 6
|| d4vals.length != uint256(n) * 128) revert BadData();
for (uint256 i = 0; i < n; i++) {
uint8 pos = base + uint8(i);
bytes memory d4v = d4vals[i * 128:(i + 1) * 128];
if (!CardMerkle.verify(h.roots[3], 4, pos, d4v, _slice6(proofs, i))) revert BadProof();
uint256 eC = me == 0 ? exps[i] : h.pendExp[i];
uint256 eJ = me == 0 ? h.pendExp[i] : exps[i];
if (claims[i] < 52) {
if (keccak256(SRA.modexpMul(SRA.cardCode(claims[i]), eC, eJ)) != keccak256(d4v)) {
revert CardMismatch();
}
h.board[pos - 4] = claims[i];
} else {
// garbage claim: verify via inverse round-trip, then open blame
if (claims[i] != 255) revert BadData();
uint8 card = _verifiedOpen(d4v, exps[i], h.pendExp[i], invMine, invTheirs);
if (card < 52) {
// opens to a real card: false garbage claim = fold + penalty
_falseAccusation(tableId, t, h, me);
} else {
_openBlame(tableId, t, h, pos, 255, me, eC, eJ);
}
return;
}
}
_afterBoardReveal(tableId, t, h, ph);
}
function _afterBoardReveal(uint256 tableId, Table storage t, Hand storage h, uint8 ph) private {
h.pendBy = 255;
h.pendExp[0] = 0; h.pendExp[1] = 0; h.pendExp[2] = 0;
if (ph == uint8(Phase.REVEAL_FLOP)) h.street = 1;
else if (ph == uint8(Phase.REVEAL_TURN)) h.street = 2;
else h.street = 3;
emit BoardRevealed(tableId, t.handId, h.street, h.board);
if (h.allIn) {
if (ph == uint8(Phase.REVEAL_FLOP)) h.phase = uint8(Phase.REVEAL_TURN);
else if (ph == uint8(Phase.REVEAL_TURN)) h.phase = uint8(Phase.REVEAL_RIVER);
else {
h.phase = uint8(Phase.SHOWDOWN_1);
h.firstShower = h.riverAggressor != 255 ? h.riverAggressor : 1 - h.btn;
}
_setDeadline(tableId, t, h, false);
} else {
if (ph == uint8(Phase.REVEAL_FLOP)) h.phase = uint8(Phase.BET_FLOP);
else if (ph == uint8(Phase.REVEAL_TURN)) h.phase = uint8(Phase.BET_TURN);
else h.phase = uint8(Phase.BET_RIVER);
BettingLib.openStreet(h, t.bbChips);
_setDeadline(tableId, t, h, true);
}
}
/// @dev Open D4[pos] given both exponents + their 128-byte inverses.
/// Each inverse is verified by round-trip (roots are unique in the
/// QR subgroup). Returns card index or 255 for garbage.
function _verifiedOpen(
bytes memory d4v,
uint256 eMine,
uint256 eTheirs,
bytes calldata invMine,
bytes calldata invTheirs
) private view returns (uint8) {
if (invMine.length != 128 || invTheirs.length != 128) revert BadData();
bytes memory t1 = SRA.modexpBytes(d4v, invTheirs);
if (keccak256(SRA.modexp32(t1, eTheirs)) != keccak256(d4v)) revert BadInverse();
bytes memory t2 = SRA.modexpBytes(t1, invMine);
if (keccak256(SRA.modexp32(t2, eMine)) != keccak256(t1)) revert BadInverse();
return SRA.cardFromHash(keccak256(t2));
}
function _slice6(bytes32[] calldata proofs, uint256 i) private pure returns (bytes32[6] memory p) {
for (uint256 d = 0; d < 6; d++) p[d] = proofs[i * 6 + d];
}
// ------------------------------------------------------------------
// Showdown
// ------------------------------------------------------------------
/// @notice Reveal your hole cards at showdown (first shower in
/// SHOWDOWN_1; the other player in SHOWDOWN_2). Provide your two
/// per-card exponents, claimed cards and Merkle-proven D4 values
/// (flat proofs, 6 nodes per card).
function showdown(
uint256 tableId,
uint32 handId,
uint256[2] calldata exps,
uint8[2] calldata claims,
bytes calldata d4vals,
bytes32[] calldata proofs
) external {
Table storage t = tables[tableId];
Hand storage h = hands[tableId];
if (handId != t.handId) revert WrongHand();
uint8 me = _seatOf(t, msg.sender);
if (h.phase == uint8(Phase.SHOWDOWN_1)) {
if (me != h.firstShower) revert NotYourReveal();
} else if (h.phase == uint8(Phase.SHOWDOWN_2)) {
if (me != 1 - h.firstShower) revert NotYourReveal();
} else {
revert BadPhase();
}
if (d4vals.length != 256 || proofs.length != 12) revert BadData();
if (claims[0] >= 52 || claims[1] >= 52 || claims[0] == claims[1]) revert BadData();
for (uint256 i = 0; i < 5; i++) {
if (h.board[i] == claims[0] || h.board[i] == claims[1]) revert BadData();
}
uint8 basePos = me == 0 ? 0 : 2; // creator holes 0,1; joiner 2,3
for (uint256 i = 0; i < 2; i++) {
if (!SRA.validScalar(exps[i])) revert BadScalar();
uint8 pos = basePos + uint8(i);
bytes memory d4v = d4vals[i * 128:(i + 1) * 128];
if (!CardMerkle.verify(h.roots[3], 4, pos, d4v, _slice6(proofs, i))) revert BadProof();
uint256 oppExp = h.holeExp[pos]; // opponent's deal exponent for my hole
uint256 eC = me == 0 ? exps[i] : oppExp;
uint256 eJ = me == 0 ? oppExp : exps[i];
if (keccak256(SRA.modexpMul(SRA.cardCode(claims[i]), eC, eJ)) != keccak256(d4v)) {
revert CardMismatch();
}
h.shownExp[me][i] = exps[i];
h.shownCards[me][i] = claims[i];
}
uint8[7] memory seven;
for (uint256 i = 0; i < 5; i++) seven[i] = h.board[i];
seven[5] = claims[0];
seven[6] = claims[1];
uint32 sc = HandEval.evaluate7(seven);
h.shownScore[me] = sc;
h.shownMask |= uint8(1) << me;
emit ShowdownRevealed(tableId, handId, me, claims[0], claims[1], sc);
if (h.phase == uint8(Phase.SHOWDOWN_1)) {
h.phase = uint8(Phase.SHOWDOWN_2);
_setDeadline(tableId, t, h, false);
} else {
uint8 first = h.firstShower;
uint8 second = 1 - first;
if (h.shownScore[second] > h.shownScore[first]) {
_awardPotAndEndHand(tableId, t, h, second);
} else if (h.shownScore[second] < h.shownScore[first]) {
_awardPotAndEndHand(tableId, t, h, first);
} else {
_splitPotAndEndHand(tableId, t, h);
}
}
}
/// @notice Concede the pot at showdown without revealing (cards stay
/// encrypted forever).
function muck(uint256 tableId, uint32 handId) external {
Table storage t = tables[tableId];
Hand storage h = hands[tableId];
if (handId != t.handId) revert WrongHand();
if (h.phase != uint8(Phase.SHOWDOWN_2)) revert BadPhase();
uint8 me = _seatOf(t, msg.sender);
if (me != 1 - h.firstShower) revert NotYourReveal();
emit Mucked(tableId, handId, me);
_awardPotAndEndHand(tableId, t, h, h.firstShower);
}
// ------------------------------------------------------------------
// Accusations & blame replay (SPEC 2.5)
// ------------------------------------------------------------------
/// @notice Accuse: "my hole card at `pos` opens to garbage". You reveal
/// your own per-card exponent + both 128-byte inverse exponents.
/// If the card is actually valid the accusation is false and you
/// fold + pay the penalty (you also exposed your own card — your
/// choice). If it is garbage, blame replay opens and will cost
/// the cheater their entire stack.
function accuseHole(
uint256 tableId,
uint32 handId,
uint8 pos,
uint256 ownExp,
bytes calldata d4val,
bytes32[6] calldata proof,
bytes calldata invOwn,
bytes calldata invTheirs
) external {
Table storage t = tables[tableId];
Hand storage h = hands[tableId];
if (handId != t.handId) revert WrongHand();
uint8 ph = h.phase;
if (ph < uint8(Phase.BET_PREFLOP) || ph > uint8(Phase.SHOWDOWN_2)) revert BadPhase();
uint8 me = _seatOf(t, msg.sender);
if (!((me == 0 && pos < 2) || (me == 1 && (pos == 2 || pos == 3)))) revert NotYourHole();
if (!SRA.validScalar(ownExp)) revert BadScalar();
if (!CardMerkle.verify(h.roots[3], 4, pos, d4val, proof)) revert BadProof();
uint8 card = _verifiedOpen(d4val, ownExp, h.holeExp[pos], invOwn, invTheirs);
if (card < 52) {
_falseAccusation(tableId, t, h, me);
} else {
uint256 postedC = me == 0 ? ownExp : h.holeExp[pos];
uint256 postedJ = me == 1 ? ownExp : h.holeExp[pos];
_openBlame(tableId, t, h, pos, 255, me, postedC, postedJ);
}
}
function _falseAccusation(uint256 tableId, Table storage t, Hand storage h, uint8 accuser) private {
h.folded = accuser;
BettingLib.closeStreet(h);
_applyPenalty(h, accuser, t.penaltyChips);
_awardPotAndEndHand(tableId, t, h, 1 - accuser);
}
function _openBlame(uint256 tableId, Table storage t, Hand storage h,
uint8 pos, uint8 claimIdx, uint8 claimBy, uint256 postedC, uint256 postedJ) private
{
h.blamePos = pos;
h.blameClaimIdx = claimIdx;
h.blameClaimBy = claimBy;
h.blamePostedC = postedC;
h.blamePostedJ = postedJ;
h.phase = uint8(Phase.BLAME_J);
_setDeadline(tableId, t, h, false);
emit BlameOpened(tableId, t.handId, pos, claimBy, claimIdx);
}
/// @notice Joiner's blame submission: their permutation source j, global
/// and per-card exponents, plus Merkle-proven stage values
/// (D1[j] ‖ D2[pos] ‖ D3[pos] ‖ D4[pos], flat proofs 6 nodes
/// each). Verifies Check2 (D2[pos] == D1[j]^gJ) and Check4
/// (D4[pos]^gJ == D3[pos]^cJ). Any failure = joiner cheated.
function submitBlameJ(
uint256 tableId,
uint32 handId,
uint8 j,
uint256 gJ,
uint256 cJ,
bytes calldata vals,
bytes32[] calldata proofs // flat, 24 nodes
) external {
Table storage t = tables[tableId];
Hand storage h = hands[tableId];
if (handId != t.handId) revert WrongHand();
if (h.phase != uint8(Phase.BLAME_J)) revert BadPhase();
if (msg.sender != t.joiner) revert NotJoiner();
if (j >= 52 || !SRA.validScalar(gJ) || !SRA.validScalar(cJ)) revert BadData();
if (vals.length != 512 || proofs.length != 24) revert BadData();
uint8 pos = h.blamePos;
bytes memory d1j = vals[0:128];
bytes memory d2 = vals[128:256];
bytes memory d3 = vals[256:384];
bytes memory d4 = vals[384:512];
if (!CardMerkle.verify(h.roots[0], 1, j, d1j, _slice6(proofs, 0))) revert BadProof();
if (!CardMerkle.verify(h.roots[1], 2, pos, d2, _slice6(proofs, 1))) revert BadProof();
if (!CardMerkle.verify(h.roots[2], 3, pos, d3, _slice6(proofs, 2))) revert BadProof();
if (!CardMerkle.verify(h.roots[3], 4, pos, d4, _slice6(proofs, 3))) revert BadProof();
// Check2: D2[pos] == D1[j]^gJ
if (SRA.modexpHash32(d1j, gJ) != keccak256(d2)) {
_settleCheat(tableId, t, h, 1);
return;
}
// Check4: D4[pos]^gJ == D3[pos]^cJ
if (SRA.modexpHash32(d4, gJ) != SRA.modexpHash32(d3, cJ)) {
_settleCheat(tableId, t, h, 1);
return;
}
h.blameGJ = gJ;
h.blameCJ = cJ;
h.blameVals[0] = keccak256(d1j);
h.blameVals[1] = keccak256(d2);
h.blameVals[2] = keccak256(d3);
h.blameVals[3] = keccak256(d4);
h.phase = uint8(Phase.BLAME_C);
_setDeadline(tableId, t, h, false);
emit BlameJSubmitted(tableId, handId, j);
}
/// @notice Creator's blame submission: original card index k, global and
/// per-card exponents, plus the raw stage values (must match the
/// hashes pinned in submitBlameJ). Runs Check1, Check3 and the
/// posted-exponent checks, then assigns the cheater — someone
/// always pays: every check isolates a single player's work.
function submitBlameC(
uint256 tableId,
uint32 handId,
uint8 k,
uint256 gC,
uint256 cC,
bytes calldata vals
) external {
Table storage t = tables[tableId];
Hand storage h = hands[tableId];
if (handId != t.handId) revert WrongHand();
if (h.phase != uint8(Phase.BLAME_C)) revert BadPhase();
if (msg.sender != t.creator) revert NotCreator();
if (k >= 52 || !SRA.validScalar(gC) || !SRA.validScalar(cC)) revert BadData();
if (vals.length != 512) revert BadData();
bytes memory d1j = vals[0:128];
bytes memory d2 = vals[128:256];
bytes memory d3 = vals[256:384];
if (keccak256(d1j) != h.blameVals[0] || keccak256(d2) != h.blameVals[1]
|| keccak256(d3) != h.blameVals[2] || keccak256(vals[384:512]) != h.blameVals[3]) {
revert BadData();
}
// Check1: D1[j] == D0[k]^gC
if (SRA.modexpHash32(SRA.cardCode(k), gC) != h.blameVals[0]) {
_settleCheat(tableId, t, h, 0);
return;
}
// Check3: D3[pos]^gC == D2[pos]^cC
if (SRA.modexpHash32(d3, gC) != SRA.modexpHash32(d2, cC)) {
_settleCheat(tableId, t, h, 0);
return;
}
// Step 5: the deck is honest at pos — test the exponents POSTED
// during play (frozen at blame-open) against the pinned true keys.
if (keccak256(SRA.modexpMul(SRA.cardCode(k), cC, h.blamePostedJ)) != h.blameVals[3]) {
_settleCheat(tableId, t, h, 1);
return;
}
if (keccak256(SRA.modexpMul(SRA.cardCode(k), h.blamePostedC, h.blameCJ)) != h.blameVals[3]) {
_settleCheat(tableId, t, h, 0);
return;
}
// both posted exponents open the position to the true card D0[k]:
// the disputed claim itself was the lie.
_settleCheat(tableId, t, h, h.blameClaimBy);
}
// ------------------------------------------------------------------
// Timeouts (SPEC 3.5)
// ------------------------------------------------------------------
/// @notice Claim the opponent timed out on the current phase. Resolution:
/// betting stall = fold; crypto/reveal stall = pot forfeit;
/// SHOWDOWN_2 stall = muck-concede; blame stall = full-stack
/// forfeit. Claimer additionally receives `penaltyChips`.
/// `andSettle` ends the whole session at the resulting stacks
/// (repeated absence never requires grinding hand by hand).
function claimTimeout(uint256 tableId, uint32 handId, uint8 expectedPhase, bool andSettle) external {
Table storage t = tables[tableId];
Hand storage h = hands[tableId];
if (t.status != uint8(TableStatus.PLAYING)) revert NotPlaying();
if (handId != t.handId) revert WrongHand();
uint8 ph = h.phase;
if (ph != expectedPhase || ph == uint8(Phase.NONE)) revert BadPhase();
if (block.timestamp <= h.deadline) revert NotExpired();
uint8 me = _seatOf(t, msg.sender);
uint8 staller;
if (ph == uint8(Phase.SHUF2) || ph == uint8(Phase.LOCK2)) staller = 1;
else if (ph == uint8(Phase.LOCK1)) staller = 0;
else if (ph == uint8(Phase.DEAL)) {
uint8 myBit = me == 1 ? 1 : 2;
if (h.dealMask & myBit == 0) revert PostYoursFirst();
staller = 1 - me;
} else if (ph == uint8(Phase.REVEAL_FLOP) || ph == uint8(Phase.REVEAL_TURN)
|| ph == uint8(Phase.REVEAL_RIVER)) {
if (h.pendBy != me) revert PostYoursFirst();
staller = 1 - me;
} else if (ph == uint8(Phase.BET_PREFLOP) || ph == uint8(Phase.BET_FLOP)
|| ph == uint8(Phase.BET_TURN) || ph == uint8(Phase.BET_RIVER)) {
staller = h.toAct;
} else if (ph == uint8(Phase.SHOWDOWN_1)) {
staller = h.firstShower;
} else if (ph == uint8(Phase.SHOWDOWN_2)) {
staller = 1 - h.firstShower;
} else if (ph == uint8(Phase.BLAME_J)) {
if (me != 0) revert NotCreator();
emit TimeoutClaimed(tableId, handId, ph, 1);
_settleCheat(tableId, t, h, 1);
return;
} else if (ph == uint8(Phase.BLAME_C)) {
if (me != 1) revert NotJoiner();
emit TimeoutClaimed(tableId, handId, ph, 0);
_settleCheat(tableId, t, h, 0);
return;
} else {
revert BadPhase();
}
if (staller == me) revert YouAreTheStaller();
emit TimeoutClaimed(tableId, handId, ph, staller);
BettingLib.closeStreet(h);
_applyPenalty(h, staller, t.penaltyChips);
_awardPotAndEndHand(tableId, t, h, me);
if (andSettle && t.status == uint8(TableStatus.PLAYING)) {
_settleSession(tableId, t);
}
}
/// @notice Claim the creator failed to open the next hand in time. The
/// absent player pays one big blind + penalty from their escrowed
/// stack; the hand number advances (button rotates).
function claimNoShow(uint256 tableId, bool andSettle) external {
Table storage t = tables[tableId];
Hand storage h = hands[tableId];
if (t.status != uint8(TableStatus.PLAYING)) revert NotPlaying();
if (h.phase != uint8(Phase.NONE)) revert BadPhase();
if (msg.sender != t.joiner) revert NotJoiner();
if (block.timestamp <= t.lastSettleTime + t.cryptoTimeout) revert NotExpired();
uint32 x = _min32(t.bbChips + t.penaltyChips, t.stackC);
t.stackC -= x;
t.stackJ += x;
emit NoShowClaimed(tableId, t.handId, x);
emit HandSettled(tableId, t.handId, t.stackC, t.stackJ);
t.handId += 1;
t.lastSettleTime = uint64(block.timestamp);
if (t.stackC == 0 || andSettle) {
_settleSession(tableId, t);
}
}
function _applyPenalty(Hand storage h, uint8 staller, uint32 penalty) private {
uint32 x = _min32(penalty, h.stack[staller]);
h.stack[staller] -= x;
h.stack[1 - staller] += x;
}
// ------------------------------------------------------------------
// Leaving / settlement
// ------------------------------------------------------------------
/// @notice Leave the table. Between hands: session settles at current
/// stacks (either player may exit; this is the cash-out path).
/// Mid-hand: you forfeit the pot + penalty (never a free roll);
/// during blame you are treated as the cheater.
function leaveTable(uint256 tableId) external {
Table storage t = tables[tableId];
Hand storage h = hands[tableId];
if (t.status != uint8(TableStatus.PLAYING)) revert NotPlaying();
uint8 me = _seatOf(t, msg.sender);
uint8 ph = h.phase;
emit PlayerLeft(tableId, me);
if (ph == uint8(Phase.NONE)) {
_settleSession(tableId, t);
} else if (ph == uint8(Phase.BLAME_J) || ph == uint8(Phase.BLAME_C)) {
_settleCheat(tableId, t, h, me);
} else {
BettingLib.closeStreet(h);
_applyPenalty(h, me, t.penaltyChips);
_awardPotAndEndHand(tableId, t, h, 1 - me);
if (t.status == uint8(TableStatus.PLAYING)) {
_settleSession(tableId, t);
}
}
}
function _awardPotAndEndHand(uint256 tableId, Table storage t, Hand storage h, uint8 winner) private {
uint32 rake = uint32((uint256(h.pot) * t.rakeBps) / 10000);
uint32 win = h.pot - rake;
h.stack[winner] += win;
t.rakeChips += rake;
emit PotAwarded(tableId, t.handId, winner, win, rake);
_endHand(tableId, t, h);
}
function _splitPotAndEndHand(uint256 tableId, Table storage t, Hand storage h) private {
uint32 rake = uint32((uint256(h.pot) * t.rakeBps) / 10000);
uint32 rem = h.pot - rake;
uint32 half = rem / 2;
uint8 oop = 1 - h.btn;
h.stack[oop] += half + (rem & 1); // odd chip to the out-of-position player
h.stack[h.btn] += half;
t.rakeChips += rake;
emit PotAwarded(tableId, t.handId, 2, rem, rake);
_endHand(tableId, t, h);
}
function _endHand(uint256 tableId, Table storage t, Hand storage h) private {
t.stackC = h.stack[0];
t.stackJ = h.stack[1];
uint32 ended = t.handId;
t.handId = ended + 1;
t.lastSettleTime = uint64(block.timestamp);
delete hands[tableId];
emit HandSettled(tableId, ended, t.stackC, t.stackJ);
if (t.stackC == 0 || t.stackJ == 0) {
_settleSession(tableId, t);
}
}
/// @dev Cheater forfeits every chip on the table (no rake); session ends.
function _settleCheat(uint256 tableId, Table storage t, Hand storage h, uint8 cheater) private {
uint32 all = h.stack[0] + h.stack[1] + h.streetBet[0] + h.streetBet[1] + h.pot;
if (cheater == 0) { t.stackC = 0; t.stackJ = all; }
else { t.stackC = all; t.stackJ = 0; }
delete hands[tableId];
emit CheatSettled(tableId, cheater);
_settleSession(tableId, t);
}
function _settleSession(uint256 tableId, Table storage t) private {
t.status = uint8(TableStatus.SETTLED);
uint256 cv = t.chipValue;
uint256 cWei = uint256(t.stackC) * cv;
uint256 jWei = uint256(t.stackJ) * cv;
uint256 rWei = uint256(t.rakeChips) * cv;
if (cWei > 0) withdrawable[t.creator] += cWei;
if (jWei > 0) withdrawable[t.joiner] += jWei;
if (rWei > 0) withdrawable[tableFeeAccount[tableId]] += rWei;
delete hands[tableId];
emit SessionSettled(tableId, cWei, jWei, rWei);
}
/// @notice Withdraw all credited winnings/refunds (pull-payment).
function withdraw() external nonReentrant {
uint256 amount = withdrawable[msg.sender];
if (amount == 0) revert NothingToDo();
withdrawable[msg.sender] = 0;
(bool ok, ) = msg.sender.call{value: amount}("");
if (!ok) revert TransferFailed();
emit Withdrawn(msg.sender, amount);
}
// ------------------------------------------------------------------
// Views
// ------------------------------------------------------------------
/// @notice Full hand state (UI + crash recovery).
function getHand(uint256 tableId) external view returns (Hand memory) {
return hands[tableId];
}
/// @notice Open tables for the lobby (bounded scan, newest first).
function getOpenTables(uint256 maxN) external view returns (uint256[] memory ids) {
uint256 n = 0;
uint256[] memory tmp = new uint256[](maxN);
for (uint256 id = tableCount; id >= 1 && n < maxN; id--) {
if (tables[id].status == uint8(TableStatus.OPEN)) tmp[n++] = id;
}
ids = new uint256[](n);
for (uint256 i = 0; i < n; i++) ids[i] = tmp[i];
}
// ------------------------------------------------------------------
// Internals
// ------------------------------------------------------------------
/// @dev Set the phase deadline and emit PhaseAdvanced (single site).
function _setDeadline(uint256 tableId, Table storage t, Hand storage h, bool betting) private {
h.deadline = uint64(block.timestamp) + (betting ? t.actTimeout : t.cryptoTimeout);
emit PhaseAdvanced(tableId, t.handId, h.phase, h.deadline);
}
function _seatOf(Table storage t, address a) private view returns (uint8) {
if (a == t.creator) return 0;
if (a == t.joiner) return 1;
revert NotAPlayer();
}
function _min32(uint32 a, uint32 b) private pure returns (uint32) {
return a < b ? a : b;
}
}
@openzeppelin/contracts/utils/ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.5.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
import {StorageSlot} from "./StorageSlot.sol";
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
* consider using {ReentrancyGuardTransient} instead.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*
* IMPORTANT: Deprecated. This storage-based reentrancy guard will be removed and replaced
* by the {ReentrancyGuardTransient} variant in v6.0.
*
* @custom:stateless
*/
abstract contract ReentrancyGuard {
using StorageSlot for bytes32;
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant REENTRANCY_GUARD_STORAGE =
0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
constructor() {
_reentrancyGuardStorageSlot().getUint256Slot().value = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
/**
* @dev A `view` only version of {nonReentrant}. Use to block view functions
* from being called, preventing reading from inconsistent contract state.
*
* CAUTION: This is a "view" modifier and does not change the reentrancy
* status. Use it only on view functions. For payable or non-payable functions,
* use the standard {nonReentrant} modifier instead.
*/
modifier nonReentrantView() {
_nonReentrantBeforeView();
_;
}
function _nonReentrantBeforeView() private view {
if (_reentrancyGuardEntered()) {
revert ReentrancyGuardReentrantCall();
}
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
_nonReentrantBeforeView();
// Any calls to nonReentrant after this point will fail
_reentrancyGuardStorageSlot().getUint256Slot().value = ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_reentrancyGuardStorageSlot().getUint256Slot().value = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _reentrancyGuardStorageSlot().getUint256Slot().value == ENTERED;
}
function _reentrancyGuardStorageSlot() internal pure virtual returns (bytes32) {
return REENTRANCY_GUARD_STORAGE;
}
}
@openzeppelin/contracts/utils/StorageSlot.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.20;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC-1967 implementation slot:
* ```solidity
* contract ERC1967 {
* // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(newImplementation.code.length > 0);
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* TIP: Consider using this library along with {SlotDerivation}.
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct Int256Slot {
int256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Int256Slot` with member `value` located at `slot`.
*/
function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
assembly ("memory-safe") {
r.slot := store.slot
}
}
/**
* @dev Returns a `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
assembly ("memory-safe") {
r.slot := store.slot
}
}
}
contracts/GameTypes.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/// @title GameTypes — shared structs, enums and errors for PLSHoldem
/// @notice File-level definitions so the main contract and external game
/// libraries share one source of truth.
enum TableStatus { NONE, OPEN, PLAYING, SETTLED, CANCELLED }
enum Phase {
NONE, // between hands: waiting for creator to post D1
SHUF1, // (never stored: D1 post transitions NONE->SHUF2)
SHUF2, // joiner must post D2
LOCK1, // creator must post D3
LOCK2, // joiner must post D4
DEAL, // both must post hole-card exponents
BET_PREFLOP,
REVEAL_FLOP,
BET_FLOP,
REVEAL_TURN,
BET_TURN,
REVEAL_RIVER,
BET_RIVER,
SHOWDOWN_1, // first revealer must show
SHOWDOWN_2, // second player shows or mucks
BLAME_J, // joiner must submit blame data
BLAME_C // creator must submit blame data
}
enum Action { FOLD, CHECK, CALL, BET, RAISE }
struct Table {
address creator;
uint96 chipValue; // wei per chip
address joiner;
uint8 status; // TableStatus
uint8 creatorAvatar;
uint8 joinerAvatar;
uint16 rakeBps;
uint32 buyInChips;
uint32 sbChips;
uint32 bbChips;
uint32 penaltyChips;
uint32 actTimeout; // seconds, betting actions
uint32 cryptoTimeout; // seconds, crypto steps / reveals / next hand
uint32 handId; // next / current hand number
uint32 rakeChips; // accrued rake
uint32 stackC; // creator session stack (between hands)
uint32 stackJ; // joiner session stack
uint64 lastSettleTime; // anchor for the next-hand (no-show) clock
}
struct Hand {
uint8 phase; // Phase
uint8 btn; // 0 = creator has the button, 1 = joiner
uint64 deadline; // current actor(s) must move before this
// betting state (mirrors web/js/betting.mjs)
uint32[2] stack; // chips behind, [creator, joiner]
uint32[2] streetBet;
uint32 pot;
uint32 currentBet;
uint32 inc; // last full raise increment
uint8 street; // 0..3
uint8 toAct;
uint8 firstToAct;
bool reopen;
bool bbOption;
bool allIn;
uint8 folded; // 255 = none
uint8 riverAggressor; // 255 = none
// dealing / reveals
bytes32[4] roots; // D1..D4 Merkle roots
uint256[4] holeExp; // per-card exps for positions 0..3 (0,1 by J; 2,3 by C)
uint8 dealMask; // bit0 = joiner posted, bit1 = creator posted
uint8[5] board; // card indices, 255 = unset
uint256[3] pendExp; // first poster's exps for the current reveal
uint8 pendBy; // 255 = none
// showdown
uint8 firstShower;
uint8 shownMask; // bit per seat
uint256[2][2] shownExp; // [seat][slot]
uint8[2][2] shownCards; // [seat][slot]
uint32[2] shownScore;
// blame
uint8 blamePos;
uint8 blameClaimIdx; // disputed claimed card (255 = "garbage" claim)
uint8 blameClaimBy; // seat whose claim/accusation opened blame
uint256 blameGJ; // J's pinned global exponent
uint256 blameCJ; // J's pinned per-card exponent
uint256 blamePostedC; // C's per-card exponent as posted during play
uint256 blamePostedJ; // J's per-card exponent as posted during play
bytes32[4] blameVals; // keccak of Merkle-proven D1[j], D2..D4[pos]
}
// Custom errors (compact reverts), shared by contract + libraries
error AlreadyPosted();
error BadAmount();
error BadData();
error BadInverse();
error BadParam();
error BadPhase();
error BadProof();
error BadScalar();
error BetBelowMin();
error BetTooLarge();
error Bust();
error CannotCheck();
error CardMismatch();
error FacingABet();
error NotAPlayer();
error NotCreator();
error NotExpired();
error NotJoiner();
error NotOpen();
error NotOwner();
error NotPlaying();
error NotReopened();
error NotYourHole();
error NotYourReveal();
error NotYourTurn();
error NothingToCall();
error NothingToDo();
error NothingToRaise();
error OpponentAllIn();
error PostYoursFirst();
error RaiseBelowMin();
error RaiseTooSmall();
error RakeCap();
error TransferFailed();
error WrongDeposit();
error WrongHand();
error YouAreTheStaller();
contracts/lib/BettingLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {Hand, Action, CannotCheck, NothingToCall, FacingABet, OpponentAllIn,
BadAmount, BetBelowMin, NotReopened, RaiseTooSmall, BetTooLarge,
RaiseBelowMin, BadParam} from "../GameTypes.sol";
/// @title BettingLib — heads-up NLHE betting transitions
/// @notice The exact on-chain mirror of web/js/betting.mjs (the executable
/// betting spec, cross-checked by shared vectors and integration
/// tests). Deployed as an external library to keep the main contract
/// under the EIP-170 size limit.
library BettingLib {
uint8 internal constant OUT_TURN = 0; // turn passed to opponent
uint8 internal constant OUT_CLOSED = 1; // street closed
uint8 internal constant OUT_FOLDED = 2; // actor folded (street closed too)
/// @notice Initialize per-hand betting state and auto-post the blinds.
function initHand(Hand storage h, uint8 btn, uint32 sbChips, uint32 bbChips) public {
h.btn = btn;
h.folded = 255;
h.riverAggressor = 255;
h.pendBy = 255;
h.board = [255, 255, 255, 255, 255];
h.inc = bbChips;
h.reopen = true;
h.bbOption = true;
h.street = 0;
uint8 opp = 1 - btn;
uint32 sbPost = _min32(sbChips, h.stack[btn]);
uint32 bbPost = _min32(bbChips, h.stack[opp]);
h.stack[btn] -= sbPost;
h.streetBet[btn] = sbPost;
h.stack[opp] -= bbPost;
h.streetBet[opp] = bbPost;
h.currentBet = sbPost > bbPost ? sbPost : bbPost;
h.toAct = btn;
h.firstToAct = btn;
if (_shouldAutoClose(h)) closeStreet(h);
}
/// @notice Apply one betting action for seat `me`; reverts if illegal.
/// @return outcome OUT_TURN / OUT_CLOSED / OUT_FOLDED
function applyAction(Hand storage h, uint8 me, Action action, uint32 amount, uint32 bbNominal)
public returns (uint8 outcome)
{
uint8 opp = 1 - me;
uint32 facing = h.currentBet - h.streetBet[me];
if (h.street == 0 && me == 1 - h.btn) h.bbOption = false; // BB is acting
if (action == Action.FOLD) {
h.folded = me;
closeStreet(h);
return OUT_FOLDED;
}
if (action == Action.CHECK) {
if (facing != 0) revert CannotCheck();
if (me != h.firstToAct) {
closeStreet(h);
return OUT_CLOSED;
}
h.toAct = opp;
if (_shouldAutoClose(h)) { closeStreet(h); return OUT_CLOSED; }
return OUT_TURN;
}
if (action == Action.CALL) {
if (facing == 0) revert NothingToCall();
uint32 pay = _min32(facing, h.stack[me]);
h.stack[me] -= pay;
h.streetBet[me] += pay;
// preflop limp: BB is still owed the option to check or raise
if (h.street == 0 && me == h.btn && h.bbOption) {
h.bbOption = false;
h.toAct = opp;
if (_shouldAutoClose(h)) { closeStreet(h); return OUT_CLOSED; }
return OUT_TURN;
}
closeStreet(h);
return OUT_CLOSED;
}
if (action == Action.BET) {
if (h.currentBet != 0) revert FacingABet();
if (h.stack[opp] == 0) revert OpponentAllIn();
if (amount == 0 || amount > h.stack[me]) revert BadAmount();
if (amount < bbNominal && amount != h.stack[me]) revert BetBelowMin();
h.stack[me] -= amount;
h.streetBet[me] = amount;
h.currentBet = amount;
// short all-in bet does not reopen raising for the opponent
if (amount >= bbNominal) { h.inc = amount; h.reopen = true; }
else { h.reopen = false; }
if (h.street == 3) h.riverAggressor = me;
h.toAct = opp;
if (_shouldAutoClose(h)) { closeStreet(h); return OUT_CLOSED; }
return OUT_TURN;
}
if (action == Action.RAISE) {
if (h.currentBet == 0) revert FacingABet();
if (!h.reopen) revert NotReopened();
if (h.stack[opp] == 0) revert OpponentAllIn();
if (amount <= h.currentBet) revert RaiseTooSmall();
uint32 newMoney = amount - h.streetBet[me];
if (newMoney > h.stack[me]) revert BetTooLarge();
uint32 minTo = h.currentBet + h.inc;
bool isAllIn = newMoney == h.stack[me];
if (amount < minTo && !isAllIn) revert RaiseBelowMin();
h.stack[me] -= newMoney;
h.streetBet[me] = amount;
if (amount >= minTo) { h.inc = amount - h.currentBet; h.reopen = true; }
else { h.reopen = false; } // short all-in raise: call/fold only
h.currentBet = amount;
if (h.street == 3) h.riverAggressor = me;
if (h.street == 0) h.bbOption = false;
h.toAct = opp;
if (_shouldAutoClose(h)) { closeStreet(h); return OUT_CLOSED; }
return OUT_TURN;
}
revert BadParam();
}
/// @notice Open the next street's betting round (after a board reveal).
function openStreet(Hand storage h, uint32 bbNominal) public {
h.currentBet = 0;
h.inc = bbNominal;
h.reopen = true;
h.firstToAct = 1 - h.btn;
h.toAct = h.firstToAct;
}
/// @notice Return the uncalled portion, move matched chips to the pot.
function closeStreet(Hand storage h) public {
uint32 m = _min32(h.streetBet[0], h.streetBet[1]);
h.stack[0] += h.streetBet[0] - m;
h.stack[1] += h.streetBet[1] - m;
h.pot += 2 * m;
h.streetBet[0] = 0;
h.streetBet[1] = 0;
if (h.stack[0] == 0 || h.stack[1] == 0) h.allIn = true;
}
/// @dev Nobody can act: actor is all-in, or opponent is all-in with
/// nothing left to call.
function _shouldAutoClose(Hand storage h) private view returns (bool) {
return h.stack[h.toAct] == 0
|| (h.stack[1 - h.toAct] == 0 && h.streetBet[h.toAct] == h.currentBet);
}
function _min32(uint32 a, uint32 b) private pure returns (uint32) {
return a < b ? a : b;
}
}
contracts/lib/CardMerkle.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/// @title CardMerkle — 52-leaf (padded to 64) keccak Merkle tree over deck values
/// @notice leaf(i) = keccak256(uint8 stage ‖ uint8 i ‖ 128-byte value);
/// padding leaves are bytes32(0). Mirrors web/js/merkle.mjs.
library CardMerkle {
uint256 internal constant DECK = 52;
uint256 internal constant TREE = 64;
uint256 internal constant DEPTH = 6;
uint256 internal constant VALUE_BYTES = 128;
/// @notice Compute the root of a full posted deck stage (calldata blob of
/// 52 × 128 bytes). `stage` ∈ 1..4 domain-separates the stages.
function computeRoot(bytes calldata deck, uint8 stage) public pure returns (bytes32) {
require(deck.length == DECK * VALUE_BYTES, "deck len");
bytes32[TREE] memory nodes;
for (uint256 i = 0; i < DECK; i++) {
nodes[i] = keccak256(
abi.encodePacked(stage, uint8(i), deck[i * VALUE_BYTES:(i + 1) * VALUE_BYTES])
);
}
// 12 zero padding leaves are bytes32(0) already
uint256 width = TREE;
while (width > 1) {
for (uint256 i = 0; i < width; i += 2) {
nodes[i / 2] = keccak256(abi.encodePacked(nodes[i], nodes[i + 1]));
}
width /= 2;
}
return nodes[0];
}
/// @notice Verify a single 128-byte value against a stored stage root.
function verify(
bytes32 root,
uint8 stage,
uint8 index,
bytes memory value,
bytes32[DEPTH] memory proof
) public pure returns (bool) {
if (index >= DECK || value.length != VALUE_BYTES) return false;
bytes32 h = keccak256(abi.encodePacked(stage, index, value));
uint256 idx = index;
for (uint256 d = 0; d < DEPTH; d++) {
h = idx & 1 == 1
? keccak256(abi.encodePacked(proof[d], h))
: keccak256(abi.encodePacked(h, proof[d]));
idx >>= 1;
}
return h == root;
}
}
contracts/lib/HandEval.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/// @title HandEval — 7-card Texas Hold'em evaluator
/// @notice Mirrors web/js/handeval.mjs exactly (cross-checked by shared and
/// randomized differential test vectors).
/// @dev Card index k = 13*suit + rank; rank 0='2' … 12='A'.
/// Score: uint32 = category << 20 | five 4-bit tiebreak nibbles.
/// Categories: 0 high, 1 pair, 2 two pair, 3 trips, 4 straight,
/// 5 flush, 6 full house, 7 quads, 8 straight flush.
library HandEval {
/// @notice Evaluate 7 distinct cards; larger score = better hand.
function evaluate7(uint8[7] memory cards) public pure returns (uint32) {
uint8[13] memory rankCount;
uint8[4] memory suitCount;
uint16[4] memory suitMask;
uint16 rankMask;
for (uint256 i = 0; i < 7; i++) {
uint8 k = cards[i];
require(k < 52, "bad card");
uint8 r = k % 13;
uint8 s = k / 13;
rankCount[r]++;
suitCount[s]++;
suitMask[s] |= uint16(1) << r;
rankMask |= uint16(1) << r;
}
// straight flush
for (uint256 s = 0; s < 4; s++) {
if (suitCount[s] >= 5) {
int8 t = straightTop(suitMask[s]);
if (t >= 0) return score(8, uint8(t), 0, 0, 0, 0);
}
}
// rank groups (descending scan)
int8 quad = -1;
int8 trip = -1;
int8 trip2 = -1;
int8 pairHi = -1;
int8 pairLo = -1;
for (int8 r = 12; r >= 0; r--) {
uint8 c = rankCount[uint8(r)];
if (c == 4) quad = r;
else if (c == 3) {
if (trip < 0) trip = r;
else if (trip2 < 0) trip2 = r;
} else if (c == 2) {
if (pairHi < 0) pairHi = r;
else if (pairLo < 0) pairLo = r;
}
}
if (quad >= 0) {
uint16 rest = rankMask & ~(uint16(1) << uint8(quad));
return score(7, uint8(quad), top1(rest), 0, 0, 0);
}
if (trip >= 0 && (trip2 >= 0 || pairHi >= 0)) {
uint8 pr = trip2 >= 0 ? uint8(trip2) : uint8(pairHi);
return score(6, uint8(trip), pr, 0, 0, 0);
}
for (uint256 s = 0; s < 4; s++) {
if (suitCount[s] >= 5) {
(uint8 a, uint8 b, uint8 c, uint8 d, uint8 e) = top5(suitMask[s]);
return score(5, a, b, c, d, e);
}
}
{
int8 t = straightTop(rankMask);
if (t >= 0) return score(4, uint8(t), 0, 0, 0, 0);
}
if (trip >= 0) {
uint16 rest = rankMask & ~(uint16(1) << uint8(trip));
(uint8 k1, uint8 k2) = top2(rest);
return score(3, uint8(trip), k1, k2, 0, 0);
}
if (pairHi >= 0 && pairLo >= 0) {
uint16 rest = rankMask & ~(uint16(1) << uint8(pairHi)) & ~(uint16(1) << uint8(pairLo));
return score(2, uint8(pairHi), uint8(pairLo), top1(rest), 0, 0);
}
if (pairHi >= 0) {
uint16 rest = rankMask & ~(uint16(1) << uint8(pairHi));
(uint8 k1, uint8 k2) = top2(rest);
uint8 k3 = top1(rest & ~(uint16(1) << k1) & ~(uint16(1) << k2));
return score(1, uint8(pairHi), k1, k2, k3, 0);
}
(uint8 h1, uint8 h2, uint8 h3, uint8 h4, uint8 h5) = top5(rankMask);
return score(0, h1, h2, h3, h4, h5);
}
/// @dev Highest straight top-rank in a 13-bit mask, or -1. Wheel top = 3.
function straightTop(uint16 mask) internal pure returns (int8) {
for (int8 t = 12; t >= 4; t--) {
uint16 need = uint16(0x1f) << uint8(t - 4);
if (mask & need == need) return t;
}
// wheel A-2-3-4-5: bits 12,0,1,2,3
if (mask & 0x100f == 0x100f) return 3;
return -1;
}
function score(uint8 cat, uint8 n1, uint8 n2, uint8 n3, uint8 n4, uint8 n5)
private pure returns (uint32)
{
return (uint32(cat) << 20) | (uint32(n1) << 16) | (uint32(n2) << 12)
| (uint32(n3) << 8) | (uint32(n4) << 4) | uint32(n5);
}
function top1(uint16 mask) private pure returns (uint8) {
for (int8 r = 12; r >= 0; r--) {
if (mask & (uint16(1) << uint8(r)) != 0) return uint8(r);
}
return 0;
}
function top2(uint16 mask) private pure returns (uint8, uint8) {
uint8 a = top1(mask);
uint8 b = top1(mask & ~(uint16(1) << a));
return (a, b);
}
function top5(uint16 mask) private pure returns (uint8, uint8, uint8, uint8, uint8) {
uint8[5] memory o;
uint256 n = 0;
for (int8 r = 12; r >= 0 && n < 5; r--) {
if (mask & (uint16(1) << uint8(r)) != 0) {
o[n++] = uint8(r);
}
}
return (o[0], o[1], o[2], o[3], o[4]);
}
}
contracts/lib/SRA.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/// @title SRA — commutative-encryption verification primitives
/// @notice On-chain half of the SRA mental-poker protocol (SPEC §2). All
/// deck values are 1024-bit big-endian byte strings (quadratic
/// residues mod P); all exponent checks run forward through the
/// MODEXP precompile. Mirrors web/js/sra.mjs.
library SRA {
/// RFC 2409 Oakley Group 2 prime (1024-bit safe prime), big-endian words.
uint256 internal constant P0 = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74;
uint256 internal constant P1 = 0x020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437;
uint256 internal constant P2 = 0x4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED;
uint256 internal constant P3 = 0xEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF;
uint256 internal constant VALUE_BYTES = 128;
uint256 internal constant DECK = 52;
/// @dev P as a 128-byte string (for the precompile modulus field).
function pBytes() internal pure returns (bytes memory out) {
out = new bytes(128);
assembly ("memory-safe") {
mstore(add(out, 32), P0)
mstore(add(out, 64), P1)
mstore(add(out, 96), P2)
mstore(add(out, 128), P3)
}
}
/// @notice base^exp mod P with a 32-byte exponent; returns 128-byte result.
function modexp32(bytes memory base128, uint256 exp) public view returns (bytes memory) {
return _modexp(base128, abi.encodePacked(exp));
}
/// @notice base^(e1*e2) mod P (512-bit exponent product of two scalars).
function modexpMul(bytes memory base128, uint256 e1, uint256 e2)
public view returns (bytes memory)
{
(uint256 hi, uint256 lo) = mul512(e1, e2);
return _modexp(base128, abi.encodePacked(hi, lo));
}
/// @dev Full 512-bit product of two uint256.
function mul512(uint256 a, uint256 b) internal pure returns (uint256 hi, uint256 lo) {
unchecked {
lo = a * b;
uint256 mm = mulmod(a, b, type(uint256).max);
hi = mm - lo;
if (mm < lo) hi--;
}
}
/// @dev MODEXP precompile call; base must be 128 bytes; mod = P.
function _modexp(bytes memory base128, bytes memory exp)
private view returns (bytes memory result)
{
require(base128.length == 128, "base len");
bytes memory input = abi.encodePacked(
uint256(128), uint256(exp.length), uint256(128), base128, exp, pBytes()
);
result = new bytes(128);
bool ok;
assembly ("memory-safe") {
ok := staticcall(gas(), 0x05, add(input, 32), mload(input), add(result, 32), 128)
}
require(ok, "modexp failed");
}
/// @notice keccak of base^exp mod P — cheap equality comparisons.
function modexpHash32(bytes memory base128, uint256 exp) public view returns (bytes32) {
return keccak256(modexp32(base128, exp));
}
/// @notice base^exp mod P with an arbitrary-length exponent (used for
/// 1024-bit inverse exponents in dispute round-trips).
function modexpBytes(bytes memory base128, bytes memory exp) public view returns (bytes memory) {
return _modexp(base128, exp);
}
/// @notice Deterministic card code for k in [0,52): full-size QR mod P.
/// @dev h = BE(keccak(TAG‖k‖0)‖…‖keccak(TAG‖k‖3)); code = h^2 mod P.
/// Mirrors sra.mjs cardCode().
function cardCode(uint8 k) public view returns (bytes memory) {
require(k < DECK, "bad card");
bytes memory h = new bytes(128);
bytes memory tag = "PLSHOLDEM_CARD_V1";
for (uint8 ctr = 0; ctr < 4; ctr++) {
bytes32 part = keccak256(abi.encodePacked(tag, k, ctr));
assembly ("memory-safe") {
mstore(add(add(h, 32), mul(ctr, 32)), part)
}
}
return _modexp(h, hex"02");
}
/// @notice keccak of cardCode(k).
function cardCodeHash(uint8 k) internal view returns (bytes32) {
return keccak256(cardCode(k));
}
/// @notice Find the card index whose code hashes to `h`; 255 if none.
function cardFromHash(bytes32 h) public view returns (uint8) {
for (uint8 k = 0; k < DECK; k++) {
if (cardCodeHash(k) == h) return k;
}
return 255;
}
/// @notice Extract 128-byte value i from a packed 52-value deck blob.
function deckValue(bytes calldata deck, uint256 i) internal pure returns (bytes memory v) {
v = deck[i * VALUE_BYTES:(i + 1) * VALUE_BYTES];
}
/// @notice Validate a posted deck stage: exactly 52 values, each in
/// (1, P-1), all distinct. Reverts otherwise.
function checkDeck(bytes calldata deck) public pure {
require(deck.length == DECK * VALUE_BYTES, "deck len");
bytes32[DECK] memory hashes;
for (uint256 i = 0; i < DECK; i++) {
uint256 off = i * VALUE_BYTES;
uint256 w0;
uint256 w1;
uint256 w2;
uint256 w3;
assembly ("memory-safe") {
w0 := calldataload(add(deck.offset, off))
w1 := calldataload(add(deck.offset, add(off, 32)))
w2 := calldataload(add(deck.offset, add(off, 64)))
w3 := calldataload(add(deck.offset, add(off, 96)))
}
require(inRange(w0, w1, w2, w3), "value range");
bytes32 h = keccak256(abi.encodePacked(w0, w1, w2, w3));
for (uint256 j = 0; j < i; j++) {
require(hashes[j] != h, "duplicate value");
}
hashes[i] = h;
}
}
/// @dev true iff 1 < v < P-1 for v given as 4 big-endian words.
function inRange(uint256 w0, uint256 w1, uint256 w2, uint256 w3) internal pure returns (bool) {
// v > 1
if (w0 == 0 && w1 == 0 && w2 == 0 && w3 <= 1) return false;
// v < P - 1 (P3 ends in ...FFFF, so P-1 differs only in last word)
if (w0 < P0) return true;
if (w0 > P0) return false;
if (w1 < P1) return true;
if (w1 > P1) return false;
if (w2 < P2) return true;
if (w2 > P2) return false;
return w3 < P3 - 1;
}
/// @notice Range-check a 128-byte memory value (used for proven values).
function inRangeMem(bytes memory v) internal pure returns (bool) {
require(v.length == 128, "len");
uint256 w0;
uint256 w1;
uint256 w2;
uint256 w3;
assembly ("memory-safe") {
w0 := mload(add(v, 32))
w1 := mload(add(v, 64))
w2 := mload(add(v, 96))
w3 := mload(add(v, 128))
}
return inRange(w0, w1, w2, w3);
}
/// @dev Scalar exponent sanity: odd (invertible mod P-1) and nonzero.
function validScalar(uint256 e) internal pure returns (bool) {
return e & 1 == 1 && e > 1;
}
}
Compiler Settings
{"viaIR":true,"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"],"":["ast"]}},"optimizer":{"runs":1,"enabled":true},"libraries":{"contracts/lib/SRA.sol":{"SRA":"0xb04c1e546d7cf8dc39e10dfc1714847573790806"},"contracts/lib/HandEval.sol":{"HandEval":"0x85c53cbfc7f3425f5328d05a6732f659f468592a"},"contracts/lib/CardMerkle.sol":{"CardMerkle":"0x76a5e1d2e03838dabdce38f3372510ba53cabf08"},"contracts/lib/BettingLib.sol":{"BettingLib":"0xecfafd597d0ffe9d9ba32feaa2f12f12f2bb86dc"}},"evmVersion":"paris"}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"uint16","name":"_rakeBps","internalType":"uint16"},{"type":"address","name":"_feeAccount","internalType":"address"}]},{"type":"error","name":"AlreadyPosted","inputs":[]},{"type":"error","name":"BadData","inputs":[]},{"type":"error","name":"BadInverse","inputs":[]},{"type":"error","name":"BadParam","inputs":[]},{"type":"error","name":"BadPhase","inputs":[]},{"type":"error","name":"BadProof","inputs":[]},{"type":"error","name":"BadScalar","inputs":[]},{"type":"error","name":"Bust","inputs":[]},{"type":"error","name":"CardMismatch","inputs":[]},{"type":"error","name":"NotAPlayer","inputs":[]},{"type":"error","name":"NotCreator","inputs":[]},{"type":"error","name":"NotExpired","inputs":[]},{"type":"error","name":"NotJoiner","inputs":[]},{"type":"error","name":"NotOpen","inputs":[]},{"type":"error","name":"NotOwner","inputs":[]},{"type":"error","name":"NotPlaying","inputs":[]},{"type":"error","name":"NotYourHole","inputs":[]},{"type":"error","name":"NotYourReveal","inputs":[]},{"type":"error","name":"NotYourTurn","inputs":[]},{"type":"error","name":"NothingToDo","inputs":[]},{"type":"error","name":"PostYoursFirst","inputs":[]},{"type":"error","name":"RakeCap","inputs":[]},{"type":"error","name":"ReentrancyGuardReentrantCall","inputs":[]},{"type":"error","name":"TransferFailed","inputs":[]},{"type":"error","name":"WrongDeposit","inputs":[]},{"type":"error","name":"WrongHand","inputs":[]},{"type":"error","name":"YouAreTheStaller","inputs":[]},{"type":"event","name":"ActionTaken","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256","indexed":true},{"type":"uint32","name":"handId","internalType":"uint32","indexed":true},{"type":"uint8","name":"seat","internalType":"uint8","indexed":false},{"type":"uint8","name":"action","internalType":"uint8","indexed":false},{"type":"uint32","name":"amount","internalType":"uint32","indexed":false}],"anonymous":false},{"type":"event","name":"BlameJSubmitted","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256","indexed":true},{"type":"uint32","name":"handId","internalType":"uint32","indexed":true},{"type":"uint8","name":"j","internalType":"uint8","indexed":false}],"anonymous":false},{"type":"event","name":"BlameOpened","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256","indexed":true},{"type":"uint32","name":"handId","internalType":"uint32","indexed":true},{"type":"uint8","name":"pos","internalType":"uint8","indexed":false},{"type":"uint8","name":"claimBy","internalType":"uint8","indexed":false},{"type":"uint8","name":"claimIdx","internalType":"uint8","indexed":false}],"anonymous":false},{"type":"event","name":"BoardRevealed","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256","indexed":true},{"type":"uint32","name":"handId","internalType":"uint32","indexed":true},{"type":"uint8","name":"street","internalType":"uint8","indexed":false},{"type":"uint8[5]","name":"board","internalType":"uint8[5]","indexed":false}],"anonymous":false},{"type":"event","name":"CheatSettled","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256","indexed":true},{"type":"uint8","name":"cheater","internalType":"uint8","indexed":false}],"anonymous":false},{"type":"event","name":"DeckPosted","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256","indexed":true},{"type":"uint32","name":"handId","internalType":"uint32","indexed":true},{"type":"uint8","name":"stage","internalType":"uint8","indexed":false},{"type":"bytes32","name":"root","internalType":"bytes32","indexed":false}],"anonymous":false},{"type":"event","name":"HandSettled","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256","indexed":true},{"type":"uint32","name":"handId","internalType":"uint32","indexed":true},{"type":"uint32","name":"stackC","internalType":"uint32","indexed":false},{"type":"uint32","name":"stackJ","internalType":"uint32","indexed":false}],"anonymous":false},{"type":"event","name":"HandStarted","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256","indexed":true},{"type":"uint32","name":"handId","internalType":"uint32","indexed":true},{"type":"uint8","name":"btn","internalType":"uint8","indexed":false}],"anonymous":false},{"type":"event","name":"HoleExpPosted","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256","indexed":true},{"type":"uint32","name":"handId","internalType":"uint32","indexed":true},{"type":"uint8","name":"seat","internalType":"uint8","indexed":false}],"anonymous":false},{"type":"event","name":"Mucked","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256","indexed":true},{"type":"uint32","name":"handId","internalType":"uint32","indexed":true},{"type":"uint8","name":"seat","internalType":"uint8","indexed":false}],"anonymous":false},{"type":"event","name":"NoShowClaimed","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256","indexed":true},{"type":"uint32","name":"handId","internalType":"uint32","indexed":true},{"type":"uint32","name":"chips","internalType":"uint32","indexed":false}],"anonymous":false},{"type":"event","name":"PhaseAdvanced","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256","indexed":true},{"type":"uint32","name":"handId","internalType":"uint32","indexed":true},{"type":"uint8","name":"phase","internalType":"uint8","indexed":false},{"type":"uint64","name":"deadline","internalType":"uint64","indexed":false}],"anonymous":false},{"type":"event","name":"PlayerLeft","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256","indexed":true},{"type":"uint8","name":"seat","internalType":"uint8","indexed":false}],"anonymous":false},{"type":"event","name":"PotAwarded","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256","indexed":true},{"type":"uint32","name":"handId","internalType":"uint32","indexed":true},{"type":"uint8","name":"winner","internalType":"uint8","indexed":false},{"type":"uint32","name":"amount","internalType":"uint32","indexed":false},{"type":"uint32","name":"rake","internalType":"uint32","indexed":false}],"anonymous":false},{"type":"event","name":"RakeConfigured","inputs":[{"type":"uint16","name":"rakeBps","internalType":"uint16","indexed":false},{"type":"address","name":"feeAccount","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"SessionSettled","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256","indexed":true},{"type":"uint256","name":"creatorWei","internalType":"uint256","indexed":false},{"type":"uint256","name":"joinerWei","internalType":"uint256","indexed":false},{"type":"uint256","name":"rakeWei","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ShowdownRevealed","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256","indexed":true},{"type":"uint32","name":"handId","internalType":"uint32","indexed":true},{"type":"uint8","name":"seat","internalType":"uint8","indexed":false},{"type":"uint8","name":"c0","internalType":"uint8","indexed":false},{"type":"uint8","name":"c1","internalType":"uint8","indexed":false},{"type":"uint32","name":"score","internalType":"uint32","indexed":false}],"anonymous":false},{"type":"event","name":"TableCancelled","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"TableCreated","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256","indexed":true},{"type":"address","name":"creator","internalType":"address","indexed":true},{"type":"uint96","name":"chipValue","internalType":"uint96","indexed":false},{"type":"uint32","name":"buyInChips","internalType":"uint32","indexed":false},{"type":"uint32","name":"sbChips","internalType":"uint32","indexed":false},{"type":"uint32","name":"bbChips","internalType":"uint32","indexed":false},{"type":"uint32","name":"actTimeout","internalType":"uint32","indexed":false},{"type":"uint32","name":"cryptoTimeout","internalType":"uint32","indexed":false},{"type":"uint32","name":"penaltyChips","internalType":"uint32","indexed":false},{"type":"uint16","name":"rakeBps","internalType":"uint16","indexed":false},{"type":"uint8","name":"avatar","internalType":"uint8","indexed":false}],"anonymous":false},{"type":"event","name":"TableJoined","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256","indexed":true},{"type":"address","name":"joiner","internalType":"address","indexed":true},{"type":"uint8","name":"avatar","internalType":"uint8","indexed":false}],"anonymous":false},{"type":"event","name":"TimeoutClaimed","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256","indexed":true},{"type":"uint32","name":"handId","internalType":"uint32","indexed":true},{"type":"uint8","name":"phase","internalType":"uint8","indexed":false},{"type":"uint8","name":"staller","internalType":"uint8","indexed":false}],"anonymous":false},{"type":"event","name":"Withdrawn","inputs":[{"type":"address","name":"to","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint32","name":"","internalType":"uint32"}],"name":"MAX_BUYIN_CHIPS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint16","name":"","internalType":"uint16"}],"name":"MAX_RAKE_BPS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint32","name":"","internalType":"uint32"}],"name":"MAX_TIMEOUT","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint32","name":"","internalType":"uint32"}],"name":"MIN_TIMEOUT","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"accuseHole","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256"},{"type":"uint32","name":"handId","internalType":"uint32"},{"type":"uint8","name":"pos","internalType":"uint8"},{"type":"uint256","name":"ownExp","internalType":"uint256"},{"type":"bytes","name":"d4val","internalType":"bytes"},{"type":"bytes32[6]","name":"proof","internalType":"bytes32[6]"},{"type":"bytes","name":"invOwn","internalType":"bytes"},{"type":"bytes","name":"invTheirs","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"act","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256"},{"type":"uint32","name":"handId","internalType":"uint32"},{"type":"uint8","name":"action","internalType":"enum Action"},{"type":"uint32","name":"amount","internalType":"uint32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"cancelTable","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"claimNoShow","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256"},{"type":"bool","name":"andSettle","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"claimTimeout","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256"},{"type":"uint32","name":"handId","internalType":"uint32"},{"type":"uint8","name":"expectedPhase","internalType":"uint8"},{"type":"bool","name":"andSettle","internalType":"bool"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256","name":"tableId","internalType":"uint256"}],"name":"createTable","inputs":[{"type":"uint96","name":"chipValue","internalType":"uint96"},{"type":"uint32","name":"buyInChips","internalType":"uint32"},{"type":"uint32","name":"sbChips","internalType":"uint32"},{"type":"uint32","name":"bbChips","internalType":"uint32"},{"type":"uint32","name":"actTimeoutS","internalType":"uint32"},{"type":"uint32","name":"cryptoTimeoutS","internalType":"uint32"},{"type":"uint32","name":"penaltyChips","internalType":"uint32"},{"type":"uint8","name":"avatar","internalType":"uint8"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"feeAccount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct Hand","components":[{"type":"uint8","name":"phase","internalType":"uint8"},{"type":"uint8","name":"btn","internalType":"uint8"},{"type":"uint64","name":"deadline","internalType":"uint64"},{"type":"uint32[2]","name":"stack","internalType":"uint32[2]"},{"type":"uint32[2]","name":"streetBet","internalType":"uint32[2]"},{"type":"uint32","name":"pot","internalType":"uint32"},{"type":"uint32","name":"currentBet","internalType":"uint32"},{"type":"uint32","name":"inc","internalType":"uint32"},{"type":"uint8","name":"street","internalType":"uint8"},{"type":"uint8","name":"toAct","internalType":"uint8"},{"type":"uint8","name":"firstToAct","internalType":"uint8"},{"type":"bool","name":"reopen","internalType":"bool"},{"type":"bool","name":"bbOption","internalType":"bool"},{"type":"bool","name":"allIn","internalType":"bool"},{"type":"uint8","name":"folded","internalType":"uint8"},{"type":"uint8","name":"riverAggressor","internalType":"uint8"},{"type":"bytes32[4]","name":"roots","internalType":"bytes32[4]"},{"type":"uint256[4]","name":"holeExp","internalType":"uint256[4]"},{"type":"uint8","name":"dealMask","internalType":"uint8"},{"type":"uint8[5]","name":"board","internalType":"uint8[5]"},{"type":"uint256[3]","name":"pendExp","internalType":"uint256[3]"},{"type":"uint8","name":"pendBy","internalType":"uint8"},{"type":"uint8","name":"firstShower","internalType":"uint8"},{"type":"uint8","name":"shownMask","internalType":"uint8"},{"type":"uint256[2][2]","name":"shownExp","internalType":"uint256[2][2]"},{"type":"uint8[2][2]","name":"shownCards","internalType":"uint8[2][2]"},{"type":"uint32[2]","name":"shownScore","internalType":"uint32[2]"},{"type":"uint8","name":"blamePos","internalType":"uint8"},{"type":"uint8","name":"blameClaimIdx","internalType":"uint8"},{"type":"uint8","name":"blameClaimBy","internalType":"uint8"},{"type":"uint256","name":"blameGJ","internalType":"uint256"},{"type":"uint256","name":"blameCJ","internalType":"uint256"},{"type":"uint256","name":"blamePostedC","internalType":"uint256"},{"type":"uint256","name":"blamePostedJ","internalType":"uint256"},{"type":"bytes32[4]","name":"blameVals","internalType":"bytes32[4]"}]}],"name":"getHand","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"ids","internalType":"uint256[]"}],"name":"getOpenTables","inputs":[{"type":"uint256","name":"maxN","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"joinTable","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256"},{"type":"uint8","name":"avatar","internalType":"uint8"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"leaveTable","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"muck","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256"},{"type":"uint32","name":"handId","internalType":"uint32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"postBoardExp","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256"},{"type":"uint32","name":"handId","internalType":"uint32"},{"type":"uint256[]","name":"exps","internalType":"uint256[]"},{"type":"uint8[]","name":"claims","internalType":"uint8[]"},{"type":"bytes","name":"d4vals","internalType":"bytes"},{"type":"bytes32[]","name":"proofs","internalType":"bytes32[]"},{"type":"bytes","name":"invMine","internalType":"bytes"},{"type":"bytes","name":"invTheirs","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"postDeck","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256"},{"type":"uint32","name":"handId","internalType":"uint32"},{"type":"uint8","name":"stage","internalType":"uint8"},{"type":"bytes","name":"deck","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"postHoleExp","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256"},{"type":"uint32","name":"handId","internalType":"uint32"},{"type":"uint256","name":"e0","internalType":"uint256"},{"type":"uint256","name":"e1","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint16","name":"","internalType":"uint16"}],"name":"rakeBps","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setRake","inputs":[{"type":"uint16","name":"_rakeBps","internalType":"uint16"},{"type":"address","name":"_feeAccount","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"showdown","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256"},{"type":"uint32","name":"handId","internalType":"uint32"},{"type":"uint256[2]","name":"exps","internalType":"uint256[2]"},{"type":"uint8[2]","name":"claims","internalType":"uint8[2]"},{"type":"bytes","name":"d4vals","internalType":"bytes"},{"type":"bytes32[]","name":"proofs","internalType":"bytes32[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"submitBlameC","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256"},{"type":"uint32","name":"handId","internalType":"uint32"},{"type":"uint8","name":"k","internalType":"uint8"},{"type":"uint256","name":"gC","internalType":"uint256"},{"type":"uint256","name":"cC","internalType":"uint256"},{"type":"bytes","name":"vals","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"submitBlameJ","inputs":[{"type":"uint256","name":"tableId","internalType":"uint256"},{"type":"uint32","name":"handId","internalType":"uint32"},{"type":"uint8","name":"j","internalType":"uint8"},{"type":"uint256","name":"gJ","internalType":"uint256"},{"type":"uint256","name":"cJ","internalType":"uint256"},{"type":"bytes","name":"vals","internalType":"bytes"},{"type":"bytes32[]","name":"proofs","internalType":"bytes32[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"tableCount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"tableFeeAccount","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"creator","internalType":"address"},{"type":"uint96","name":"chipValue","internalType":"uint96"},{"type":"address","name":"joiner","internalType":"address"},{"type":"uint8","name":"status","internalType":"uint8"},{"type":"uint8","name":"creatorAvatar","internalType":"uint8"},{"type":"uint8","name":"joinerAvatar","internalType":"uint8"},{"type":"uint16","name":"rakeBps","internalType":"uint16"},{"type":"uint32","name":"buyInChips","internalType":"uint32"},{"type":"uint32","name":"sbChips","internalType":"uint32"},{"type":"uint32","name":"bbChips","internalType":"uint32"},{"type":"uint32","name":"penaltyChips","internalType":"uint32"},{"type":"uint32","name":"actTimeout","internalType":"uint32"},{"type":"uint32","name":"cryptoTimeout","internalType":"uint32"},{"type":"uint32","name":"handId","internalType":"uint32"},{"type":"uint32","name":"rakeChips","internalType":"uint32"},{"type":"uint32","name":"stackC","internalType":"uint32"},{"type":"uint32","name":"stackJ","internalType":"uint32"},{"type":"uint64","name":"lastSettleTime","internalType":"uint64"}],"name":"tables","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"withdraw","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"withdrawable","inputs":[{"type":"address","name":"","internalType":"address"}]}]
Contract Creation Code
0x6080346100fe57601f615fd138819003918201601f19168301916001600160401b038311848410176101035780849260409485528339810103126100fe57805161ffff8116918282036100fe57602001516001600160a01b03811692908390036100fe576101f49060017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055116100ed5781156100dc57600080546001600160a01b03191633179055600180546001600160b01b03191660a09290921b61ffff60a01b1691909117919091179055604051615eb7908161011a8239f35b63de17a3af60e01b60005260046000fd5b63c6c3513b60e01b60005260046000fd5b600080fd5b634e487b7160e01b600052604160045260246000fdfe6102c0604052600436101561001357600080fd5b6000803560e01c806301bf32dc14612d6857806308ce201114612be8578063102672ba14612bcb5780631162384c146122db57806317cf8c0d146122bc57806321c9c44a14612297578063264f5c01146121bb57806327e5e0c114611f0e5780633ccfd60b14611e0b5780633f637e8614611dd05780634155fc5b14611db2578063543ad1df14611d9657806365e17c9d14611d6d578063668fd78214611b1a578063704b25271461168857806370a4e95b1461165557806371671bf214611523578063763c8cd91461142457806381c908581461133a578063898acd1f146110285780638da5cb5b146110015780639492264514610ee4578063b3a5c8c814610e8c578063ce513b6f14610e53578063d1e9e14014610d39578063d47f269e14610547578063d924cb931461046a578063de38eb3a1461044c578063dfa170e7146103c9578063f2fde38b146103715763f42452581461017357600080fd5b3461036e57604036600319011261036e57600435602435801515810361036c5781835260036020526040832090828452600460205260408420600183015490600260ff8360a01c160361035d575460ff1661034e576001600160a01b0316330361033f576003820180546002840180549092916001600160401b039061020990608084901c63ffffffff169060201c83166151ee565b1642111561033057906102f36102ed7f50038a922c31e04bdb655c3d957330398521774f6656bf130c3fcb3303102d1d602089600080516020615da28339815191528761028b61028561027e61027363ffffffff6103039d60401c1663ffffffff868a1c1661521f565b9360e01c8094615cc6565b8093615239565b8b61322f565b63ffffffff61029d82828b541661521f565b1663ffffffff198954161788558954908363ffffffff8360a01c16968796879363ffffffff60405191168152a363ffffffff885416906102e560405192839260e01c83615253565b0390a3615207565b8461526a565b426001600160401b03169061430c565b5460e01c15908115610328575b50610319578280f35b6103229161552c565b38808280f35b905038610310565b63d0404f8560e01b8752600487fd5b6338b72ae960e11b8452600484fd5b63301c306b60e01b8552600485fd5b63126ab15560e01b8652600486fd5b825b80fd5b503461036e57602036600319011261036e5761038b61316b565b8154906001600160a01b03821633036103ba576001600160a01b03166001600160a01b03199190911617815580f35b6330cd747160e01b8352600483fd5b503461036e5760e036600319011261036e576103e36130d8565b6103eb6130fe565b60a4356001600160401b0381116104485761040a90369060040161310e565b60c43593916001600160401b0385116104445761042e61044195369060040161313b565b949093608435916064359160043561496f565b80f35b8580fd5b8380fd5b503461036e578060031936011261036e57602060405162093a808152f35b503461036e57604036600319011261036e5760043561ffff81169081810361036c576024356001600160a01b03811691908290036104485783546001600160a01b03163303610538576101f4831161052957811561051a57600180546001600160b01b03191660a09290921b61ffff60a01b1691909117821790556040805192835260208301919091527fb5add0bb8de8016e167bf0c471fb32731f765637e15e0156d13ee6853f6a064391a180f35b63de17a3af60e01b8452600484fd5b63c6c3513b60e01b8452600484fd5b6330cd747160e01b8452600484fd5b503461036e57602036600319011261036e5760405161056581613252565b8181528160208201528160408201526040918251610583848261329f565b8336823760608301528251610598848261329f565b8336823760808301528060a08301528060c08301528060e08301528061010083015280610120830152806101408301528061016083015280610180830152806101a0830152806101c0830152806101e083015260809280516105fa858261329f565b843682376102008401528051610610858261329f565b843682376102208401528161024084015260a0815161062f828261329f565b813682376102608501526060938251610648868261329f565b85368237610280820152836102a0820152836102c0820152836102e08201528251610673848261329f565b845b848110610d1c5750610300820152825161068f848261329f565b845b848110610cff575061032082015282516106ab848261329f565b833682376103408201528361036082015283610380820152836103a0820152836103c0820152836103e082015283610400820152836104208201526104408351916106f6888461329f565b873684370152600435835260046020528183209482519561071687613252565b805460ff8082168952600882901c16602089015260101c6001600160401b031684880152610746600182016148e1565b86880152610756600282016148e1565b8288015260ff600382015463ffffffff8116858a015263ffffffff8160201c1660c08a015263ffffffff81871c1660e08a01528181891c166101008a0152818160681c166101208a0152818160701c166101408a0152818160781c1615156101608a01528181851c1615156101808a0152818160881c1615156101a08a0152818160901c166101c08a015260981c166101e08801526107f760048201614905565b61020088015283516008820186825b60048210610ce95750505061081b838261329f565b61022088015260ff600c82015416610240880152835161083e81600d840161493a565b610848848261329f565b6102608801528351600e820186825b60038210610cd35750505061086c878261329f565b61028088015260ff60118201548181166102a08a0152818160081c166102c08a015260101c166102e088015283516108a381613284565b6012820186825b60028210610c8c5750505061030088015283516108c681613284565b6016820186825b60028210610c5b575050509161095b601e6109a49363ffffffff96956103208c01526108fb601882016148e1565b6103408c015260ff60198201548c6103608383169101528c610380838360081c1691015260101c166103a08c0152601a8101546103c08c0152601b8101546103e08c0152601c8101546104008c0152601d8101546104208c015201614905565b61044089015261099885519760ff8a5116895260ff60208b01511660208a015260018060401b03878b015116878a0152808a015190890190613181565b87015182870190613181565b8501511660e084015263ffffffff60c08501511661010084015263ffffffff60e08501511661012084015260ff6101008501511661014084015260ff6101208501511661016084015260ff6101408501511661018084015261016084015115156101a084015261018084015115156101c08401526101a084015115156101e084015260ff6101c08501511661020084015260ff6101e085015116610220840152610a586102008501516102408501906131af565b610220840151826102c085015b60048210610c455750505061024084015160ff166103408401526102608401518261036085015b60058210610c2c575050506102808401518261040085015b60038210610c165750505060ff6102a08501511661046084015260ff6102c08501511661048084015260ff6102e0850151166104a08401526103008401516104c0840183905b60028210610bd85750505061032084015190829061054085015b60028310610b975761076086610b956104408a610b2b6103408201516105c0860190613181565b60ff6103608201511661060085015260ff6103808201511661062085015260ff6103a0820151166106408501526103c08101516106608501526103e08101516106808501526104008101516106a08501526104208101516106c085015201516106e08301906131af565bf35b818451829087915b60028310610bbc5750505060209060019201940192019192610b04565b815160ff16815285935060019092019160209182019101610b9f565b838351829087915b60028310610bfd5750505060209060019201930191019091610aea565b8151815287935060019092019160209182019101610be0565b6020806001928551815201930191019091610aa4565b60208060019260ff865116815201930191019091610a8c565b6020806001928551815201930191019091610a65565b6001602081928a5160ff8754818116835260081c1683820152610c7e8c8261329f565b8152019301910190916108cd565b87518984825b60028310610cbd57505050602060019282610caf8c60029561329f565b8152019301910190916108aa565b6001602081928454815201920192019190610c92565b6001602081928554815201930191019091610857565b6001602081928554815201930191019091610806565b6020908551610d0e878261329f565b863682378184015201610691565b6020908551610d2b878261329f565b863682378184015201610675565b503461036e57602036600319011261036e576004359080610d598361489b565b906002545b600181101580610e4a575b15610dc8578084526003602052600160ff8160408720015460a01c1614610dae575b8015610d9a5760001901610d5e565b634e487b7160e01b84526011600452602484fd5b80610dc2610dbb84613200565b93856148cd565b52610d8b565b50908290610dd58361489b565b92825b818110610e2657505050604051918291602083016020845282518091526020604085019301915b818110610e0d575050500390f35b8251845285945060209384019390920191600101610dff565b80610e356001928597956148cd565b51610e4082866148cd565b5201939193610dd8565b50848210610d69565b503461036e57602036600319011261036e576020906040906001600160a01b03610e7b61316b565b168152600683522054604051908152f35b503461036e5760c036600319011261036e57610ea66130d8565b610eae6130fe565b60a435916001600160401b03831161044857610ed161044193369060040161310e565b9290916084359160643591600435614351565b50604036600319011261036e576004356024359060ff821691828103610448578184526003602052604084209060018201908154600160ff8260a01c1603610ff257835490336001600160a01b03831614610fe3576008871015610fe3579063ffffffff610f599260c81c169060a01c6131d7565b3403610fd457815462ff00ff60a01b193316600161ff0160a81b03199091161760b09190911b60ff60b01b1617600160a11b179055610fa590426001600160401b03169060030161430c565b6040519182527f396423bd1651bba635e3419ce325aa401229452a6b2be1768843d2e8dea8177860203393a380f35b634366b25d60e01b8652600486fd5b63de17a3af60e01b8852600488fd5b631bb5f5b360e31b8752600487fd5b503461036e578060031936011261036e57546040516001600160a01b039091168152602090f35b503461036e576101a036600319011261036e576004356110466130d8565b61104e6130fe565b906064356084356001600160401b0381116104445761107190369060040161310e565b9093366101641161133657610164356001600160401b0381116113325761109c90369060040161310e565b9095909390610184356001600160401b03811161132e576110c190369060040161310e565b90898b52600360205260408b20968a8c52600460205260408c209863ffffffff8060028b015460a01c1691160361131f5760ff89541660068110908115611314575b5061130557611112338961528d565b9960ff8b169788159586806112f8575b80156112cb575b156112bc57600180891614806112b2575b156112a3578e60078d01546040519063af0586a360e01b825260048201526004602482015260ff8b1660448201526101406064820152602081806111836101448201888861399d565b60c060a4608484013703817376a5e1d2e03838dabdce38f3372510ba53cabf085af49182156112975791611268575b5015611259579360ff936111ee938a9897938a8f6034996111da60086111e993019d8e61341a565b90549060031b1c9336916133e3565b615981565b16101561120357505050505061044193615c2a565b9261044198979592918195979460001461124057600183985b0361122957505095615b97565b611233925061341a565b90549060031b1c95615b97565b600161124c838361341a565b90549060031b1c9861121c565b637ca55c7760e01b8f5260048ffd5b61128a915060203d602011611290575b611282818361329f565b81019061342a565b386111b2565b503d611278565b604051903d90823e3d90fd5b63284f145b60e11b8f5260048ffd5b506001881161113a565b631749e6cd60e11b8f5260048ffd5b5060018a148015611129575060ff8916600281149081156112ed575b50611129565b6003915014386112e7565b50600260ff8a1610611122565b63301c306b60e01b8c5260048cfd5b600e91501138611103565b636226556b60e01b8c5260048cfd5b8980fd5b8780fd5b8680fd5b503461036e57604036600319011261036e57600435906113586130d8565b8282526003602052604082208383526004602052604083209063ffffffff80600283015460a01c16931692830361141557600e60ff835416036114065761139f338261528d565b9460ff601184015460081c169360ff806113b8876132c2565b1697169687036113f757817f50295138b8e123fbfba534eabbb8054c406902fa7aadac163b85767230452e7e60206104419899604051908152a3615350565b630304193d60e11b8652600486fd5b63301c306b60e01b8452600484fd5b636226556b60e01b8452600484fd5b503461036e5761010036600319011261036e5761143f6130d8565b6044356001600160401b03811161036c5761145e90369060040161313b565b916064356001600160401b03811161151f5761147e90369060040161313b565b906084356001600160401b0381116113365761149e90369060040161310e565b9060a4356001600160401b03811161151b576114be90369060040161313b565b92909160c4356001600160401b038111611517576114e090369060040161310e565b60e4359a90979196906001600160401b038c11611513576115086104419c369060040161310e565b9b909a6004356139fb565b8c80fd5b8a80fd5b8880fd5b8480fd5b503461036e57602036600319011261036e57604061024091600435815260036020522080549060018101549063ffffffff60036002830154920154926040519460018060a01b038116865260a01c602086015260018060a01b038116604086015260ff8160a01c16606086015260ff8160a81c16608086015260ff8160b01c1660a086015261ffff8160b81c1660c086015260c81c1660e084015263ffffffff811661010084015263ffffffff8160201c1661012084015263ffffffff8160401c1661014084015263ffffffff8160601c1661016084015263ffffffff8160801c1661018084015263ffffffff8160a01c166101a084015263ffffffff8160c01c166101c084015260e01c6101e083015263ffffffff811661020083015260018060401b039060201c16610220820152f35b503461036e57602036600319011261036e57602090600435815260058252604060018060a01b0391205416604051908152f35b503461036e57608036600319011261036e576004356116a56130d8565b906116ae6130fe565b6064356001600160401b03811161151f576116cd90369060040161310e565b838652600360205260408620848752600460205260408720916001820194855495600260ff8860a01c1603611b0b57600284019163ffffffff80845460a01c169a16998a03611afc5760ff169660018803611a1d57505082546001600160a01b03163303611a0e5760ff8454166119ff57805460e01c801580156119ed575b6119de576117769061176360018701918c836135b5565b600463ffffffff600387015416916135b5565b8873ecfafd597d0ffe9d9ba32feaa2f12f12f2bb86dc8254813b1561036c5763ffffffff60848492604051948593849263074bdbe560e01b84528c600485015260018160a01c166024850152818116604485015260201c1660648301525af480156119ba576119c9575b505063ffffffff905460a01c16867fbfeb6d84abeccf1947b6157b7ac00b04d40817b53e2440dcfa49f712fa234810602060ff875460081c16604051908152a3825460ff191660021783555b8773b04c1e546d7cf8dc39e10dfc1714847573790806803b156119c557816040518092626a007d60e01b825260206004830152818061186f60248201898d61399d565b03915af480156119ba576119a5575b50506118aa93602091604051958692839263b65c02d360e01b845260406004850152604484019161399d565b87602483015203817376a5e1d2e03838dabdce38f3372510ba53cabf085af492831561199a578793611961575b50600019840160ff811161194d5782849260409561193761192061193f9560047ff4169c826e1cd0db9f677dc2ffe40cc7cae264e9ecb54b4cc220cff5b82bbfc69b990161341a565b819391549060031b91821b91600019901b19161790565b9055876158ad565b82519182526020820152a380f35b634e487b7160e01b88526011600452602488fd5b9092506020813d602011611992575b8161197d6020938361329f565b8101031261198d575191386118d7565b600080fd5b3d9150611970565b6040513d89823e3d90fd5b816119af9161329f565b61133257873861187e565b6040513d84823e3d90fd5b5080fd5b816119d39161329f565b61151b5788386117e0565b6309107e2560e31b8a5260048afd5b5063ffffffff6003850154161561174c565b63301c306b60e01b8952600489fd5b6393687c0b60e01b8952600489fd5b90915060028703611a735750546001600160a01b03163303611a6457600260ff84541603611a5557825460ff1916600317835561182c565b63301c306b60e01b8852600488fd5b6338b72ae960e11b8852600488fd5b905060038603611aba575081546001600160a01b03163303611aab57600360ff84541603611a5557825460ff1916600417835561182c565b6393687c0b60e01b8852600488fd5b60048603611aed576001600160a01b03163303611a6457600460ff84541603611a5557825460ff1916600517835561182c565b63de17a3af60e01b8952600489fd5b636226556b60e01b8b5260048bfd5b63126ab15560e01b8a5260048afd5b503461036e57608036600319011261036e57600435611b376130d8565b90604435606435908285526003602052604085209383865260046020526040862092600286019563ffffffff80885460a01c169316928303611d5e57600560ff86541603611a555760018085161480611d54575b158015611d37575b611d2857611ba460ff91339061528d565b1690600182149384600014611d20576001945b600c870195865460ff82821616611d11579460ff96948a947fe47a1a70cd8e25e08c403d1b29c6e7f3f5625c8899a042b3d6c0971ebf8b4cad94896020958160039d9b1617168a198a5416178955600014611d025760088b015560098a01555b604051908152a3541614611c29578380f35b60ff81816003600080516020615e62833981519152940181815460881c16600014611cfa578160075b1682198454161783555460881c1615600014611ce257611cd863ffffffff611c8c8180895460601c165b16426001600160401b03166151ee565b835462010000600160501b03198116601092831b62010000600160501b03161794859055975460405160a09190911c92909216979194859490911c6001600160401b03169116836139de565b0390a33880808380f35b611cd863ffffffff611c8c8180895460801c16611c7c565b816006611c52565b600a8b0155600b8a0155611c17565b63e9f155db60e01b8c5260048cfd5b600294611bb7565b63284f145b60e11b8852600488fd5b5060018083161480611d4a575b15611b93565b5060018211611d44565b5060018411611b8b565b636226556b60e01b8852600488fd5b503461036e578060031936011261036e576001546040516001600160a01b039091168152602090f35b503461036e578060031936011261036e576020604051603c8152f35b503461036e578060031936011261036e576020600254604051908152f35b503461036e57608036600319011261036e57611dea6130d8565b611df26130fe565b6064359081151582036104485761044192600435613601565b503461036e578060031936011261036e576002600080516020615e428339815191525414611eff576002600080516020615e4283398151915255338152600660205260408120548015611ef05733825260066020528160408120558180808084335af13d15611eeb573d611e7e8161338d565b90611e8c604051928361329f565b81528360203d92013e5b15611edc576040519081527f7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d560203392a26001600080516020615e428339815191525580f35b6312171d8360e31b8252600482fd5b611e96565b630b8a550d60e31b8252600482fd5b633ee5aeb560e01b8152600490fd5b503461036e57608036600319011261036e57600435611f2b6130d8565b60443590600582101561044857611f406130eb565b90838552600360205260408520918486526004602052604086209363ffffffff600285015493169263ffffffff8160a01c168403611d5e5760ff865416600681141590816121af575b816121a3575b81612197575b50611a5557611fa4338661528d565b93600387019360ff855460681c1660ff87169081036121885763ffffffff80604051936370807d8760e11b85528b6004860152836024860152611fe68861320f565b876044860152169485606485015260201c16608483015260208260a48173ecfafd597d0ffe9d9ba32feaa2f12f12f2bb86dc5af493841561217d5760ff958b938d96612133575b5091606091878461204c600080516020615dc28339815191529661320f565b6040519384521660208301526040820152a3166002810361207e575050906120786104419493926132c2565b92615350565b90949150600103612129578360ff610441955460601c1680156000146120b0575050825460ff191660071783556158ad565b600181036120ca575050825460ff191660091783556158ad565b6002036120e25750825460ff1916600b1783556158ad565b835460ff1916600d1784555460981c60ff9081169081146121105761210b905b6011850161591b565b6158ad565b5061210b61212460ff855460081c166132c2565b612102565b61044193506158e4565b600080516020615dc283398151915293919650918761216b60609460203d602011612176575b612163818361329f565b8101906135e8565b97929450509161202d565b503d612159565b6040513d8d823e3d90fd5b631cc191eb60e31b8b5260048bfd5b600c9150141538611f95565b600a8114159150611f8f565b60088114159150611f89565b503461036e57602036600319011261036e576004358082526003602052604082206001810190600160ff835460a01c16036122885780546001600160a01b0316330361227957815460ff60a01b1916600160a21b1791829055549061222d9060c81c63ffffffff1660a083901c6131d7565b6001600160a01b0390911683526006602052604083208054909161225091613368565b90557f8bd66138689095e38b45ec45363241e614b91b8e6fb90f7c15dfdeb93f34fcfe8280a280f35b6393687c0b60e01b8452600484fd5b631bb5f5b360e31b8452600484fd5b503461036e578060031936011261036e57602061ffff60015460a01c16604051908152f35b503461036e578060031936011261036e576020604051633b9aca008152f35b503461036e5761010036600319011261036e576122f66130d8565b366084116119c5573660c4116119c55760c4356001600160401b03811161036c5761232590369060040161310e565b9060e4356001600160401b03811161151f5761234590369060040161313b565b60049291923586526003602052604086209260043587526004602052604087209463ffffffff600286015460a01c1663ffffffff881603611d5e5761238a338661528d565b9460ff875416600d8114600014612b96575060ff601188015460081c1660ff871603612b87575b6101008214801590612b7c575b612b3b57603460ff6123ce61330d565b1610801590612b67575b8015612b4a575b612b3b57885b60058110612acd575060ff8616612ac55788905b895b600281101561271f5761240d816132d3565b356001808216149081612714575b50156127055761242e60ff821684613354565b818060071b04608014821517156126f157600182018083116126dd57808060071b04608014811517156126dd57908c8b6020838a6124b16124968f8f61248b8f612484908e9460079e8f1b918f87901b91613375565b36916133e3565b9a8b9801549461584b565b60405163af0586a360e01b81529586948594600486016134b2565b03817376a5e1d2e03838dabdce38f3372510ba53cabf085af491821561129757916126bf575b50156126b0576124ea9060088c0161341a565b90549060031b1c60ff8a16156000146126a9578c612507846132d3565b35915b60ff8c1661269857915b612525612520866132fb565b61332d565b60ff604051916349b11d3760e01b8352166004820152828160248173b04c1e546d7cf8dc39e10dfc17148475737908065af490811561268d578391612673575b5061258460405194859384936340604be760e01b85526004850161354f565b038173b04c1e546d7cf8dc39e10dfc17148475737908065af4908115612668578d91612646575b5060208151910120906020815191012003612637576125c9816132d3565b35600289101561262357906001916125eb6119208360128e8e881b0101613571565b905561261d8a8d61260b8c6016612604612520886132fb565b9401613571565b509050601f8416908460051c01613581565b016123fb565b634e487b7160e01b8c52603260045260248cfd5b6399f4b04160e01b8b5260048bfd5b61266291503d808f833e61265a818361329f565b8101906134ee565b386125ab565b6040513d8f823e3d90fd5b61268791503d8085833e61265a818361329f565b38612565565b6040513d85823e3d90fd5b506126a2846132d3565b3591612514565b8c8161250a565b637ca55c7760e01b8d5260048dfd5b6126d7915060203d811161129057611282818361329f565b386124d7565b634e487b7160e01b8d52601160045260248dfd5b634e487b7160e01b8c52601160045260248cfd5b63284f145b60e11b8b5260048bfd5b60019150113861241b565b8a888a848d6040519361273360e08661329f565b60e0368637855b60058110612a81575060ff61274d61330d565b1660a086015260ff61275d61331d565b1660c08601526040516342b9d18160e11b81529486600487015b60078210612a685750505060208560e4817385c53cbfc7f3425f5328d05a6732f659f468592a5af4948515612a5d578695612a1c575b5063ffffffff60188501926127cc876127c68587613599565b906135b5565b8160118701976127ed60ff60018188161b1660ff8b5460101c16178a6135d0565b60ff6127f761330d565b8161280061331d565b91816040519916895216602088015216604086015216606084015216907fe656b3a671b1d5b27cba2c8f42d1b11df035c0a6f0fc7ffcb2bfb7f5c51657fd608060043592a382549360ff8516600d0361286d575050815460ff1916600e17825561044192506004356158ad565b5460081c60ff169390929190612882856132c2565b9363ffffffff6128928683613599565b90549060031b1c1663ffffffff6128a98884613599565b90549060031b1c16106000146128c85750506104419350600435615350565b909192936128ee8663ffffffff6128e0819486613599565b90549060031b1c1693613599565b90549060031b1c161160001461290d5750906104419291600435615350565b610441935063ffffffff600384015416906129b461294a63ffffffff61271061294161ffff600189015460b81c16876131d7565b04168094615239565b916129a061298b61296a60ff637fffffff8760011c169460081c166132c2565b6129a661297a600188168661521f565b6129a061298b60018d019485613599565b91909263ffffffff84548460031b1c1661521f565b916135b5565b60ff895460081c1690613599565b6129d76129ce8363ffffffff600287015460c01c1661521f565b6002850161532d565b63ffffffff600284015460a01c169163ffffffff60405192600284521660208301526040820152600080516020615e02833981519152606060043592a3600435615ce6565b9094506020813d602011612a55575b81612a386020938361329f565b81010312610444575163ffffffff811681036104445793866127ad565b3d9150612a2b565b6040513d88823e3d90fd5b60208060019260ff865116815201930191019091612777565b60ff612a9082600d880161333b565b90549060031b1c166007821015612ab157600582901b87015260010161273a565b634e487b7160e01b88526032600452602488fd5b6002906123f9565b60ff612adc82600d8b0161333b565b90549060031b1c1660ff612aee61330d565b16148015612b12575b612b03576001016123e5565b63a554dcdf60e01b8a5260048afd5b5060ff612b2282600d8b0161333b565b90549060031b1c1660ff612b3461331d565b1614612af7565b63a554dcdf60e01b8952600489fd5b50612b5361330d565b60ff80612b5e61331d565b169116146123df565b50603460ff612b7461331d565b1610156123d8565b50600c8414156123be565b630304193d60e11b8952600489fd5b600e036119ff5760ff612bb18160118a015460081c166132c2565b1660ff871603156123b157630304193d60e11b8952600489fd5b503461036e578060031936011261036e5760206040516101f48152f35b503461036e57602036600319011261036e576004358082526003602052604082208183526004602052604083206001820190600260ff835460a01c1603612d5957612c33338461528d565b9160ff825416857f410304baf564e21c104cbc1ab0a0f9c155f9a5119091083c05d2a6e2b0fd54b5602060405160ff88168152a280612c7957505050506104419161552c565b600f819593949514908115612d4e575b5015612c995750610441936157a3565b91909273ecfafd597d0ffe9d9ba32feaa2f12f12f2bb86dc86813b1561036e5760249160405192838092631067a2ef60e11b82528760048301525af4801561199a57612d2a575b5091612d1260ff92612d0a600295612d0563ffffffff888a015460401c1682856152d1565b6132c2565b908688615350565b5460a01c1614612d2157505080f35b6104419161552c565b60ff92612d0a88612d42600297959a612d129561329f565b98939550509250612ce0565b601091501438612c89565b63126ab15560e01b8552600485fd5b5061010036600319011261036e576004356001600160601b03811680820361036c57612d926130d8565b916044359063ffffffff821680920361151f57612dad6130eb565b946084359563ffffffff87169081880361036c5760a4359463ffffffff86169384870361036e5760c4359563ffffffff87169687810361036c5760e4359a60ff8c16998a8d0361151f578b1580156130ca575b80156130b7575b613058576001861080156130a8575b801561308e575b61305857603c88108015613082575b61305857603c89108015613076575b6130585760018a108015613067575b6130585760088b1015613058578b9c9d612e6e63ffffffff849e9d9e16809d6131d7565b3403610fd457600254612e8090613200565b9e8f80600255875260036020526040872095600160a01b600190033316600160a01b6001900319885416178755865490600160a01b60019003199060a01b1690600160a01b60019003161786556001860190600160a01b60ff60a01b1983541617825581549060ff60a81b9060a81b168060ff60a81b19831617835561ffff60b81b60015460181b169063ffffffff60c81b8760c81b169267ffffffffffff00ff60a81b1916171717905560028501938763ffffffff1986541617855584549263ffffffff60801b9060801b169263ffffffff60801b199063ffffffff60201b8b60201b1690600160201b600160801b03191617169063ffffffff60401b9060401b16179063ffffffff60601b9060601b1617178255612f9f9161322f565b6003018763ffffffff19825416179055600160a01b6001900360015416908a815260056020526040902090600160a01b6001900316600160a01b600190031982541617905560015460a01c61ffff16956040519889526020890152604088015263ffffffff166060870152608086015260a085015260c084015260e08301526101008201528133916101207f420317bb06dc9f1eea6d42235fffbfe838d3bef49aba0b479ecb45b332dd6dbb91a3604051908152602090f35b63de17a3af60e01b8552600485fd5b5063ffffffff87168a11612e4a565b5062093a808911612e3b565b5062093a808811612e2c565b5063ffffffff600a818416041663ffffffff881611612e1d565b508563ffffffff881610612e16565b50633b9aca0063ffffffff831611612e07565b5063ffffffff821615612e00565b6024359063ffffffff8216820361198d57565b6064359063ffffffff8216820361198d57565b6044359060ff8216820361198d57565b9181601f8401121561198d578235916001600160401b03831161198d576020838186019501011161198d57565b9181601f8401121561198d578235916001600160401b03831161198d576020808501948460051b01011161198d57565b600435906001600160a01b038216820361198d57565b906000905b6002821061319357505050565b60208060019263ffffffff865116815201930191019091613186565b906000905b600482106131c157505050565b60208060019285518152019301910190916131b4565b818102929181159184041417156131ea57565b634e487b7160e01b600052601160045260246000fd5b60001981146131ea5760010190565b6005111561321957565b634e487b7160e01b600052602160045260246000fd5b80546001600160e01b031660e09290921b6001600160e01b031916919091179055565b61046081019081106001600160401b0382111761326e57604052565b634e487b7160e01b600052604160045260246000fd5b604081019081106001600160401b0382111761326e57604052565b601f909101601f19168101906001600160401b0382119082101761326e57604052565b60ff166001039060ff82116131ea57565b60028110156132e55760051b60440190565b634e487b7160e01b600052603260045260246000fd5b60028110156132e55760051b60840190565b60843560ff8116810361198d5790565b60a43560ff8116810361198d5790565b3560ff8116810361198d5790565b91909160058310156132e557601f908360051c01921690565b9060ff8091169116019060ff82116131ea57565b919082018092116131ea57565b9093929384831161198d57841161198d578101920390565b6001600160401b03811161326e57601f01601f191660200190565b9190916133b5608061338d565b906133c3604051928361329f565b819360808352608082011161198d5781608060a092602060009501370152565b9291926133ef8261338d565b916133fd604051938461329f565b82948184528183011161198d578281602093846000960137010152565b60048210156132e5570190600090565b9081602091031261198d5751801515810361198d5790565b60005b8381106134555750506000910152565b8181015183820152602001613445565b9060209161347e81518092818552858086019101613442565b601f01601f1916010190565b906000905b6006821061349c57505050565b602080600192855181520193019101909161348f565b9493916134ec9360ff6134e4926080948952600460208a01521660408801526101406060880152610140870190613465565b94019061348a565b565b60208183031261198d578051906001600160401b03821161198d570181601f8201121561198d5780516135208161338d565b9261352e604051948561329f565b8184526020828401011161198d5761354c9160208085019101613442565b90565b61356760409295949395606083526060830190613465565b9460208201520152565b60028210156132e5570190600090565b919060ff8084549260031b9316831b921b1916179055565b91909160028310156132e557601c908360031c019260021b1690565b919063ffffffff8084549260031b9316831b921b1916179055565b9062ff000082549160101b169062ff00001916179055565b9081602091031261198d575160ff8116810361198d5790565b929091600084815260036020526040812093858252600460205260408220906001860191600260ff845460a01c160361398e57600287019463ffffffff80875460a01c16931692830361397f57815460ff80821692168214801590613977575b6139685760101c6001600160401b031642111561395957613682338961528d565b9260028214801561394f575b1561378a576001915b60ff831660ff8616811461377b5760408c92600080516020615de28339815191529282519182526020820152a373ecfafd597d0ffe9d9ba32feaa2f12f12f2bb86dc803b15610444578560249160405192838092631067a2ef60e11b82528760048301525af48015612a5d57613759575b506137249063ffffffff61372b9596975460401c1690836152d1565b8688615350565b82613744575b505061373b575050565b6134ec9161552c565b5460a01c60ff16600214915038905080613731565b9063ffffffff8661377161372b97986137249561329f565b9695505090613708565b635f07a36960e11b8852600488fd5b60038203613799578591613697565b600582036137e45760ff84166001036137db5760ff60015b600c8501541616156137cc576137c6846132c2565b91613697565b632a0e78a360e01b8652600486fd5b60ff60026137b1565b600782148015613945575b801561393b575b156138155760ff60118401541660ff8516036137cc576137c6846132c2565b600682148015613931575b8015613927575b801561391d575b156138445760ff600384015460681c1691613697565b600d820361385d5760ff601184015460081c1691613697565b600e8203613878576137c660ff601185015460081c166132c2565b949350600f919796508099989550146000146138ce5760ff166138bf575081600080516020615de283398151915260406134ec9798815190815260016020820152a3615722565b6393687c0b60e01b8152600490fd5b601088929814600014611a555760ff600191160361390e57600080516020615de2833981519152604084926134ec989982519182526020820152a3615670565b6338b72ae960e11b8752600487fd5b50600c821461382e565b50600a8214613827565b5060088214613820565b50600b82146137f6565b50600982146137ef565b506004821461368e565b63d0404f8560e01b8552600485fd5b63301c306b60e01b8652600486fd5b508115613661565b636226556b60e01b8552600485fd5b63126ab15560e01b8452600484fd5b908060209392818452848401376000828201840152601f01601f1916010190565b91908110156132e55760051b0190565b60038210156132e5570190600090565b60ff90911681526001600160401b03909116602082015260400190565b9c9491979a9998959c9b93909b969296610220526101a0526101c0526102a0526101405261016052600061020052610220516102005152600360205260406102005120610240526102205161020051526004602052604061020051206102605263ffffffff600261024051015460a01c1663ffffffff8816036142f75760ff610260515416610280526007610280511415806142e9575b806142db575b6142c657613aa9336102405161528d565b61018052613ab961028051615931565b6101e0529660ff6101e0511683036140f257610200515b60ff6101e05116811061427f575060ff80601161026051015416146141d5575060ff60116102605101541660ff6101805116146141c05760ff6101e051166101c0511480159061419c575b801561416b575b6140f257610200515b60ff6101e051168110613dd25750506102605160118101805460ff191660ff17905561020051600e8201819055600f8201819055601090910155505061028051600714159450613d8d93505050505761026051600301805460ff60601b1916600160601b1790555b63ffffffff600261024051015460a01c1660ff6003610260510191825490604051838360601c168152613bcf60208201600d610260510161493a565b7ff6c035c10ac1e9a6d54a4228a066116b018fcb4d11df171c9a55665c47de523760c06102205192a360881c1615613ca25761028051600703613c31575061026051805460ff191660091790555b6134ec6102605161024051610220516158ad565b61028051600903613c51575061026051805460ff1916600b179055613c1d565b61026051805460ff1916600d1790555460981c60ff908116908114613c8657613c81905b6011610260510161591b565b613c1d565b50613c81613c9d60ff610260515460081c166132c2565b613c75565b5061028051600703613d5a5761026051805460ff191660081790555b610240516002015460201c63ffffffff1673ecfafd597d0ffe9d9ba32feaa2f12f12f2bb86dc803b15613d53576040519163441271fb60e11b835261026051600484015260248301528160448161020051935af48015613d4557613d32575b506134ec6102605161024051610220516158e4565b61020051613d3f9161329f565b38613d1d565b6040513d61020051823e3d90fd5b6102005180fd5b61028051600903613d795761026051805460ff1916600a179055613cbe565b61026051805460ff1916600c179055613cbe565b61028051600903613db55761026051600301805460ff60601b1916600160611b179055613b93565b61026051600301805460ff60601b1916600360601b179055613b93565b613ddf60ff821689613354565b90808060071b0460801481151715613fe15760018101808211613fe157808060071b0460801481151715613fe157612484613e269160071b8360071b8d6102a05190613375565b806020848b613e41612496878a60076102605101549461584b565b03817376a5e1d2e03838dabdce38f3372510ba53cabf085af4908115613d4557610200519161414d575b50156141385761020051610120526101805160ff1661411857613e8f8286886139be565b35610120525b6101805160ff1661410757613eb082600e61026051016139ce565b90549060031b1c5b603460ff613ed0612520866101c0516101a0516139be565b16101561404c57613eeb612520846101c0516101a0516139be565b9060ff604051926349b11d3760e01b8452166004830152610200518260248173b04c1e546d7cf8dc39e10dfc17148475737908065af4908115613d4557613f5b92610200519261402d575b50604051809381926340604be760e01b8352610200519461012051906004850161354f565b038173b04c1e546d7cf8dc39e10dfc17148475737908065af4908115613d45576102005191614010575b5060208151910120906020815191012003613ffb57613fae612520826101c0516101a0516139be565b9160ff90811660031901908111613fe157600192613fd5613fdb92600d610260510161333b565b90613581565b01613b2b565b634e487b7160e01b61020051526011600452602461020051fd5b6399f4b04160e01b6102005152600461020051fd5b61402791503d8061020051833e61265a818361329f565b38613f85565b6140459192503d8061020051833e61265a818361329f565b9038613f36565b99509496925092949650975060ff8061406f612520856101c0516101a0516139be565b16036140f25760349561409961408b846140b29760ff996139be565b3593600e61026051016139ce565b94909361016051956101405195549060031b1c91615981565b1610156140d35750506134ec61018051610260516102405161022051615c2a565b6134ec9161012051906101805190610260516102405161022051615b97565b63a554dcdf60e01b6102005152600461020051fd5b6141128286886139be565b35613eb8565b61412882600e61026051016139ce565b90549060031b1c61012052613e95565b637ca55c7760e01b6102005152600461020051fd5b614165915060203d811161129057611282818361329f565b38613e6b565b50617f806101e05160071b1660ff6101e05116810460801460ff6101e05116151715613fe1576102a0511415613b22565b506101e05160ff1660068181029180159083049091141715613fe157811415613b1b565b63e9f155db60e01b6102005152600461020051fd5b9750509350935050506102005190600e6102605101915b60ff6101e05116811061425b575050505060ff610180511660ff1960116102605101541617601161026051015560018060401b03610260515460101c16600080516020615e6283398151915263ffffffff60405193169280614256610220519461028051836139de565b0390a3565b8061426960019284876139be565b3561427761192083876139ce565b9055016141ec565b61428a8185876139be565b3560018082161490816142bb575b50156142a657600101613ad0565b63284f145b60e11b6102005152600461020051fd5b600191501138614298565b63301c306b60e01b6102005152600461020051fd5b50600b610280511415613a98565b506009610280511415613a92565b636226556b60e01b6102005152600461020051fd5b8054600160201b600160601b03191660209290921b600160201b600160601b0316919091179055565b92919061434c602091604086526040860190613465565b930152565b95949193909486600052600360205260406000209487600052600460205260406000209663ffffffff80600289015460a01c1691160361487357601060ff885416036148625785546001600160a01b031633036148515760ff169360348510801590614834575b8015614817575b6147ac5761020082036147ac578160801161198d576143de36856133a8565b826101001161198d576143f436608087016133a8565b92806101801161198d5761440c3661010088016133a8565b916020815191012095601e8a019687541491821592614802575b82156147ec575b82156147bd575b50506147ac576040516349b11d3760e01b81526004810187905273b04c1e546d7cf8dc39e10dfc1714847573790806956000826024818a5af48015614667578460209161449f94600091614791575b50604051630f237ed160e21b8152948592839260048401614335565b03818a5af49182156146675760009261475d575b50540361474e57604051630f237ed160e21b81529291602091849182916144de919060048401614335565b0381875af4908115614667578392600092614715575b50604051630f237ed160e21b81529260209184918291614518919060048401614335565b0381875af4918215614667576000926146e1575b50036146d5576040516349b11d3760e01b81526004810184905290600082602481865af4918215614667576000926146b6575b50601d8601546040516340604be760e01b8152926000928492839261458892906004850161354f565b0381855af49081156146675760009161469b575b506020815191012091602185019283540361468f57604051906349b11d3760e01b82526004820152600081602481855af480156146675761460a926000928392614673575b50601c870154601b8801546040516340604be760e01b815295869485938493916004850161354f565b03915af49081156146675760009161464c575b506020815191012090540361464357906134ec929160ff601983015460101c16926157a3565b6134ec92615670565b61466191503d806000833e61265a818361329f565b3861461d565b6040513d6000823e3d90fd5b6146889192503d8085833e61265a818361329f565b90386145e1565b5050506134ec92615722565b6146b091503d806000833e61265a818361329f565b3861459c565b60009192506146ce903d8084833e61265a818361329f565b919061455f565b5050506134ec92615670565b9091506020813d60201161470d575b816146fd6020938361329f565b8101031261198d5751903861452c565b3d91506146f0565b925090506020823d602011614746575b816147326020938361329f565b8101031261198d57905182916145186144f4565b3d9150614725565b5050505050506134ec92615670565b9091506020813d602011614789575b816147796020938361329f565b8101031261198d575190386144b3565b3d915061476c565b6147a691503d806000833e61265a818361329f565b38614483565b63a554dcdf60e01b60005260046000fd5b9091506102001161198d576147d7906101803691016133a8565b60208151910120602189015414153880614434565b91508251602084012060208b015414159161442d565b85516020870120601f8c015414159250614426565b506001808416148061482a575b156143bf565b5060018311614824565b5060018082161480614847575b156143b8565b5060018111614841565b6393687c0b60e01b60005260046000fd5b63301c306b60e01b60005260046000fd5b636226556b60e01b60005260046000fd5b6001600160401b03811161326e5760051b60200190565b906148a582614884565b6148b2604051918261329f565b82815280926148c3601f1991614884565b0190602036910137565b80518210156132e55760209160051b010190565b9063ffffffff6040519254818116845260201c1660208301526134ec60408361329f565b60405191906000835b60048210614924575050506134ec60808361329f565b600160208192855481520193019101909161490e565b60ff608091548181168452818160081c166020850152818160101c166040850152818160181c16606085015260201c16910152565b939096919497929895986101e05260e0526102405261026052600061028052806102805152600360205260406102805120948161028051526004602052604061028051209663ffffffff600288015460a01c1663ffffffff8716036151d957600f60ff895416036151c45760018701546001600160a01b031633036151af57603460ff85161080159061518c575b801561516b575b61514857610200811480159061515d575b6151485760ff6019890154166080528060801161514157614a3636836133a8565b61010052806101001161514157614a5036608084016133a8565b6101c052806101801161514157614a6b3661010084016133a8565b6101a0526102001161514157614a86906101803691016133a8565b60048701546102a05260c0808052604051610220819052919591614aaa919061329f565b60c0516102205136903761028051610200525b6006610200511015614b2d5761028051614b13576102005150600061028052614af06102005161026051610240516139be565b3561028051506102005160051b6102205101526001610200510161020052614abd565b634e487b7160e01b61028051526011600452602461028051fd5b60405163af0586a360e01b81526102a05160048201526001602482015260ff84166044820152610140606482015260208180614b70610144820161010051613465565b614b80608483016102205161348a565b03817376a5e1d2e03838dabdce38f3372510ba53cabf085af490811561502c576102805191615122575b50156150b05760058701546101605260c051604051610180819052614bcf919061329f565b60c05161018051369037610280515b6006811015614c205761028051614b13578060060190816006116131ea57614c0f60019261026051610240516139be565b358160051b61018051015201614bde565b5090919293949560405163af0586a360e01b8152610160516004820152600260248201526080516044820152610140606482015260208180614c6961014482016101c051613465565b614c79608483016101805161348a565b03817376a5e1d2e03838dabdce38f3372510ba53cabf085af490811561502c576102805191615103575b50156150b05760068101546101205260c051604051610140819052614cc8919061329f565b60c05161014051369037610280515b6006811015614d195761028051614b135780600c019081600c116131ea57614d0860019261026051610240516139be565b358160051b61014051015201614cd7565b5090919293949560405163af0586a360e01b8152610120516004820152600360248201526080516044820152610140606482015260208180614d6261014482016101a051613465565b614d72608483016101405161348a565b03817376a5e1d2e03838dabdce38f3372510ba53cabf085af490811561502c5761028051916150e4575b50156150b057600782015460c05160405160a0819052919891614dbf919061329f565b60c05160a051369037610280515b6006811015614e0e5761028051614b13578060120190816012116131ea57614dfe60019261026051610240516139be565b358160051b60a051015201614dcd565b50909192939495966020614e3f916040518093819263af0586a360e01b83528660a0519160805190600486016134b2565b03817376a5e1d2e03838dabdce38f3372510ba53cabf085af490811561502c5761028051916150c5575b50156150b05773b04c1e546d7cf8dc39e10dfc171484757379080660405190630f237ed160e21b825260208280614eaa6101e0516101005160048401614335565b0381845af491821561502c57610280519261507c575b506101c051519160206101c051019283200361506c57604051630f237ed160e21b815260208180614ef86101e0518860048401614335565b0381855af490811561502c57610280519161503a575b5060206040518093630f237ed160e21b82528180614f3560e0516101a05160048401614335565b03915af491821561502c576102805192614ff8575b5003614fe9576101e051601a85015560e051601b850155610100518051602091820120601e8601556101c05151909120601f8501556101a051805190820120848201558151918101919091206021840155825460ff191660101783557f1b2ab5b594fd7d4b1494f908fa143ff6f2f9879b399e1d17f68ec54f44241b8f93909263ffffffff92614fdb9190876158ad565b60ff604051961686521693a3565b50509150916134ec9350615722565b9091506020813d602011615024575b816150146020938361329f565b8101031261198d57519038614f4a565b3d9150615007565b6040513d61028051823e3d90fd5b90506020813d602011615064575b816150556020938361329f565b8101031261198d575138614f0e565b3d9150615048565b5050509150916134ec9350615722565b9091506020813d6020116150a8575b816150986020938361329f565b8101031261198d57519038614ec0565b3d915061508b565b637ca55c7760e01b6102805152600461028051fd5b6150de915060203d60201161129057611282818361329f565b38614e69565b6150fd915060203d60201161129057611282818361329f565b38614d9c565b61511c915060203d60201161129057611282818361329f565b38614ca3565b61513b915060203d60201161129057611282818361329f565b38614baa565b6102805180fd5b63a554dcdf60e01b6102805152600461028051fd5b506018610260511415614a15565b5060018060e051161480615180575b15614a04565b50600160e0511161517a565b506001806101e0511614806151a2575b156149fd565b5060016101e0511161519c565b6338b72ae960e11b6102805152600461028051fd5b63301c306b60e01b6102805152600461028051fd5b636226556b60e01b6102805152600461028051fd5b6001600160401b0391821690821601919082116131ea57565b63ffffffff60019116019063ffffffff82116131ea57565b9063ffffffff8091169116019063ffffffff82116131ea57565b9063ffffffff8091169116039063ffffffff82116131ea57565b63ffffffff91821681529116602082015260400190565b805463ffffffff60a01b191660a09290921b63ffffffff60a01b16919091179055565b80546001600160a01b03928316921682146152ca57600101546001600160a01b0316146152c55763abca351760e01b60005260046000fd5b600190565b5050600090565b61298b6134ec9361532761530360016129a095019263ffffffff6152f58886613599565b90549060031b1c1690615cc6565b94612d056153118285613599565b6129a08963ffffffff84548460031b1c16615239565b90613599565b805463ffffffff60c01b191660c09290921b63ffffffff60c01b16919091179055565b6134ec9381600080516020615e02833981519152606063ffffffff6003880154169361539b63ffffffff61271061539261ffff60018c015460b81c16896131d7565b04168096615239565b946153c26153ac8360018c01613599565b6129a08963ffffffff84548460031b1c1661521f565b63ffffffff8060028a016153e56153df8584845460c01c1661521f565b8261532d565b5460a01c169660ff604051941684521660208301526040820152a3615ce6565b818110615410575050565b60008155600101615405565b60048101905b81811061542d575050565b60008155600101615422565b6000815561544d6002820160018301615405565b61545d6003820160028301615405565b600060038201556154706004820161541c565b6000600c82016154838160088501615405565b556000600e820161549781600d8501615405565b6154a5601184018092615405565b5560168101601282015b818110615517575060188201905b81811061550257506134ec916154d9601e926019830190615405565b600060198201556000601a8201556000601b8201556000601c8201556000601d8201550161541c565b80615511600180930182615405565b016154bd565b80615526600280930182615405565b016154af565b60018201805460ff60a01b1916600360a01b1781558254600284015492937ffabca260174d01cb14c0ba7f3a1678d899165a405e9f9781f2d21cc5ab7bdab8936060939192916155a760a084901c63ffffffff61559d828260036155948360e08b901c6131d7565b9a0154166131d7565b9460c01c166131d7565b9284615648575b508161561b575b50816155e5575b8560005260046020526155d26040600020615439565b60405192835260208301526040820152a2565b85600052600560205260018060a01b036040600020541660005260066020526040600020615614838254613368565b90556155bc565b546001600160a01b031660009081526006602052604090208054615640908390613368565b9055386155b5565b60018060a01b031660005260066020526040600020615668858254613368565b9055386155ae565b80600080516020615e2283398151915260206134ec956156dc600182019163ffffffff60036156d26156b1600096848082541691546004861b1c169061521f565b836156c46002870192828454169061521f565b91546004851b1c169061521f565b920154169061521f565b6002870160018060e01b03815416905563ffffffff60038801911663ffffffff198254161790558381526004825261571660408220615439565b604051908152a261552c565b61575f6134ec93615754600182019163ffffffff60036156d26156b1600096848082541691546004861b1c169061521f565b90506002840161322f565b6003820163ffffffff1981541690558060005260046020526157846040600020615439565b80600080516020615e22833981519152602060405160018152a261552c565b600080516020615e2283398151915260206134ec95615716604085969760ff6157ed600183019263ffffffff60036156d26156b1600097848082541691546004861b1c169061521f565b9416938461582a5760028a0160018060e01b03815416905563ffffffff60038b01911663ffffffff198254161790555b8681526004855220615439565b6158379060028b0161322f565b60038901805463ffffffff1916905561581d565b6040519193929060c061585e818561329f565b3684378260005b60068110156158a5576006840290848204600614851517156131ea5761589761589082600194613368565b89866139be565b358160051b87015201615865565b509450505050565b9060ff6002600080516020615e6283398151915292019361425663ffffffff611c8c81885460801c1660018060401b0342166151ee565b9060ff6002600080516020615e6283398151915292019361425663ffffffff611c8c81885460601c1660018060401b0342166151ee565b9061ff0082549160081b169061ff001916179055565b60ff16600781146159535760091461594b57600890600190565b600790600190565b50600490600390565b9161597361354c9492604085526040850190613465565b92602081850391015261399d565b9092919593949560808614801590615b8c575b6147ac5760006159d09573b04c1e546d7cf8dc39e10dfc171484757379080698604051978892839263d325c5b160e01b8452876004850161595c565b03818a5af494851561466757600095615b6a575b506000615a06916040518093819263631ec63f60e01b83528960048401614335565b03818a5af490811561466757600091615b4f575b5060208151910120906020815191012003615b005760405163d325c5b160e01b81529360009185918291615a539190876004850161595c565b0381875af492831561466757600093615b2c575b506000615a89916040518093819263631ec63f60e01b83528760048401614335565b0381875af490811561466757600091615b11575b5060208151910120906020815191012003615b0057806020809251910120602460405180948193633b8c1f2d60e21b835260048301525af490811561466757600091615ae7575090565b61354c915060203d60201161217657612163818361329f565b638ba09fd960e01b60005260046000fd5b615b2691503d806000833e61265a818361329f565b38615a9d565b615a89919350615b476000913d8084833e61265a818361329f565b939150615a67565b615b6491503d806000833e61265a818361329f565b38615a1a565b615b84600091615a0693973d8091833e61265a818361329f565b9591506159e4565b506080851415615994565b94600260ff92615c0b606096957fd3060bf93343487b796703319013dda402debbb3819e37ca26f987571ca09805989a63ffffffff95615bf08d601985018a8c168b1982541617815561ff0080198254161781556135d0565b601c830155601d820155600f8619825416178155828a6158ad565b015460a01c1695816040519316835216602082015260ff6040820152a3565b60038301805460ff60901b1916609086901b60ff60901b16179055600094939092919073ecfafd597d0ffe9d9ba32feaa2f12f12f2bb86dc86813b1561036e5760249160405192838092631067a2ef60e11b82528860048301525af4801561199a579661207892916134ec9798615cb6575b5050612d0563ffffffff600285015460401c1682866152d1565b81615cc09161329f565b38615c9c565b9063ffffffff811663ffffffff831610600014615ce1575090565b905090565b9160010160009063ffffffff808083541692615d0660028701948561322f565b54600460031b1c1691816003860193168219845416178355615d5c604083835460a01c1695615d3d615d3788615207565b8561526a565b615d4e600180841b0342168761430c565b888152600460205220615439565b5460e01c9154169184600080516020615da283398151915260405180615d83878783615253565b0390a315908115615d98575b5061373b575050565b90501538615d8f56fe59d3350cb221a36e23b483076e38f2c093b609b0aebdfdeb03d0512d7d5a2315134e07fe001e0bc82a08476763b074f743a6d3b7aa79f27bf2deff8f576129c66d007a5bc0a881f142027c9c5a95f04f9f8ec48e7f9c4b538aef7ac8d8397ede30e1dec6db2d80ec3e362df6665049ac592e8cc98d7b236d0c61681304da81340cd5cc6eac0dc3c714a186b81c618ab602c9b02830ba885dc7af3e76dbfc5c349b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f008e0405abb6bd265a8563b4e6fb2118d701c6055b85e736e6b75e062b5d2e0f28a2646970667358221220ffcb69456a0b24ba9cd329cc2d2b2d5233221b9e3a8286b72c65191b8641030664736f6c634300081c00330000000000000000000000000000000000000000000000000000000000000064000000000000000000000000fcafcc30cdfcda7b95e8466a8776d55b9c9963aa
Deployed ByteCode
0x6102c0604052600436101561001357600080fd5b6000803560e01c806301bf32dc14612d6857806308ce201114612be8578063102672ba14612bcb5780631162384c146122db57806317cf8c0d146122bc57806321c9c44a14612297578063264f5c01146121bb57806327e5e0c114611f0e5780633ccfd60b14611e0b5780633f637e8614611dd05780634155fc5b14611db2578063543ad1df14611d9657806365e17c9d14611d6d578063668fd78214611b1a578063704b25271461168857806370a4e95b1461165557806371671bf214611523578063763c8cd91461142457806381c908581461133a578063898acd1f146110285780638da5cb5b146110015780639492264514610ee4578063b3a5c8c814610e8c578063ce513b6f14610e53578063d1e9e14014610d39578063d47f269e14610547578063d924cb931461046a578063de38eb3a1461044c578063dfa170e7146103c9578063f2fde38b146103715763f42452581461017357600080fd5b3461036e57604036600319011261036e57600435602435801515810361036c5781835260036020526040832090828452600460205260408420600183015490600260ff8360a01c160361035d575460ff1661034e576001600160a01b0316330361033f576003820180546002840180549092916001600160401b039061020990608084901c63ffffffff169060201c83166151ee565b1642111561033057906102f36102ed7f50038a922c31e04bdb655c3d957330398521774f6656bf130c3fcb3303102d1d602089600080516020615da28339815191528761028b61028561027e61027363ffffffff6103039d60401c1663ffffffff868a1c1661521f565b9360e01c8094615cc6565b8093615239565b8b61322f565b63ffffffff61029d82828b541661521f565b1663ffffffff198954161788558954908363ffffffff8360a01c16968796879363ffffffff60405191168152a363ffffffff885416906102e560405192839260e01c83615253565b0390a3615207565b8461526a565b426001600160401b03169061430c565b5460e01c15908115610328575b50610319578280f35b6103229161552c565b38808280f35b905038610310565b63d0404f8560e01b8752600487fd5b6338b72ae960e11b8452600484fd5b63301c306b60e01b8552600485fd5b63126ab15560e01b8652600486fd5b825b80fd5b503461036e57602036600319011261036e5761038b61316b565b8154906001600160a01b03821633036103ba576001600160a01b03166001600160a01b03199190911617815580f35b6330cd747160e01b8352600483fd5b503461036e5760e036600319011261036e576103e36130d8565b6103eb6130fe565b60a4356001600160401b0381116104485761040a90369060040161310e565b60c43593916001600160401b0385116104445761042e61044195369060040161313b565b949093608435916064359160043561496f565b80f35b8580fd5b8380fd5b503461036e578060031936011261036e57602060405162093a808152f35b503461036e57604036600319011261036e5760043561ffff81169081810361036c576024356001600160a01b03811691908290036104485783546001600160a01b03163303610538576101f4831161052957811561051a57600180546001600160b01b03191660a09290921b61ffff60a01b1691909117821790556040805192835260208301919091527fb5add0bb8de8016e167bf0c471fb32731f765637e15e0156d13ee6853f6a064391a180f35b63de17a3af60e01b8452600484fd5b63c6c3513b60e01b8452600484fd5b6330cd747160e01b8452600484fd5b503461036e57602036600319011261036e5760405161056581613252565b8181528160208201528160408201526040918251610583848261329f565b8336823760608301528251610598848261329f565b8336823760808301528060a08301528060c08301528060e08301528061010083015280610120830152806101408301528061016083015280610180830152806101a0830152806101c0830152806101e083015260809280516105fa858261329f565b843682376102008401528051610610858261329f565b843682376102208401528161024084015260a0815161062f828261329f565b813682376102608501526060938251610648868261329f565b85368237610280820152836102a0820152836102c0820152836102e08201528251610673848261329f565b845b848110610d1c5750610300820152825161068f848261329f565b845b848110610cff575061032082015282516106ab848261329f565b833682376103408201528361036082015283610380820152836103a0820152836103c0820152836103e082015283610400820152836104208201526104408351916106f6888461329f565b873684370152600435835260046020528183209482519561071687613252565b805460ff8082168952600882901c16602089015260101c6001600160401b031684880152610746600182016148e1565b86880152610756600282016148e1565b8288015260ff600382015463ffffffff8116858a015263ffffffff8160201c1660c08a015263ffffffff81871c1660e08a01528181891c166101008a0152818160681c166101208a0152818160701c166101408a0152818160781c1615156101608a01528181851c1615156101808a0152818160881c1615156101a08a0152818160901c166101c08a015260981c166101e08801526107f760048201614905565b61020088015283516008820186825b60048210610ce95750505061081b838261329f565b61022088015260ff600c82015416610240880152835161083e81600d840161493a565b610848848261329f565b6102608801528351600e820186825b60038210610cd35750505061086c878261329f565b61028088015260ff60118201548181166102a08a0152818160081c166102c08a015260101c166102e088015283516108a381613284565b6012820186825b60028210610c8c5750505061030088015283516108c681613284565b6016820186825b60028210610c5b575050509161095b601e6109a49363ffffffff96956103208c01526108fb601882016148e1565b6103408c015260ff60198201548c6103608383169101528c610380838360081c1691015260101c166103a08c0152601a8101546103c08c0152601b8101546103e08c0152601c8101546104008c0152601d8101546104208c015201614905565b61044089015261099885519760ff8a5116895260ff60208b01511660208a015260018060401b03878b015116878a0152808a015190890190613181565b87015182870190613181565b8501511660e084015263ffffffff60c08501511661010084015263ffffffff60e08501511661012084015260ff6101008501511661014084015260ff6101208501511661016084015260ff6101408501511661018084015261016084015115156101a084015261018084015115156101c08401526101a084015115156101e084015260ff6101c08501511661020084015260ff6101e085015116610220840152610a586102008501516102408501906131af565b610220840151826102c085015b60048210610c455750505061024084015160ff166103408401526102608401518261036085015b60058210610c2c575050506102808401518261040085015b60038210610c165750505060ff6102a08501511661046084015260ff6102c08501511661048084015260ff6102e0850151166104a08401526103008401516104c0840183905b60028210610bd85750505061032084015190829061054085015b60028310610b975761076086610b956104408a610b2b6103408201516105c0860190613181565b60ff6103608201511661060085015260ff6103808201511661062085015260ff6103a0820151166106408501526103c08101516106608501526103e08101516106808501526104008101516106a08501526104208101516106c085015201516106e08301906131af565bf35b818451829087915b60028310610bbc5750505060209060019201940192019192610b04565b815160ff16815285935060019092019160209182019101610b9f565b838351829087915b60028310610bfd5750505060209060019201930191019091610aea565b8151815287935060019092019160209182019101610be0565b6020806001928551815201930191019091610aa4565b60208060019260ff865116815201930191019091610a8c565b6020806001928551815201930191019091610a65565b6001602081928a5160ff8754818116835260081c1683820152610c7e8c8261329f565b8152019301910190916108cd565b87518984825b60028310610cbd57505050602060019282610caf8c60029561329f565b8152019301910190916108aa565b6001602081928454815201920192019190610c92565b6001602081928554815201930191019091610857565b6001602081928554815201930191019091610806565b6020908551610d0e878261329f565b863682378184015201610691565b6020908551610d2b878261329f565b863682378184015201610675565b503461036e57602036600319011261036e576004359080610d598361489b565b906002545b600181101580610e4a575b15610dc8578084526003602052600160ff8160408720015460a01c1614610dae575b8015610d9a5760001901610d5e565b634e487b7160e01b84526011600452602484fd5b80610dc2610dbb84613200565b93856148cd565b52610d8b565b50908290610dd58361489b565b92825b818110610e2657505050604051918291602083016020845282518091526020604085019301915b818110610e0d575050500390f35b8251845285945060209384019390920191600101610dff565b80610e356001928597956148cd565b51610e4082866148cd565b5201939193610dd8565b50848210610d69565b503461036e57602036600319011261036e576020906040906001600160a01b03610e7b61316b565b168152600683522054604051908152f35b503461036e5760c036600319011261036e57610ea66130d8565b610eae6130fe565b60a435916001600160401b03831161044857610ed161044193369060040161310e565b9290916084359160643591600435614351565b50604036600319011261036e576004356024359060ff821691828103610448578184526003602052604084209060018201908154600160ff8260a01c1603610ff257835490336001600160a01b03831614610fe3576008871015610fe3579063ffffffff610f599260c81c169060a01c6131d7565b3403610fd457815462ff00ff60a01b193316600161ff0160a81b03199091161760b09190911b60ff60b01b1617600160a11b179055610fa590426001600160401b03169060030161430c565b6040519182527f396423bd1651bba635e3419ce325aa401229452a6b2be1768843d2e8dea8177860203393a380f35b634366b25d60e01b8652600486fd5b63de17a3af60e01b8852600488fd5b631bb5f5b360e31b8752600487fd5b503461036e578060031936011261036e57546040516001600160a01b039091168152602090f35b503461036e576101a036600319011261036e576004356110466130d8565b61104e6130fe565b906064356084356001600160401b0381116104445761107190369060040161310e565b9093366101641161133657610164356001600160401b0381116113325761109c90369060040161310e565b9095909390610184356001600160401b03811161132e576110c190369060040161310e565b90898b52600360205260408b20968a8c52600460205260408c209863ffffffff8060028b015460a01c1691160361131f5760ff89541660068110908115611314575b5061130557611112338961528d565b9960ff8b169788159586806112f8575b80156112cb575b156112bc57600180891614806112b2575b156112a3578e60078d01546040519063af0586a360e01b825260048201526004602482015260ff8b1660448201526101406064820152602081806111836101448201888861399d565b60c060a4608484013703817376a5e1d2e03838dabdce38f3372510ba53cabf085af49182156112975791611268575b5015611259579360ff936111ee938a9897938a8f6034996111da60086111e993019d8e61341a565b90549060031b1c9336916133e3565b615981565b16101561120357505050505061044193615c2a565b9261044198979592918195979460001461124057600183985b0361122957505095615b97565b611233925061341a565b90549060031b1c95615b97565b600161124c838361341a565b90549060031b1c9861121c565b637ca55c7760e01b8f5260048ffd5b61128a915060203d602011611290575b611282818361329f565b81019061342a565b386111b2565b503d611278565b604051903d90823e3d90fd5b63284f145b60e11b8f5260048ffd5b506001881161113a565b631749e6cd60e11b8f5260048ffd5b5060018a148015611129575060ff8916600281149081156112ed575b50611129565b6003915014386112e7565b50600260ff8a1610611122565b63301c306b60e01b8c5260048cfd5b600e91501138611103565b636226556b60e01b8c5260048cfd5b8980fd5b8780fd5b8680fd5b503461036e57604036600319011261036e57600435906113586130d8565b8282526003602052604082208383526004602052604083209063ffffffff80600283015460a01c16931692830361141557600e60ff835416036114065761139f338261528d565b9460ff601184015460081c169360ff806113b8876132c2565b1697169687036113f757817f50295138b8e123fbfba534eabbb8054c406902fa7aadac163b85767230452e7e60206104419899604051908152a3615350565b630304193d60e11b8652600486fd5b63301c306b60e01b8452600484fd5b636226556b60e01b8452600484fd5b503461036e5761010036600319011261036e5761143f6130d8565b6044356001600160401b03811161036c5761145e90369060040161313b565b916064356001600160401b03811161151f5761147e90369060040161313b565b906084356001600160401b0381116113365761149e90369060040161310e565b9060a4356001600160401b03811161151b576114be90369060040161313b565b92909160c4356001600160401b038111611517576114e090369060040161310e565b60e4359a90979196906001600160401b038c11611513576115086104419c369060040161310e565b9b909a6004356139fb565b8c80fd5b8a80fd5b8880fd5b8480fd5b503461036e57602036600319011261036e57604061024091600435815260036020522080549060018101549063ffffffff60036002830154920154926040519460018060a01b038116865260a01c602086015260018060a01b038116604086015260ff8160a01c16606086015260ff8160a81c16608086015260ff8160b01c1660a086015261ffff8160b81c1660c086015260c81c1660e084015263ffffffff811661010084015263ffffffff8160201c1661012084015263ffffffff8160401c1661014084015263ffffffff8160601c1661016084015263ffffffff8160801c1661018084015263ffffffff8160a01c166101a084015263ffffffff8160c01c166101c084015260e01c6101e083015263ffffffff811661020083015260018060401b039060201c16610220820152f35b503461036e57602036600319011261036e57602090600435815260058252604060018060a01b0391205416604051908152f35b503461036e57608036600319011261036e576004356116a56130d8565b906116ae6130fe565b6064356001600160401b03811161151f576116cd90369060040161310e565b838652600360205260408620848752600460205260408720916001820194855495600260ff8860a01c1603611b0b57600284019163ffffffff80845460a01c169a16998a03611afc5760ff169660018803611a1d57505082546001600160a01b03163303611a0e5760ff8454166119ff57805460e01c801580156119ed575b6119de576117769061176360018701918c836135b5565b600463ffffffff600387015416916135b5565b8873ecfafd597d0ffe9d9ba32feaa2f12f12f2bb86dc8254813b1561036c5763ffffffff60848492604051948593849263074bdbe560e01b84528c600485015260018160a01c166024850152818116604485015260201c1660648301525af480156119ba576119c9575b505063ffffffff905460a01c16867fbfeb6d84abeccf1947b6157b7ac00b04d40817b53e2440dcfa49f712fa234810602060ff875460081c16604051908152a3825460ff191660021783555b8773b04c1e546d7cf8dc39e10dfc1714847573790806803b156119c557816040518092626a007d60e01b825260206004830152818061186f60248201898d61399d565b03915af480156119ba576119a5575b50506118aa93602091604051958692839263b65c02d360e01b845260406004850152604484019161399d565b87602483015203817376a5e1d2e03838dabdce38f3372510ba53cabf085af492831561199a578793611961575b50600019840160ff811161194d5782849260409561193761192061193f9560047ff4169c826e1cd0db9f677dc2ffe40cc7cae264e9ecb54b4cc220cff5b82bbfc69b990161341a565b819391549060031b91821b91600019901b19161790565b9055876158ad565b82519182526020820152a380f35b634e487b7160e01b88526011600452602488fd5b9092506020813d602011611992575b8161197d6020938361329f565b8101031261198d575191386118d7565b600080fd5b3d9150611970565b6040513d89823e3d90fd5b816119af9161329f565b61133257873861187e565b6040513d84823e3d90fd5b5080fd5b816119d39161329f565b61151b5788386117e0565b6309107e2560e31b8a5260048afd5b5063ffffffff6003850154161561174c565b63301c306b60e01b8952600489fd5b6393687c0b60e01b8952600489fd5b90915060028703611a735750546001600160a01b03163303611a6457600260ff84541603611a5557825460ff1916600317835561182c565b63301c306b60e01b8852600488fd5b6338b72ae960e11b8852600488fd5b905060038603611aba575081546001600160a01b03163303611aab57600360ff84541603611a5557825460ff1916600417835561182c565b6393687c0b60e01b8852600488fd5b60048603611aed576001600160a01b03163303611a6457600460ff84541603611a5557825460ff1916600517835561182c565b63de17a3af60e01b8952600489fd5b636226556b60e01b8b5260048bfd5b63126ab15560e01b8a5260048afd5b503461036e57608036600319011261036e57600435611b376130d8565b90604435606435908285526003602052604085209383865260046020526040862092600286019563ffffffff80885460a01c169316928303611d5e57600560ff86541603611a555760018085161480611d54575b158015611d37575b611d2857611ba460ff91339061528d565b1690600182149384600014611d20576001945b600c870195865460ff82821616611d11579460ff96948a947fe47a1a70cd8e25e08c403d1b29c6e7f3f5625c8899a042b3d6c0971ebf8b4cad94896020958160039d9b1617168a198a5416178955600014611d025760088b015560098a01555b604051908152a3541614611c29578380f35b60ff81816003600080516020615e62833981519152940181815460881c16600014611cfa578160075b1682198454161783555460881c1615600014611ce257611cd863ffffffff611c8c8180895460601c165b16426001600160401b03166151ee565b835462010000600160501b03198116601092831b62010000600160501b03161794859055975460405160a09190911c92909216979194859490911c6001600160401b03169116836139de565b0390a33880808380f35b611cd863ffffffff611c8c8180895460801c16611c7c565b816006611c52565b600a8b0155600b8a0155611c17565b63e9f155db60e01b8c5260048cfd5b600294611bb7565b63284f145b60e11b8852600488fd5b5060018083161480611d4a575b15611b93565b5060018211611d44565b5060018411611b8b565b636226556b60e01b8852600488fd5b503461036e578060031936011261036e576001546040516001600160a01b039091168152602090f35b503461036e578060031936011261036e576020604051603c8152f35b503461036e578060031936011261036e576020600254604051908152f35b503461036e57608036600319011261036e57611dea6130d8565b611df26130fe565b6064359081151582036104485761044192600435613601565b503461036e578060031936011261036e576002600080516020615e428339815191525414611eff576002600080516020615e4283398151915255338152600660205260408120548015611ef05733825260066020528160408120558180808084335af13d15611eeb573d611e7e8161338d565b90611e8c604051928361329f565b81528360203d92013e5b15611edc576040519081527f7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d560203392a26001600080516020615e428339815191525580f35b6312171d8360e31b8252600482fd5b611e96565b630b8a550d60e31b8252600482fd5b633ee5aeb560e01b8152600490fd5b503461036e57608036600319011261036e57600435611f2b6130d8565b60443590600582101561044857611f406130eb565b90838552600360205260408520918486526004602052604086209363ffffffff600285015493169263ffffffff8160a01c168403611d5e5760ff865416600681141590816121af575b816121a3575b81612197575b50611a5557611fa4338661528d565b93600387019360ff855460681c1660ff87169081036121885763ffffffff80604051936370807d8760e11b85528b6004860152836024860152611fe68861320f565b876044860152169485606485015260201c16608483015260208260a48173ecfafd597d0ffe9d9ba32feaa2f12f12f2bb86dc5af493841561217d5760ff958b938d96612133575b5091606091878461204c600080516020615dc28339815191529661320f565b6040519384521660208301526040820152a3166002810361207e575050906120786104419493926132c2565b92615350565b90949150600103612129578360ff610441955460601c1680156000146120b0575050825460ff191660071783556158ad565b600181036120ca575050825460ff191660091783556158ad565b6002036120e25750825460ff1916600b1783556158ad565b835460ff1916600d1784555460981c60ff9081169081146121105761210b905b6011850161591b565b6158ad565b5061210b61212460ff855460081c166132c2565b612102565b61044193506158e4565b600080516020615dc283398151915293919650918761216b60609460203d602011612176575b612163818361329f565b8101906135e8565b97929450509161202d565b503d612159565b6040513d8d823e3d90fd5b631cc191eb60e31b8b5260048bfd5b600c9150141538611f95565b600a8114159150611f8f565b60088114159150611f89565b503461036e57602036600319011261036e576004358082526003602052604082206001810190600160ff835460a01c16036122885780546001600160a01b0316330361227957815460ff60a01b1916600160a21b1791829055549061222d9060c81c63ffffffff1660a083901c6131d7565b6001600160a01b0390911683526006602052604083208054909161225091613368565b90557f8bd66138689095e38b45ec45363241e614b91b8e6fb90f7c15dfdeb93f34fcfe8280a280f35b6393687c0b60e01b8452600484fd5b631bb5f5b360e31b8452600484fd5b503461036e578060031936011261036e57602061ffff60015460a01c16604051908152f35b503461036e578060031936011261036e576020604051633b9aca008152f35b503461036e5761010036600319011261036e576122f66130d8565b366084116119c5573660c4116119c55760c4356001600160401b03811161036c5761232590369060040161310e565b9060e4356001600160401b03811161151f5761234590369060040161313b565b60049291923586526003602052604086209260043587526004602052604087209463ffffffff600286015460a01c1663ffffffff881603611d5e5761238a338661528d565b9460ff875416600d8114600014612b96575060ff601188015460081c1660ff871603612b87575b6101008214801590612b7c575b612b3b57603460ff6123ce61330d565b1610801590612b67575b8015612b4a575b612b3b57885b60058110612acd575060ff8616612ac55788905b895b600281101561271f5761240d816132d3565b356001808216149081612714575b50156127055761242e60ff821684613354565b818060071b04608014821517156126f157600182018083116126dd57808060071b04608014811517156126dd57908c8b6020838a6124b16124968f8f61248b8f612484908e9460079e8f1b918f87901b91613375565b36916133e3565b9a8b9801549461584b565b60405163af0586a360e01b81529586948594600486016134b2565b03817376a5e1d2e03838dabdce38f3372510ba53cabf085af491821561129757916126bf575b50156126b0576124ea9060088c0161341a565b90549060031b1c60ff8a16156000146126a9578c612507846132d3565b35915b60ff8c1661269857915b612525612520866132fb565b61332d565b60ff604051916349b11d3760e01b8352166004820152828160248173b04c1e546d7cf8dc39e10dfc17148475737908065af490811561268d578391612673575b5061258460405194859384936340604be760e01b85526004850161354f565b038173b04c1e546d7cf8dc39e10dfc17148475737908065af4908115612668578d91612646575b5060208151910120906020815191012003612637576125c9816132d3565b35600289101561262357906001916125eb6119208360128e8e881b0101613571565b905561261d8a8d61260b8c6016612604612520886132fb565b9401613571565b509050601f8416908460051c01613581565b016123fb565b634e487b7160e01b8c52603260045260248cfd5b6399f4b04160e01b8b5260048bfd5b61266291503d808f833e61265a818361329f565b8101906134ee565b386125ab565b6040513d8f823e3d90fd5b61268791503d8085833e61265a818361329f565b38612565565b6040513d85823e3d90fd5b506126a2846132d3565b3591612514565b8c8161250a565b637ca55c7760e01b8d5260048dfd5b6126d7915060203d811161129057611282818361329f565b386124d7565b634e487b7160e01b8d52601160045260248dfd5b634e487b7160e01b8c52601160045260248cfd5b63284f145b60e11b8b5260048bfd5b60019150113861241b565b8a888a848d6040519361273360e08661329f565b60e0368637855b60058110612a81575060ff61274d61330d565b1660a086015260ff61275d61331d565b1660c08601526040516342b9d18160e11b81529486600487015b60078210612a685750505060208560e4817385c53cbfc7f3425f5328d05a6732f659f468592a5af4948515612a5d578695612a1c575b5063ffffffff60188501926127cc876127c68587613599565b906135b5565b8160118701976127ed60ff60018188161b1660ff8b5460101c16178a6135d0565b60ff6127f761330d565b8161280061331d565b91816040519916895216602088015216604086015216606084015216907fe656b3a671b1d5b27cba2c8f42d1b11df035c0a6f0fc7ffcb2bfb7f5c51657fd608060043592a382549360ff8516600d0361286d575050815460ff1916600e17825561044192506004356158ad565b5460081c60ff169390929190612882856132c2565b9363ffffffff6128928683613599565b90549060031b1c1663ffffffff6128a98884613599565b90549060031b1c16106000146128c85750506104419350600435615350565b909192936128ee8663ffffffff6128e0819486613599565b90549060031b1c1693613599565b90549060031b1c161160001461290d5750906104419291600435615350565b610441935063ffffffff600384015416906129b461294a63ffffffff61271061294161ffff600189015460b81c16876131d7565b04168094615239565b916129a061298b61296a60ff637fffffff8760011c169460081c166132c2565b6129a661297a600188168661521f565b6129a061298b60018d019485613599565b91909263ffffffff84548460031b1c1661521f565b916135b5565b60ff895460081c1690613599565b6129d76129ce8363ffffffff600287015460c01c1661521f565b6002850161532d565b63ffffffff600284015460a01c169163ffffffff60405192600284521660208301526040820152600080516020615e02833981519152606060043592a3600435615ce6565b9094506020813d602011612a55575b81612a386020938361329f565b81010312610444575163ffffffff811681036104445793866127ad565b3d9150612a2b565b6040513d88823e3d90fd5b60208060019260ff865116815201930191019091612777565b60ff612a9082600d880161333b565b90549060031b1c166007821015612ab157600582901b87015260010161273a565b634e487b7160e01b88526032600452602488fd5b6002906123f9565b60ff612adc82600d8b0161333b565b90549060031b1c1660ff612aee61330d565b16148015612b12575b612b03576001016123e5565b63a554dcdf60e01b8a5260048afd5b5060ff612b2282600d8b0161333b565b90549060031b1c1660ff612b3461331d565b1614612af7565b63a554dcdf60e01b8952600489fd5b50612b5361330d565b60ff80612b5e61331d565b169116146123df565b50603460ff612b7461331d565b1610156123d8565b50600c8414156123be565b630304193d60e11b8952600489fd5b600e036119ff5760ff612bb18160118a015460081c166132c2565b1660ff871603156123b157630304193d60e11b8952600489fd5b503461036e578060031936011261036e5760206040516101f48152f35b503461036e57602036600319011261036e576004358082526003602052604082208183526004602052604083206001820190600260ff835460a01c1603612d5957612c33338461528d565b9160ff825416857f410304baf564e21c104cbc1ab0a0f9c155f9a5119091083c05d2a6e2b0fd54b5602060405160ff88168152a280612c7957505050506104419161552c565b600f819593949514908115612d4e575b5015612c995750610441936157a3565b91909273ecfafd597d0ffe9d9ba32feaa2f12f12f2bb86dc86813b1561036e5760249160405192838092631067a2ef60e11b82528760048301525af4801561199a57612d2a575b5091612d1260ff92612d0a600295612d0563ffffffff888a015460401c1682856152d1565b6132c2565b908688615350565b5460a01c1614612d2157505080f35b6104419161552c565b60ff92612d0a88612d42600297959a612d129561329f565b98939550509250612ce0565b601091501438612c89565b63126ab15560e01b8552600485fd5b5061010036600319011261036e576004356001600160601b03811680820361036c57612d926130d8565b916044359063ffffffff821680920361151f57612dad6130eb565b946084359563ffffffff87169081880361036c5760a4359463ffffffff86169384870361036e5760c4359563ffffffff87169687810361036c5760e4359a60ff8c16998a8d0361151f578b1580156130ca575b80156130b7575b613058576001861080156130a8575b801561308e575b61305857603c88108015613082575b61305857603c89108015613076575b6130585760018a108015613067575b6130585760088b1015613058578b9c9d612e6e63ffffffff849e9d9e16809d6131d7565b3403610fd457600254612e8090613200565b9e8f80600255875260036020526040872095600160a01b600190033316600160a01b6001900319885416178755865490600160a01b60019003199060a01b1690600160a01b60019003161786556001860190600160a01b60ff60a01b1983541617825581549060ff60a81b9060a81b168060ff60a81b19831617835561ffff60b81b60015460181b169063ffffffff60c81b8760c81b169267ffffffffffff00ff60a81b1916171717905560028501938763ffffffff1986541617855584549263ffffffff60801b9060801b169263ffffffff60801b199063ffffffff60201b8b60201b1690600160201b600160801b03191617169063ffffffff60401b9060401b16179063ffffffff60601b9060601b1617178255612f9f9161322f565b6003018763ffffffff19825416179055600160a01b6001900360015416908a815260056020526040902090600160a01b6001900316600160a01b600190031982541617905560015460a01c61ffff16956040519889526020890152604088015263ffffffff166060870152608086015260a085015260c084015260e08301526101008201528133916101207f420317bb06dc9f1eea6d42235fffbfe838d3bef49aba0b479ecb45b332dd6dbb91a3604051908152602090f35b63de17a3af60e01b8552600485fd5b5063ffffffff87168a11612e4a565b5062093a808911612e3b565b5062093a808811612e2c565b5063ffffffff600a818416041663ffffffff881611612e1d565b508563ffffffff881610612e16565b50633b9aca0063ffffffff831611612e07565b5063ffffffff821615612e00565b6024359063ffffffff8216820361198d57565b6064359063ffffffff8216820361198d57565b6044359060ff8216820361198d57565b9181601f8401121561198d578235916001600160401b03831161198d576020838186019501011161198d57565b9181601f8401121561198d578235916001600160401b03831161198d576020808501948460051b01011161198d57565b600435906001600160a01b038216820361198d57565b906000905b6002821061319357505050565b60208060019263ffffffff865116815201930191019091613186565b906000905b600482106131c157505050565b60208060019285518152019301910190916131b4565b818102929181159184041417156131ea57565b634e487b7160e01b600052601160045260246000fd5b60001981146131ea5760010190565b6005111561321957565b634e487b7160e01b600052602160045260246000fd5b80546001600160e01b031660e09290921b6001600160e01b031916919091179055565b61046081019081106001600160401b0382111761326e57604052565b634e487b7160e01b600052604160045260246000fd5b604081019081106001600160401b0382111761326e57604052565b601f909101601f19168101906001600160401b0382119082101761326e57604052565b60ff166001039060ff82116131ea57565b60028110156132e55760051b60440190565b634e487b7160e01b600052603260045260246000fd5b60028110156132e55760051b60840190565b60843560ff8116810361198d5790565b60a43560ff8116810361198d5790565b3560ff8116810361198d5790565b91909160058310156132e557601f908360051c01921690565b9060ff8091169116019060ff82116131ea57565b919082018092116131ea57565b9093929384831161198d57841161198d578101920390565b6001600160401b03811161326e57601f01601f191660200190565b9190916133b5608061338d565b906133c3604051928361329f565b819360808352608082011161198d5781608060a092602060009501370152565b9291926133ef8261338d565b916133fd604051938461329f565b82948184528183011161198d578281602093846000960137010152565b60048210156132e5570190600090565b9081602091031261198d5751801515810361198d5790565b60005b8381106134555750506000910152565b8181015183820152602001613445565b9060209161347e81518092818552858086019101613442565b601f01601f1916010190565b906000905b6006821061349c57505050565b602080600192855181520193019101909161348f565b9493916134ec9360ff6134e4926080948952600460208a01521660408801526101406060880152610140870190613465565b94019061348a565b565b60208183031261198d578051906001600160401b03821161198d570181601f8201121561198d5780516135208161338d565b9261352e604051948561329f565b8184526020828401011161198d5761354c9160208085019101613442565b90565b61356760409295949395606083526060830190613465565b9460208201520152565b60028210156132e5570190600090565b919060ff8084549260031b9316831b921b1916179055565b91909160028310156132e557601c908360031c019260021b1690565b919063ffffffff8084549260031b9316831b921b1916179055565b9062ff000082549160101b169062ff00001916179055565b9081602091031261198d575160ff8116810361198d5790565b929091600084815260036020526040812093858252600460205260408220906001860191600260ff845460a01c160361398e57600287019463ffffffff80875460a01c16931692830361397f57815460ff80821692168214801590613977575b6139685760101c6001600160401b031642111561395957613682338961528d565b9260028214801561394f575b1561378a576001915b60ff831660ff8616811461377b5760408c92600080516020615de28339815191529282519182526020820152a373ecfafd597d0ffe9d9ba32feaa2f12f12f2bb86dc803b15610444578560249160405192838092631067a2ef60e11b82528760048301525af48015612a5d57613759575b506137249063ffffffff61372b9596975460401c1690836152d1565b8688615350565b82613744575b505061373b575050565b6134ec9161552c565b5460a01c60ff16600214915038905080613731565b9063ffffffff8661377161372b97986137249561329f565b9695505090613708565b635f07a36960e11b8852600488fd5b60038203613799578591613697565b600582036137e45760ff84166001036137db5760ff60015b600c8501541616156137cc576137c6846132c2565b91613697565b632a0e78a360e01b8652600486fd5b60ff60026137b1565b600782148015613945575b801561393b575b156138155760ff60118401541660ff8516036137cc576137c6846132c2565b600682148015613931575b8015613927575b801561391d575b156138445760ff600384015460681c1691613697565b600d820361385d5760ff601184015460081c1691613697565b600e8203613878576137c660ff601185015460081c166132c2565b949350600f919796508099989550146000146138ce5760ff166138bf575081600080516020615de283398151915260406134ec9798815190815260016020820152a3615722565b6393687c0b60e01b8152600490fd5b601088929814600014611a555760ff600191160361390e57600080516020615de2833981519152604084926134ec989982519182526020820152a3615670565b6338b72ae960e11b8752600487fd5b50600c821461382e565b50600a8214613827565b5060088214613820565b50600b82146137f6565b50600982146137ef565b506004821461368e565b63d0404f8560e01b8552600485fd5b63301c306b60e01b8652600486fd5b508115613661565b636226556b60e01b8552600485fd5b63126ab15560e01b8452600484fd5b908060209392818452848401376000828201840152601f01601f1916010190565b91908110156132e55760051b0190565b60038210156132e5570190600090565b60ff90911681526001600160401b03909116602082015260400190565b9c9491979a9998959c9b93909b969296610220526101a0526101c0526102a0526101405261016052600061020052610220516102005152600360205260406102005120610240526102205161020051526004602052604061020051206102605263ffffffff600261024051015460a01c1663ffffffff8816036142f75760ff610260515416610280526007610280511415806142e9575b806142db575b6142c657613aa9336102405161528d565b61018052613ab961028051615931565b6101e0529660ff6101e0511683036140f257610200515b60ff6101e05116811061427f575060ff80601161026051015416146141d5575060ff60116102605101541660ff6101805116146141c05760ff6101e051166101c0511480159061419c575b801561416b575b6140f257610200515b60ff6101e051168110613dd25750506102605160118101805460ff191660ff17905561020051600e8201819055600f8201819055601090910155505061028051600714159450613d8d93505050505761026051600301805460ff60601b1916600160601b1790555b63ffffffff600261024051015460a01c1660ff6003610260510191825490604051838360601c168152613bcf60208201600d610260510161493a565b7ff6c035c10ac1e9a6d54a4228a066116b018fcb4d11df171c9a55665c47de523760c06102205192a360881c1615613ca25761028051600703613c31575061026051805460ff191660091790555b6134ec6102605161024051610220516158ad565b61028051600903613c51575061026051805460ff1916600b179055613c1d565b61026051805460ff1916600d1790555460981c60ff908116908114613c8657613c81905b6011610260510161591b565b613c1d565b50613c81613c9d60ff610260515460081c166132c2565b613c75565b5061028051600703613d5a5761026051805460ff191660081790555b610240516002015460201c63ffffffff1673ecfafd597d0ffe9d9ba32feaa2f12f12f2bb86dc803b15613d53576040519163441271fb60e11b835261026051600484015260248301528160448161020051935af48015613d4557613d32575b506134ec6102605161024051610220516158e4565b61020051613d3f9161329f565b38613d1d565b6040513d61020051823e3d90fd5b6102005180fd5b61028051600903613d795761026051805460ff1916600a179055613cbe565b61026051805460ff1916600c179055613cbe565b61028051600903613db55761026051600301805460ff60601b1916600160611b179055613b93565b61026051600301805460ff60601b1916600360601b179055613b93565b613ddf60ff821689613354565b90808060071b0460801481151715613fe15760018101808211613fe157808060071b0460801481151715613fe157612484613e269160071b8360071b8d6102a05190613375565b806020848b613e41612496878a60076102605101549461584b565b03817376a5e1d2e03838dabdce38f3372510ba53cabf085af4908115613d4557610200519161414d575b50156141385761020051610120526101805160ff1661411857613e8f8286886139be565b35610120525b6101805160ff1661410757613eb082600e61026051016139ce565b90549060031b1c5b603460ff613ed0612520866101c0516101a0516139be565b16101561404c57613eeb612520846101c0516101a0516139be565b9060ff604051926349b11d3760e01b8452166004830152610200518260248173b04c1e546d7cf8dc39e10dfc17148475737908065af4908115613d4557613f5b92610200519261402d575b50604051809381926340604be760e01b8352610200519461012051906004850161354f565b038173b04c1e546d7cf8dc39e10dfc17148475737908065af4908115613d45576102005191614010575b5060208151910120906020815191012003613ffb57613fae612520826101c0516101a0516139be565b9160ff90811660031901908111613fe157600192613fd5613fdb92600d610260510161333b565b90613581565b01613b2b565b634e487b7160e01b61020051526011600452602461020051fd5b6399f4b04160e01b6102005152600461020051fd5b61402791503d8061020051833e61265a818361329f565b38613f85565b6140459192503d8061020051833e61265a818361329f565b9038613f36565b99509496925092949650975060ff8061406f612520856101c0516101a0516139be565b16036140f25760349561409961408b846140b29760ff996139be565b3593600e61026051016139ce565b94909361016051956101405195549060031b1c91615981565b1610156140d35750506134ec61018051610260516102405161022051615c2a565b6134ec9161012051906101805190610260516102405161022051615b97565b63a554dcdf60e01b6102005152600461020051fd5b6141128286886139be565b35613eb8565b61412882600e61026051016139ce565b90549060031b1c61012052613e95565b637ca55c7760e01b6102005152600461020051fd5b614165915060203d811161129057611282818361329f565b38613e6b565b50617f806101e05160071b1660ff6101e05116810460801460ff6101e05116151715613fe1576102a0511415613b22565b506101e05160ff1660068181029180159083049091141715613fe157811415613b1b565b63e9f155db60e01b6102005152600461020051fd5b9750509350935050506102005190600e6102605101915b60ff6101e05116811061425b575050505060ff610180511660ff1960116102605101541617601161026051015560018060401b03610260515460101c16600080516020615e6283398151915263ffffffff60405193169280614256610220519461028051836139de565b0390a3565b8061426960019284876139be565b3561427761192083876139ce565b9055016141ec565b61428a8185876139be565b3560018082161490816142bb575b50156142a657600101613ad0565b63284f145b60e11b6102005152600461020051fd5b600191501138614298565b63301c306b60e01b6102005152600461020051fd5b50600b610280511415613a98565b506009610280511415613a92565b636226556b60e01b6102005152600461020051fd5b8054600160201b600160601b03191660209290921b600160201b600160601b0316919091179055565b92919061434c602091604086526040860190613465565b930152565b95949193909486600052600360205260406000209487600052600460205260406000209663ffffffff80600289015460a01c1691160361487357601060ff885416036148625785546001600160a01b031633036148515760ff169360348510801590614834575b8015614817575b6147ac5761020082036147ac578160801161198d576143de36856133a8565b826101001161198d576143f436608087016133a8565b92806101801161198d5761440c3661010088016133a8565b916020815191012095601e8a019687541491821592614802575b82156147ec575b82156147bd575b50506147ac576040516349b11d3760e01b81526004810187905273b04c1e546d7cf8dc39e10dfc1714847573790806956000826024818a5af48015614667578460209161449f94600091614791575b50604051630f237ed160e21b8152948592839260048401614335565b03818a5af49182156146675760009261475d575b50540361474e57604051630f237ed160e21b81529291602091849182916144de919060048401614335565b0381875af4908115614667578392600092614715575b50604051630f237ed160e21b81529260209184918291614518919060048401614335565b0381875af4918215614667576000926146e1575b50036146d5576040516349b11d3760e01b81526004810184905290600082602481865af4918215614667576000926146b6575b50601d8601546040516340604be760e01b8152926000928492839261458892906004850161354f565b0381855af49081156146675760009161469b575b506020815191012091602185019283540361468f57604051906349b11d3760e01b82526004820152600081602481855af480156146675761460a926000928392614673575b50601c870154601b8801546040516340604be760e01b815295869485938493916004850161354f565b03915af49081156146675760009161464c575b506020815191012090540361464357906134ec929160ff601983015460101c16926157a3565b6134ec92615670565b61466191503d806000833e61265a818361329f565b3861461d565b6040513d6000823e3d90fd5b6146889192503d8085833e61265a818361329f565b90386145e1565b5050506134ec92615722565b6146b091503d806000833e61265a818361329f565b3861459c565b60009192506146ce903d8084833e61265a818361329f565b919061455f565b5050506134ec92615670565b9091506020813d60201161470d575b816146fd6020938361329f565b8101031261198d5751903861452c565b3d91506146f0565b925090506020823d602011614746575b816147326020938361329f565b8101031261198d57905182916145186144f4565b3d9150614725565b5050505050506134ec92615670565b9091506020813d602011614789575b816147796020938361329f565b8101031261198d575190386144b3565b3d915061476c565b6147a691503d806000833e61265a818361329f565b38614483565b63a554dcdf60e01b60005260046000fd5b9091506102001161198d576147d7906101803691016133a8565b60208151910120602189015414153880614434565b91508251602084012060208b015414159161442d565b85516020870120601f8c015414159250614426565b506001808416148061482a575b156143bf565b5060018311614824565b5060018082161480614847575b156143b8565b5060018111614841565b6393687c0b60e01b60005260046000fd5b63301c306b60e01b60005260046000fd5b636226556b60e01b60005260046000fd5b6001600160401b03811161326e5760051b60200190565b906148a582614884565b6148b2604051918261329f565b82815280926148c3601f1991614884565b0190602036910137565b80518210156132e55760209160051b010190565b9063ffffffff6040519254818116845260201c1660208301526134ec60408361329f565b60405191906000835b60048210614924575050506134ec60808361329f565b600160208192855481520193019101909161490e565b60ff608091548181168452818160081c166020850152818160101c166040850152818160181c16606085015260201c16910152565b939096919497929895986101e05260e0526102405261026052600061028052806102805152600360205260406102805120948161028051526004602052604061028051209663ffffffff600288015460a01c1663ffffffff8716036151d957600f60ff895416036151c45760018701546001600160a01b031633036151af57603460ff85161080159061518c575b801561516b575b61514857610200811480159061515d575b6151485760ff6019890154166080528060801161514157614a3636836133a8565b61010052806101001161514157614a5036608084016133a8565b6101c052806101801161514157614a6b3661010084016133a8565b6101a0526102001161514157614a86906101803691016133a8565b60048701546102a05260c0808052604051610220819052919591614aaa919061329f565b60c0516102205136903761028051610200525b6006610200511015614b2d5761028051614b13576102005150600061028052614af06102005161026051610240516139be565b3561028051506102005160051b6102205101526001610200510161020052614abd565b634e487b7160e01b61028051526011600452602461028051fd5b60405163af0586a360e01b81526102a05160048201526001602482015260ff84166044820152610140606482015260208180614b70610144820161010051613465565b614b80608483016102205161348a565b03817376a5e1d2e03838dabdce38f3372510ba53cabf085af490811561502c576102805191615122575b50156150b05760058701546101605260c051604051610180819052614bcf919061329f565b60c05161018051369037610280515b6006811015614c205761028051614b13578060060190816006116131ea57614c0f60019261026051610240516139be565b358160051b61018051015201614bde565b5090919293949560405163af0586a360e01b8152610160516004820152600260248201526080516044820152610140606482015260208180614c6961014482016101c051613465565b614c79608483016101805161348a565b03817376a5e1d2e03838dabdce38f3372510ba53cabf085af490811561502c576102805191615103575b50156150b05760068101546101205260c051604051610140819052614cc8919061329f565b60c05161014051369037610280515b6006811015614d195761028051614b135780600c019081600c116131ea57614d0860019261026051610240516139be565b358160051b61014051015201614cd7565b5090919293949560405163af0586a360e01b8152610120516004820152600360248201526080516044820152610140606482015260208180614d6261014482016101a051613465565b614d72608483016101405161348a565b03817376a5e1d2e03838dabdce38f3372510ba53cabf085af490811561502c5761028051916150e4575b50156150b057600782015460c05160405160a0819052919891614dbf919061329f565b60c05160a051369037610280515b6006811015614e0e5761028051614b13578060120190816012116131ea57614dfe60019261026051610240516139be565b358160051b60a051015201614dcd565b50909192939495966020614e3f916040518093819263af0586a360e01b83528660a0519160805190600486016134b2565b03817376a5e1d2e03838dabdce38f3372510ba53cabf085af490811561502c5761028051916150c5575b50156150b05773b04c1e546d7cf8dc39e10dfc171484757379080660405190630f237ed160e21b825260208280614eaa6101e0516101005160048401614335565b0381845af491821561502c57610280519261507c575b506101c051519160206101c051019283200361506c57604051630f237ed160e21b815260208180614ef86101e0518860048401614335565b0381855af490811561502c57610280519161503a575b5060206040518093630f237ed160e21b82528180614f3560e0516101a05160048401614335565b03915af491821561502c576102805192614ff8575b5003614fe9576101e051601a85015560e051601b850155610100518051602091820120601e8601556101c05151909120601f8501556101a051805190820120848201558151918101919091206021840155825460ff191660101783557f1b2ab5b594fd7d4b1494f908fa143ff6f2f9879b399e1d17f68ec54f44241b8f93909263ffffffff92614fdb9190876158ad565b60ff604051961686521693a3565b50509150916134ec9350615722565b9091506020813d602011615024575b816150146020938361329f565b8101031261198d57519038614f4a565b3d9150615007565b6040513d61028051823e3d90fd5b90506020813d602011615064575b816150556020938361329f565b8101031261198d575138614f0e565b3d9150615048565b5050509150916134ec9350615722565b9091506020813d6020116150a8575b816150986020938361329f565b8101031261198d57519038614ec0565b3d915061508b565b637ca55c7760e01b6102805152600461028051fd5b6150de915060203d60201161129057611282818361329f565b38614e69565b6150fd915060203d60201161129057611282818361329f565b38614d9c565b61511c915060203d60201161129057611282818361329f565b38614ca3565b61513b915060203d60201161129057611282818361329f565b38614baa565b6102805180fd5b63a554dcdf60e01b6102805152600461028051fd5b506018610260511415614a15565b5060018060e051161480615180575b15614a04565b50600160e0511161517a565b506001806101e0511614806151a2575b156149fd565b5060016101e0511161519c565b6338b72ae960e11b6102805152600461028051fd5b63301c306b60e01b6102805152600461028051fd5b636226556b60e01b6102805152600461028051fd5b6001600160401b0391821690821601919082116131ea57565b63ffffffff60019116019063ffffffff82116131ea57565b9063ffffffff8091169116019063ffffffff82116131ea57565b9063ffffffff8091169116039063ffffffff82116131ea57565b63ffffffff91821681529116602082015260400190565b805463ffffffff60a01b191660a09290921b63ffffffff60a01b16919091179055565b80546001600160a01b03928316921682146152ca57600101546001600160a01b0316146152c55763abca351760e01b60005260046000fd5b600190565b5050600090565b61298b6134ec9361532761530360016129a095019263ffffffff6152f58886613599565b90549060031b1c1690615cc6565b94612d056153118285613599565b6129a08963ffffffff84548460031b1c16615239565b90613599565b805463ffffffff60c01b191660c09290921b63ffffffff60c01b16919091179055565b6134ec9381600080516020615e02833981519152606063ffffffff6003880154169361539b63ffffffff61271061539261ffff60018c015460b81c16896131d7565b04168096615239565b946153c26153ac8360018c01613599565b6129a08963ffffffff84548460031b1c1661521f565b63ffffffff8060028a016153e56153df8584845460c01c1661521f565b8261532d565b5460a01c169660ff604051941684521660208301526040820152a3615ce6565b818110615410575050565b60008155600101615405565b60048101905b81811061542d575050565b60008155600101615422565b6000815561544d6002820160018301615405565b61545d6003820160028301615405565b600060038201556154706004820161541c565b6000600c82016154838160088501615405565b556000600e820161549781600d8501615405565b6154a5601184018092615405565b5560168101601282015b818110615517575060188201905b81811061550257506134ec916154d9601e926019830190615405565b600060198201556000601a8201556000601b8201556000601c8201556000601d8201550161541c565b80615511600180930182615405565b016154bd565b80615526600280930182615405565b016154af565b60018201805460ff60a01b1916600360a01b1781558254600284015492937ffabca260174d01cb14c0ba7f3a1678d899165a405e9f9781f2d21cc5ab7bdab8936060939192916155a760a084901c63ffffffff61559d828260036155948360e08b901c6131d7565b9a0154166131d7565b9460c01c166131d7565b9284615648575b508161561b575b50816155e5575b8560005260046020526155d26040600020615439565b60405192835260208301526040820152a2565b85600052600560205260018060a01b036040600020541660005260066020526040600020615614838254613368565b90556155bc565b546001600160a01b031660009081526006602052604090208054615640908390613368565b9055386155b5565b60018060a01b031660005260066020526040600020615668858254613368565b9055386155ae565b80600080516020615e2283398151915260206134ec956156dc600182019163ffffffff60036156d26156b1600096848082541691546004861b1c169061521f565b836156c46002870192828454169061521f565b91546004851b1c169061521f565b920154169061521f565b6002870160018060e01b03815416905563ffffffff60038801911663ffffffff198254161790558381526004825261571660408220615439565b604051908152a261552c565b61575f6134ec93615754600182019163ffffffff60036156d26156b1600096848082541691546004861b1c169061521f565b90506002840161322f565b6003820163ffffffff1981541690558060005260046020526157846040600020615439565b80600080516020615e22833981519152602060405160018152a261552c565b600080516020615e2283398151915260206134ec95615716604085969760ff6157ed600183019263ffffffff60036156d26156b1600097848082541691546004861b1c169061521f565b9416938461582a5760028a0160018060e01b03815416905563ffffffff60038b01911663ffffffff198254161790555b8681526004855220615439565b6158379060028b0161322f565b60038901805463ffffffff1916905561581d565b6040519193929060c061585e818561329f565b3684378260005b60068110156158a5576006840290848204600614851517156131ea5761589761589082600194613368565b89866139be565b358160051b87015201615865565b509450505050565b9060ff6002600080516020615e6283398151915292019361425663ffffffff611c8c81885460801c1660018060401b0342166151ee565b9060ff6002600080516020615e6283398151915292019361425663ffffffff611c8c81885460601c1660018060401b0342166151ee565b9061ff0082549160081b169061ff001916179055565b60ff16600781146159535760091461594b57600890600190565b600790600190565b50600490600390565b9161597361354c9492604085526040850190613465565b92602081850391015261399d565b9092919593949560808614801590615b8c575b6147ac5760006159d09573b04c1e546d7cf8dc39e10dfc171484757379080698604051978892839263d325c5b160e01b8452876004850161595c565b03818a5af494851561466757600095615b6a575b506000615a06916040518093819263631ec63f60e01b83528960048401614335565b03818a5af490811561466757600091615b4f575b5060208151910120906020815191012003615b005760405163d325c5b160e01b81529360009185918291615a539190876004850161595c565b0381875af492831561466757600093615b2c575b506000615a89916040518093819263631ec63f60e01b83528760048401614335565b0381875af490811561466757600091615b11575b5060208151910120906020815191012003615b0057806020809251910120602460405180948193633b8c1f2d60e21b835260048301525af490811561466757600091615ae7575090565b61354c915060203d60201161217657612163818361329f565b638ba09fd960e01b60005260046000fd5b615b2691503d806000833e61265a818361329f565b38615a9d565b615a89919350615b476000913d8084833e61265a818361329f565b939150615a67565b615b6491503d806000833e61265a818361329f565b38615a1a565b615b84600091615a0693973d8091833e61265a818361329f565b9591506159e4565b506080851415615994565b94600260ff92615c0b606096957fd3060bf93343487b796703319013dda402debbb3819e37ca26f987571ca09805989a63ffffffff95615bf08d601985018a8c168b1982541617815561ff0080198254161781556135d0565b601c830155601d820155600f8619825416178155828a6158ad565b015460a01c1695816040519316835216602082015260ff6040820152a3565b60038301805460ff60901b1916609086901b60ff60901b16179055600094939092919073ecfafd597d0ffe9d9ba32feaa2f12f12f2bb86dc86813b1561036e5760249160405192838092631067a2ef60e11b82528860048301525af4801561199a579661207892916134ec9798615cb6575b5050612d0563ffffffff600285015460401c1682866152d1565b81615cc09161329f565b38615c9c565b9063ffffffff811663ffffffff831610600014615ce1575090565b905090565b9160010160009063ffffffff808083541692615d0660028701948561322f565b54600460031b1c1691816003860193168219845416178355615d5c604083835460a01c1695615d3d615d3788615207565b8561526a565b615d4e600180841b0342168761430c565b888152600460205220615439565b5460e01c9154169184600080516020615da283398151915260405180615d83878783615253565b0390a315908115615d98575b5061373b575050565b90501538615d8f56fe59d3350cb221a36e23b483076e38f2c093b609b0aebdfdeb03d0512d7d5a2315134e07fe001e0bc82a08476763b074f743a6d3b7aa79f27bf2deff8f576129c66d007a5bc0a881f142027c9c5a95f04f9f8ec48e7f9c4b538aef7ac8d8397ede30e1dec6db2d80ec3e362df6665049ac592e8cc98d7b236d0c61681304da81340cd5cc6eac0dc3c714a186b81c618ab602c9b02830ba885dc7af3e76dbfc5c349b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f008e0405abb6bd265a8563b4e6fb2118d701c6055b85e736e6b75e062b5d2e0f28a2646970667358221220ffcb69456a0b24ba9cd329cc2d2b2d5233221b9e3a8286b72c65191b8641030664736f6c634300081c0033