ERC-20
Gaming
Overview
Max Total Supply
10,000,000 PRPS
Holders
1,071 (0.00%)
Market
Onchain Market Cap
$0.00
Circulating Supply Market Cap
-
Other Info
Token Contract (WITH 18 Decimals)
Balance
0.000035418559073634 PRPSValue
$0.00Loading...
Loading
Loading...
Loading
Loading...
Loading
# | Exchange | Pair | Price | 24H Volume | % Volume |
---|
This contract contains unverified libraries: ProtectedBoostableLib
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
Purpose
Compiler Version
v0.6.12+commit.27d51765
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "./ERC20.sol"; import "./Dubi.sol"; import "./IHodl.sol"; import "./MintMath.sol"; contract Purpose is ERC20 { // The DUBI contract, required for auto-minting DUBI on burn. Dubi private immutable _dubi; // The HODL contract, required for burning locked PRPS. IHodl private immutable _hodl; modifier onlyHodl() { require(msg.sender == _hodlAddress, "PRPS-1"); _; } constructor( uint256 initialSupply, address optIn, address dubi, address hodl, address externalAddress1, address externalAddress2, address externalAddress3 ) public ERC20( "Purpose", "PRPS", optIn, hodl, externalAddress1, externalAddress2, externalAddress3 ) { _dubi = Dubi(dubi); _hodl = IHodl(hodl); _mintInitialSupply(msg.sender, initialSupply); } /** * @dev Returns the address of the {HODL} contract used for burning locked PRPS. */ function hodl() external view returns (address) { return address(_hodl); } /** * @dev Returns the hodl balance of the given `tokenHolder` */ function hodlBalanceOf(address tokenHolder) public view returns (uint256) { // The hodl balance follows after the first 96 bits in the packed data. return uint96(_packedData[tokenHolder] >> 96); } /** * @dev Transfer `amount` PRPS from `from` to the Hodl contract. * * This can only be called by the Hodl contract. */ function hodlTransfer(address from, uint96 amount) external onlyHodl { _move(from, address(_hodl), amount); } /** * @dev Increase the hodl balance of `account` by `hodlAmount`. This is * only used as part of the migration. */ function migrateHodlBalance(address account, uint96 hodlAmount) external onlyHodl { UnpackedData memory unpacked = _unpackPackedData(_packedData[account]); unpacked.hodlBalance += hodlAmount; _packedData[account] = _packUnpackedData(unpacked); } /** * @dev Increase the hodl balance of `to` by moving `amount` PRPS from `from`'s balance. * * This can only be called by the Hodl contract. */ function increaseHodlBalance( address from, address to, uint96 amount ) external onlyHodl { UnpackedData memory unpackedDataFrom = _unpackPackedData( _packedData[from] ); UnpackedData memory unpackedDataTo; // We only need to unpack twice if from != to if (from != to) { unpackedDataTo = _unpackPackedData(_packedData[to]); } else { unpackedDataTo = unpackedDataFrom; } // `from` must have enough balance require(unpackedDataFrom.balance >= amount, "PRPS-3"); // Subtract balance from `from` unpackedDataFrom.balance -= amount; // Add to `hodlBalance` from `to` unpackedDataTo.hodlBalance += amount; // We only need to pack twice if from != to if (from != to) { _packedData[to] = _packUnpackedData(unpackedDataTo); } _packedData[from] = _packUnpackedData(unpackedDataFrom); } /** * @dev Decrease the hodl balance of `from` by `hodlAmount` and increase * the regular balance by `refundAmount. * * `refundAmount` might be less than `hodlAmount`. * * E.g. when burning fuel in locked PRPS * * This can only be called by the Hodl contract. */ function decreaseHodlBalance( address from, uint96 hodlAmount, uint96 refundAmount ) external onlyHodl { require(hodlAmount >= refundAmount, "PRPS-4"); UnpackedData memory unpackedDataFrom = _unpackPackedData( _packedData[from] ); // `from` must have enough balance require(unpackedDataFrom.hodlBalance >= hodlAmount, "PRPS-5"); // Subtract amount from hodl balance unpackedDataFrom.hodlBalance -= hodlAmount; if (refundAmount > 0) { // Add amount to balance unpackedDataFrom.balance += refundAmount; } // Write to storage _packedData[from] = _packUnpackedData(unpackedDataFrom); } /** * @dev Revert the hodl balance change caused by `from` on `to`. * * E.g. when reverting a pending hodl. * * This can only be called by the Hodl contract. */ function revertHodlBalance( address from, address to, uint96 amount ) external onlyHodl { UnpackedData memory unpackedDataFrom = _unpackPackedData( _packedData[from] ); UnpackedData memory unpackedDataTo; // We only need to unpack twice if from != to if (from != to) { unpackedDataTo = _unpackPackedData(_packedData[to]); } else { unpackedDataTo = unpackedDataFrom; } // `to` must have enough hodl balance require(unpackedDataTo.hodlBalance >= amount, "PRPS-5"); // Subtract hodl balance from `to` unpackedDataTo.hodlBalance -= amount; // Add to `balance` from `from` unpackedDataFrom.balance += amount; // We only need to pack twice if from != to if (from != to) { _packedData[to] = _packUnpackedData(unpackedDataTo); } _packedData[from] = _packUnpackedData(unpackedDataFrom); } /** * @dev Mint DUBI when burning PRPS * @param from address token holder address * @param transferAmount amount of tokens to burn * @param occupiedAmount amount of tokens that are occupied * @param createdAt equal to block.timestamp if not finalizing a pending op, otherwise * it corresponds to op.createdAt * @param finalizing boolean indicating whether this is a finalizing transaction or not. Changes * how the `amount` is interpreted. * * When burning PRPS, we first try to burn unlocked PRPS. * If burning an amount that exceeds the unlocked PRPS of `from`, we attempt to burn the * difference from locked PRPS. * * If the desired `amount` cannot be filled by taking locked and unlocked PRPS into account, * this function reverts. * * Burning locked PRPS means reducing the `hodlBalance` while burning unlocked PRPS means reducing * the regular `balance`. * * This function returns the actual unlocked PRPS that needs to be removed from `balance`. * */ function _beforeBurn( address from, UnpackedData memory unpacked, uint96 transferAmount, uint96 occupiedAmount, uint32 createdAt, FuelBurn memory fuelBurn, bool finalizing ) internal override returns (uint96) { uint96 totalDubiToMint; uint96 lockedPrpsToBurn; uint96 burnableUnlockedPrps; // Depending on whether this is a finalizing burn or not, // the amount of locked/unlocked PRPS is determined differently. if (finalizing) { // For a finalizing burn, we use the occupied amount, since we already know how much // locked PRPS we are going to burn. This amount represents the `pendingLockedPrps` // on the hodl items. lockedPrpsToBurn = occupiedAmount; // Since `transferAmount` is the total amount of PRPS getting burned, we need to subtract // the `occupiedAmount` to get the actual amount of unlocked PRPS. // Sanity check assert(transferAmount >= occupiedAmount); transferAmount -= occupiedAmount; // Set the unlocked PRPS to burn to the updated `transferAmount` burnableUnlockedPrps = transferAmount; } else { // For a direct burn, we start off with the full amounts, since we don't know the exact // amounts initially. lockedPrpsToBurn = transferAmount; burnableUnlockedPrps = unpacked.balance; } // 1) Try to burn unlocked PRPS if (burnableUnlockedPrps > 0) { // Nice, we can burn unlocked PRPS // Catch underflow i.e. don't burn more than we need to if (burnableUnlockedPrps > transferAmount) { burnableUnlockedPrps = transferAmount; } // Calculate DUBI to mint based on unlocked PRPS we can burn totalDubiToMint = MintMath.calculateDubiToMintMax( burnableUnlockedPrps ); // Subtract the amount of burned unlocked PRPS from the locked PRPS we // need to burn if this is NOT a finalizing burn, because in that case we // already have the exact amount locked PRPS we want to burn. if (!finalizing) { lockedPrpsToBurn -= burnableUnlockedPrps; } } // 2) Burn locked PRPS if there's not enough unlocked PRPS // Burn an additional amount of locked PRPS equal to the fuel if any if (fuelBurn.fuelType == FuelType.LOCKED_PRPS) { // The `burnFromLockedPrps` call will fail, if not enough PRPS can be burned. lockedPrpsToBurn += fuelBurn.amount; } if (lockedPrpsToBurn > 0) { uint96 dubiToMintFromLockedPrps = _burnFromLockedPrps({ from: from, unpacked: unpacked, lockedPrpsToBurn: lockedPrpsToBurn, createdAt: createdAt, finalizing: finalizing }); // We check 'greater than or equal' because it's possible to mint 0 new DUBI // e.g. when called right after a hodl where not enough time passed to generate new DUBI. uint96 dubiToMint = totalDubiToMint + dubiToMintFromLockedPrps; require(dubiToMint >= totalDubiToMint, "PRPS-6"); totalDubiToMint = dubiToMint; } else { // Sanity check for finalizes that don't touch locked PRPS assert(occupiedAmount == 0); } // Burn minted DUBI equal to the fuel if any if (fuelBurn.fuelType == FuelType.AUTO_MINTED_DUBI) { require(totalDubiToMint >= fuelBurn.amount, "PRPS-7"); totalDubiToMint -= fuelBurn.amount; } // Mint DUBI taking differences between burned locked/unlocked into account if (totalDubiToMint > 0) { _dubi.purposeMint(from, totalDubiToMint); } return burnableUnlockedPrps; } function _burnFromLockedPrps( address from, UnpackedData memory unpacked, uint96 lockedPrpsToBurn, uint32 createdAt, bool finalizing ) private returns (uint96) { // Reverts if the exact amount needed cannot be burned uint96 dubiToMintFromLockedPrps = _hodl.burnLockedPrps({ from: from, amount: lockedPrpsToBurn, dubiMintTimestamp: createdAt, burnPendingLockedPrps: finalizing }); require(unpacked.hodlBalance >= lockedPrpsToBurn, "PRPS-8"); unpacked.hodlBalance -= lockedPrpsToBurn; return dubiToMintFromLockedPrps; } function _callerIsDeployTimeKnownContract() internal override view returns (bool) { if (msg.sender == address(_dubi)) { return true; } return super._callerIsDeployTimeKnownContract(); } //--------------------------------------------------------------- // Fuel //--------------------------------------------------------------- /** * @dev Burns `fuel` from `from`. Can only be called by one of the deploy-time known contracts. */ function burnFuel(address from, TokenFuel memory fuel) public override { require(_callerIsDeployTimeKnownContract(), "PRPS-2"); _burnFuel(from, fuel); } function _burnFuel(address from, TokenFuel memory fuel) private { require(fuel.amount <= MAX_BOOSTER_FUEL, "PRPS-10"); require(from != address(0) && from != msg.sender, "PRPS-11"); if (fuel.tokenAlias == TOKEN_FUEL_ALIAS_UNLOCKED_PRPS) { // Burn fuel from unlocked PRPS UnpackedData memory unpacked = _unpackPackedData(_packedData[from]); require(unpacked.balance >= fuel.amount, "PRPS-7"); unpacked.balance -= fuel.amount; _packedData[from] = _packUnpackedData(unpacked); return; } if (fuel.tokenAlias == TOKEN_FUEL_ALIAS_LOCKED_PRPS) { // Burn fuel from locked PRPS UnpackedData memory unpacked = _unpackPackedData(_packedData[from]); require(unpacked.hodlBalance >= fuel.amount, "PRPS-7"); unpacked.hodlBalance -= fuel.amount; // We pass a mint timestamp, but that doesn't mean that DUBI is minted. // The returned DUBI that should be minted is ignored. // Reverts if not enough locked PRPS can be burned. _hodl.burnLockedPrps({ from: from, amount: fuel.amount, dubiMintTimestamp: uint32(block.timestamp), burnPendingLockedPrps: false }); _packedData[from] = _packUnpackedData(unpacked); return; } revert("PRPS-12"); } /** *@dev Burn the fuel of a `boostedSend` */ function _burnBoostedSendFuel( address from, BoosterFuel memory fuel, UnpackedData memory unpacked ) internal override returns (FuelBurn memory) { FuelBurn memory fuelBurn; if (fuel.unlockedPrps > 0) { require(fuel.unlockedPrps <= MAX_BOOSTER_FUEL, "PRPS-10"); require(unpacked.balance >= fuel.unlockedPrps, "PRPS-7"); unpacked.balance -= fuel.unlockedPrps; fuelBurn.amount = fuel.unlockedPrps; fuelBurn.fuelType = FuelType.UNLOCKED_PRPS; return fuelBurn; } if (fuel.lockedPrps > 0) { require(fuel.lockedPrps <= MAX_BOOSTER_FUEL, "PRPS-10"); // We pass a mint timestamp, but that doesn't mean that DUBI is minted. // The returned DUBI that should be minted is ignored. // Reverts if not enough locked PRPS can be burned. _hodl.burnLockedPrps({ from: from, amount: fuel.lockedPrps, dubiMintTimestamp: uint32(block.timestamp), burnPendingLockedPrps: false }); require(unpacked.hodlBalance >= fuel.lockedPrps, "PRPS-7"); unpacked.hodlBalance -= fuel.lockedPrps; fuelBurn.amount = fuel.lockedPrps; fuelBurn.fuelType = FuelType.LOCKED_PRPS; return fuelBurn; } // If the fuel is DUBI, then we have to reach out to the DUBI contract. if (fuel.dubi > 0) { // Reverts if the requested amount cannot be burned _dubi.burnFuel( from, TokenFuel({ tokenAlias: TOKEN_FUEL_ALIAS_DUBI, amount: fuel.dubi }) ); fuelBurn.amount = fuel.dubi; fuelBurn.fuelType = FuelType.DUBI; return fuelBurn; } return fuelBurn; } /** *@dev Burn the fuel of a `boostedBurn` */ function _burnBoostedBurnFuel( address from, BoosterFuel memory fuel, UnpackedData memory unpacked ) internal override returns (FuelBurn memory) { FuelBurn memory fuelBurn; if (fuel.unlockedPrps > 0) { require(fuel.unlockedPrps <= MAX_BOOSTER_FUEL, "PRPS-10"); require(unpacked.balance >= fuel.unlockedPrps, "PRPS-7"); unpacked.balance -= fuel.unlockedPrps; fuelBurn.amount = fuel.unlockedPrps; fuelBurn.fuelType = FuelType.UNLOCKED_PRPS; return fuelBurn; } if (fuel.lockedPrps > 0) { require(fuel.lockedPrps <= MAX_BOOSTER_FUEL, "PRPS-10"); require(unpacked.hodlBalance >= fuel.lockedPrps, "PRPS-7"); // Fuel is taken from hodl balance in _beforeBurn // unpacked.hodlBalance -= fuel.lockedPrps; fuelBurn.amount = fuel.lockedPrps; fuelBurn.fuelType = FuelType.LOCKED_PRPS; return fuelBurn; } if (fuel.intrinsicFuel > 0) { require(fuel.intrinsicFuel <= MAX_BOOSTER_FUEL, "PRPS-10"); fuelBurn.amount = fuel.intrinsicFuel; fuelBurn.fuelType = FuelType.AUTO_MINTED_DUBI; return fuelBurn; } // If the fuel is DUBI, then we have to reach out to the DUBI contract. if (fuel.dubi > 0) { // Reverts if the requested amount cannot be burned _dubi.burnFuel( from, TokenFuel({ tokenAlias: TOKEN_FUEL_ALIAS_DUBI, amount: fuel.dubi }) ); fuelBurn.amount = fuel.dubi; fuelBurn.fuelType = FuelType.DUBI; return fuelBurn; } // No fuel at all return fuelBurn; } //--------------------------------------------------------------- // Pending ops //--------------------------------------------------------------- function _getHasherContracts() internal override returns (address[] memory) { address[] memory hashers = new address[](5); hashers[0] = address(this); hashers[1] = address(_dubi); hashers[2] = _hodlAddress; hashers[3] = _externalAddress1; hashers[4] = _externalAddress2; return hashers; } /** * @dev Create a pending transfer by moving the funds of `spender` to this contract. * Special behavior applies to pending burns to account for locked PRPS. */ function _createPendingTransferInternal( OpHandle memory opHandle, address spender, address from, address to, uint256 amount, bytes memory data ) internal override returns (PendingTransfer memory) { if (opHandle.opType != OP_TYPE_BURN) { return // Nothing special to do for non-burns so just call parent implementation super._createPendingTransferInternal( opHandle, spender, from, to, amount, data ); } // When burning, we first use unlocked PRPS and match the remaining amount with locked PRPS from the Hodl contract. // Sanity check assert(amount < 2**96); uint96 transferAmount = uint96(amount); uint96 lockedPrpsAmount = transferAmount; UnpackedData memory unpacked = _unpackPackedData(_packedData[from]); // First try to move as much unlocked PRPS as possible to the PRPS address uint96 unlockedPrpsToMove = transferAmount; if (unlockedPrpsToMove > unpacked.balance) { unlockedPrpsToMove = unpacked.balance; } // Update the locked PRPS we have to use lockedPrpsAmount -= unlockedPrpsToMove; if (unlockedPrpsToMove > 0) { _move({from: from, to: address(this), amount: unlockedPrpsToMove}); } // If we still need locked PRPS, call into the Hodl contract. // This will also take pending hodls into account, if `from` has // some. if (lockedPrpsAmount > 0) { // Reverts if not the exact amount can be set to pending _hodl.setLockedPrpsToPending(from, lockedPrpsAmount); } // Create pending transfer return PendingTransfer({ spender: spender, transferAmount: transferAmount, to: to, occupiedAmount: lockedPrpsAmount, data: data }); } /** * @dev Hook that is called during revert of a pending op. * Reverts any changes to locked PRPS when 'opType' is burn. */ function _onRevertPendingOp( address user, uint8 opType, uint64 opId, uint96 transferAmount, uint96 occupiedAmount ) internal override { if (opType != OP_TYPE_BURN) { return; } // Extract the pending locked PRPS from the amount. if (occupiedAmount > 0) { _hodl.revertLockedPrpsSetToPending(user, occupiedAmount); } } //--------------------------------------------------------------- // Shared pending ops for Hodl //--------------------------------------------------------------- /** * @dev Creates a new opHandle with the given type for `user`. Hodl and Prps share the same * opCounter to enforce a consistent order in which pending ops are finalized/reverted * across contracts. This function can only be called by Hodl. */ function createNewOpHandleShared( IOptIn.OptInStatus memory optInStatus, address user, uint8 opType ) public onlyHodl returns (OpHandle memory) { return _createNewOpHandle(optInStatus, user, opType); } /** * @dev Delete the op handle with the given `opId` from `user`. Hodl and Prps share the same * opCounter to enforce a consistent order in which pending ops are finalized/reverted * across contracts. This function can only be called by Hodl. */ function deleteOpHandleShared(address user, OpHandle memory opHandle) public onlyHodl returns (bool) { _deleteOpHandle(user, opHandle); return true; } /** * @dev Get the next op id for `user`. Hodl and Prps share the same * opCounter to enforce a consistent order in which pending ops are finalized/reverted * across contracts. This function can only be called by Hodl. */ function assertFinalizeFIFOShared(address user, uint64 opId) public onlyHodl returns (bool) { _assertFinalizeFIFO(user, opId); return true; } /** * @dev Get the next op id for `user`. Hodl and Prps share the same * opCounter to enforce a consistent order in which pending ops are finalized/reverted * across contracts. This function can only be called by Hodl. */ function assertRevertLIFOShared(address user, uint64 opId) public onlyHodl returns (bool) { _assertRevertLIFO(user, opId); return true; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/introspection/IERC1820Registry.sol"; import "./IBoostableERC20.sol"; import "./BoostableERC20.sol"; /** * @dev This is a heavily modified fork of @openzeppelin/contracts/token/ERC20/ERC20.sol (3.1.0) */ abstract contract ERC20 is IERC20, IBoostableERC20, BoostableERC20, Ownable { using SafeMath for uint256; // NOTE: In contrary to the Transfer event, the Burned event always // emits the amount including the burned fuel if any. // The amount is stored in the lower 96 bits of `amountAndFuel`, // followed by 3 bits to encode the type of fuel used and finally // another 96 bits for the fuel amount. // // 0 96 99 195 256 // amount fuelType fuelAmount padding // event Burned(uint256 amountAndFuel, bytes data); enum FuelType {NONE, UNLOCKED_PRPS, LOCKED_PRPS, DUBI, AUTO_MINTED_DUBI} struct FuelBurn { FuelType fuelType; uint96 amount; } uint256 private _totalSupply; string private _name; string private _symbol; address internal immutable _hodlAddress; address internal immutable _externalAddress1; address internal immutable _externalAddress2; address internal immutable _externalAddress3; IERC1820Registry internal constant _ERC1820_REGISTRY = IERC1820Registry( 0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24 ); // Mapping of address to packed data. // For efficiency reasons the token balance is a packed uint96 alongside // other data. The packed data has the following layout: // // MSB uint256 LSB // uint64 nonce | uint96 hodlBalance | uint96 balance // // balance: the balance of a token holder that can be transferred freely // hodlBalance: the balance of a token holder that is hodled // nonce: a sequential number used for booster replay protection // // Only PRPS utilizes `hodlBalance`. For DUBI it is always 0. // mapping(address => uint256) internal _packedData; struct UnpackedData { uint96 balance; uint96 hodlBalance; uint64 nonce; } function _unpackPackedData(uint256 packedData) internal pure returns (UnpackedData memory) { UnpackedData memory unpacked; // 1) Read balance from the first 96 bits unpacked.balance = uint96(packedData); // 2) Read hodlBalance from the next 96 bits unpacked.hodlBalance = uint96(packedData >> 96); // 3) Read nonce from the next 64 bits unpacked.nonce = uint64(packedData >> (96 + 96)); return unpacked; } function _packUnpackedData(UnpackedData memory unpacked) internal pure returns (uint256) { uint256 packedData; // 1) Write balance to the first 96 bits packedData |= unpacked.balance; // 2) Write hodlBalance to the the next 96 bits packedData |= uint256(unpacked.hodlBalance) << 96; // 3) Write nonce to the next 64 bits packedData |= uint256(unpacked.nonce) << (96 + 96); return packedData; } // ERC20-allowances mapping(address => mapping(address => uint256)) private _allowances; //--------------------------------------------------------------- // Pending state for non-boosted operations while opted-in //--------------------------------------------------------------- uint8 internal constant OP_TYPE_SEND = BOOST_TAG_SEND; uint8 internal constant OP_TYPE_BURN = BOOST_TAG_BURN; struct PendingTransfer { // NOTE: For efficiency reasons balances are stored in a uint96 which is sufficient // since we only use 18 decimals. // // Two amounts are associated with a pending transfer, to allow deriving contracts // to store extra information. // // E.g. PRPS makes use of this by encoding the pending locked PRPS in the // `occupiedAmount` field. // address spender; uint96 transferAmount; address to; uint96 occupiedAmount; bytes data; } // A mapping of hash(user, opId) to pending transfers. Pending burns are also considered regular transfers. mapping(bytes32 => PendingTransfer) private _pendingTransfers; //--------------------------------------------------------------- constructor( string memory name, string memory symbol, address optIn, address hodl, address externalAddress1, address externalAddress2, address externalAddress3 ) public Ownable() BoostableERC20(optIn) { _name = name; _symbol = symbol; _hodlAddress = hodl; _externalAddress1 = externalAddress1; _externalAddress2 = externalAddress2; _externalAddress3 = externalAddress3; // register interfaces _ERC1820_REGISTRY.setInterfaceImplementer( address(this), keccak256("BoostableERC20Token"), address(this) ); _ERC1820_REGISTRY.setInterfaceImplementer( address(this), keccak256("ERC20Token"), address(this) ); } /** * @dev Returns the name of the token. */ function name() public view returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals. */ function decimals() public pure returns (uint8) { return 18; } /** * @dev Returns the current nonce of `account` */ function getNonce(address account) external override view returns (uint64) { UnpackedData memory unpacked = _unpackPackedData(_packedData[account]); return unpacked.nonce; } /** * @dev Returns the total supply */ function totalSupply() external override(IBoostableERC20, IERC20) view returns (uint256) { return _totalSupply; } /** * @dev Returns the amount of tokens owned by an account (`tokenHolder`). */ function balanceOf(address tokenHolder) public override(IBoostableERC20, IERC20) view returns (uint256) { // Return the balance of the holder that is not hodled (i.e. first 96 bits of the packeData) return uint96(_packedData[tokenHolder]); } /** * @dev Returns the unpacked data struct of `tokenHolder` */ function unpackedDataOf(address tokenHolder) public view returns (UnpackedData memory) { return _unpackPackedData(_packedData[tokenHolder]); } /** * @dev Mints `amount` new tokens for `to`. * * To make things more efficient, the total supply is optionally packed into the passed * amount where the first 96 bits are used for the actual amount and the following 96 bits * for the total supply. * */ function mint(address to, uint256 amount) public onlyOwner { _mint(to, amount); } function _mintInitialSupply(address to, uint256 amount) internal { // _mint does not update the totalSupply by default, unless the second 96 bits // passed are non-zero - in which case the non-zero value becomes the new total supply. // So in order to get the correct initial supply, we have to mirror the lower 96 bits // to the following 96 bits. amount = amount | (amount << 96); _mint(to, amount); } function _mint(address to, uint256 amount) internal { require(to != address(0), "ERC20-1"); // The actual amount to mint (=lower 96 bits) uint96 amountToMint = uint96(amount); // The new total supply, which may be 0 in which case no update is performed. uint96 updatedTotalSupply = uint96(amount >> 96); // Update state variables if (updatedTotalSupply > 0) { _totalSupply = updatedTotalSupply; } // Update packed data and check for uint96 overflow UnpackedData memory unpacked = _unpackPackedData(_packedData[to]); uint96 updatedBalance = unpacked.balance + amountToMint; // The overflow check also takes the hodlBalance into account require( updatedBalance + unpacked.hodlBalance >= unpacked.balance, "ERC20-2" ); unpacked.balance = updatedBalance; _packedData[to] = _packUnpackedData(unpacked); emit Transfer(address(0), to, amountToMint); } /** * @dev Transfer `amount` from msg.sender to `recipient` */ function transfer(address recipient, uint256 amount) public override(IBoostableERC20, IERC20) returns (bool) { _assertSenderRecipient(msg.sender, recipient); // Never create a pending transfer if msg.sender is a deploy-time known contract if (!_callerIsDeployTimeKnownContract()) { // Create pending transfer if sender is opted-in and the permaboost is active address from = msg.sender; IOptIn.OptInStatus memory optInStatus = getOptInStatus(from); if (optInStatus.isOptedIn && optInStatus.permaBoostActive) { _createPendingTransfer({ opType: OP_TYPE_SEND, spender: msg.sender, from: msg.sender, to: recipient, amount: amount, data: "", optInStatus: optInStatus }); return true; } } _move({from: msg.sender, to: recipient, amount: amount}); return true; } /** * @dev Burns `amount` of msg.sender. * * Also emits a {IERC20-Transfer} event for ERC20 compatibility. */ function burn(uint256 amount, bytes memory data) public { // Create pending burn if sender is opted-in and the permaboost is active IOptIn.OptInStatus memory optInStatus = getOptInStatus(msg.sender); if (optInStatus.isOptedIn && optInStatus.permaBoostActive) { _createPendingTransfer({ opType: OP_TYPE_BURN, spender: msg.sender, from: msg.sender, to: address(0), amount: amount, data: data, optInStatus: optInStatus }); return; } _burn({ from: msg.sender, amount: amount, data: data, incrementNonce: false }); } /** * @dev Moves `amount` tokens from `sender` to `recipient`. * * Can only be used by deploy-time known contracts. * * IBoostableERC20 extension */ function boostedTransferFrom( address sender, address recipient, uint256 amount, bytes calldata data ) public override returns (bool) { _assertSenderRecipient(sender, recipient); IOptIn.OptInStatus memory optInStatus = getOptInStatus(sender); // Only transfer if `sender` is a deploy-time known contract, otherwise // revert. require( _isDeployTimeKnownContractAndCanTransfer( sender, recipient, amount, optInStatus, data ), "ERC20-17" ); _move({from: sender, to: recipient, amount: amount}); return true; } function _isDeployTimeKnownContractAndCanTransfer( address sender, address recipient, uint256 amount, IOptIn.OptInStatus memory optInStatus, bytes memory data ) private view returns (bool) { // If the caller not a deploy-time known contract, the transfer is not allowed if (!_callerIsDeployTimeKnownContract()) { return false; } if (msg.sender != _externalAddress3) { return true; } // _externalAddress3 passes a flag via `data` that indicates whether it is a boosted transaction // or not. uint8 isBoostedBits; assembly { // Load flag using a 1-byte offset, because `mload` always reads // 32-bytes at once and the first 32 bytes of `data` contain it's length. isBoostedBits := mload(add(data, 0x01)) } // Reading into a 'bool' directly doesn't work for some reason if (isBoostedBits & 1 == 1) { return true; } // If the latter, then _externalAddress3 can only transfer the funds if either: // - the permaboost is not active // - `sender` is not opted-in to begin with // // If `sender` is opted-in and the permaboost is active, _externalAddress3 cannot // take funds, except when boosted. Here the booster trusts _externalAddress3, since it already // verifies that `sender` provided a valid signature. // // This is special to _externalAddress3, other deploy-time known contracts do not make use of `data`. if (optInStatus.permaBoostActive && optInStatus.isOptedIn) { return false; } return true; } /** * @dev Verify the booster payload against the nonce that is stored in the packed data of an account. * The increment happens outside of this function, when the balance is updated. */ function _verifyNonce(BoosterPayload memory payload, uint64 currentNonce) internal pure { require(currentNonce == payload.nonce - 1, "ERC20-5"); } //--------------------------------------------------------------- // Boosted functions //--------------------------------------------------------------- /** * @dev Perform multiple `boostedSend` calls in a single transaction. * * NOTE: Booster extension */ function boostedSendBatch( BoostedSend[] memory sends, Signature[] memory signatures ) external { require( sends.length > 0 && sends.length == signatures.length, "ERC20-6" ); for (uint256 i = 0; i < sends.length; i++) { boostedSend(sends[i], signatures[i]); } } /** * @dev Perform multiple `boostedBurn` calls in a single transaction. * * NOTE: Booster extension */ function boostedBurnBatch( BoostedBurn[] memory burns, Signature[] memory signatures ) external { require( burns.length > 0 && burns.length == signatures.length, "ERC20-6" ); for (uint256 i = 0; i < burns.length; i++) { boostedBurn(burns[i], signatures[i]); } } /** * @dev Send `amount` tokens from `sender` to recipient`. * The `sender` must be opted-in and the `msg.sender` must be a trusted booster. * * NOTE: Booster extension */ function boostedSend(BoostedSend memory send, Signature memory signature) public { address from = send.sender; address to = send.recipient; UnpackedData memory unpackedFrom = _unpackPackedData(_packedData[from]); UnpackedData memory unpackedTo = _unpackPackedData(_packedData[to]); // We verify the nonce separately, since it's stored next to the balance _verifyNonce(send.boosterPayload, unpackedFrom.nonce); _verifyBoostWithoutNonce( send.sender, hashBoostedSend(send, msg.sender), send.boosterPayload, signature ); FuelBurn memory fuelBurn = _burnBoostedSendFuel( from, send.fuel, unpackedFrom ); _moveUnpacked({ from: send.sender, unpackedFrom: unpackedFrom, to: send.recipient, unpackedTo: unpackedTo, amount: send.amount, fuelBurn: fuelBurn, incrementNonce: true }); } /** * @dev Burn the fuel of a `boostedSend`. Returns a `FuelBurn` struct containing information about the burn. */ function _burnBoostedSendFuel( address from, BoosterFuel memory fuel, UnpackedData memory unpacked ) internal virtual returns (FuelBurn memory); /** * @dev Burn `amount` tokens from `account`. * The `account` must be opted-in and the `msg.sender` must be a trusted booster. * * NOTE: Booster extension */ function boostedBurn( BoostedBurn memory message, // A signature, that is compared against the function payload and only accepted if signed by 'sender' Signature memory signature ) public { address from = message.account; UnpackedData memory unpacked = _unpackPackedData(_packedData[from]); // We verify the nonce separately, since it's stored next to the balance _verifyNonce(message.boosterPayload, unpacked.nonce); _verifyBoostWithoutNonce( message.account, hashBoostedBurn(message, msg.sender), message.boosterPayload, signature ); FuelBurn memory fuelBurn = _burnBoostedBurnFuel( from, message.fuel, unpacked ); _burnUnpacked({ from: message.account, unpacked: unpacked, amount: message.amount, data: message.data, incrementNonce: true, fuelBurn: fuelBurn }); } /** * @dev Burn the fuel of a `boostedSend`. Returns a `FuelBurn` struct containing information about the burn. */ function _burnBoostedBurnFuel( address from, BoosterFuel memory fuel, UnpackedData memory unpacked ) internal virtual returns (FuelBurn memory); function burnFuel(address from, TokenFuel memory fuel) external virtual override {} //--------------------------------------------------------------- /** * @dev Get the allowance of `spender` for `holder` */ function allowance(address holder, address spender) public override(IBoostableERC20, IERC20) view returns (uint256) { return _allowances[holder][spender]; } /** * @dev Increase the allowance of `spender` by `value` for msg.sender */ function approve(address spender, uint256 value) public override(IBoostableERC20, IERC20) returns (bool) { address holder = msg.sender; _assertSenderRecipient(holder, spender); _approve(holder, spender, value); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _assertSenderRecipient(msg.sender, spender); _approve( msg.sender, spender, _allowances[msg.sender][spender].add(addedValue) ); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _assertSenderRecipient(msg.sender, spender); _approve( msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue, "ERC20-18") ); return true; } /** * @dev Transfer `amount` from `holder` to `recipient`. * * `msg.sender` requires an allowance >= `amount` of `holder`. */ function transferFrom( address holder, address recipient, uint256 amount ) public override(IBoostableERC20, IERC20) returns (bool) { _assertSenderRecipient(holder, recipient); address spender = msg.sender; // Create pending transfer if the token holder is opted-in and the permaboost is active IOptIn.OptInStatus memory optInStatus = getOptInStatus(holder); if (optInStatus.isOptedIn && optInStatus.permaBoostActive) { // Ignore allowances if holder is opted-in require(holder == spender, "ERC20-7"); _createPendingTransfer({ opType: OP_TYPE_SEND, spender: spender, from: holder, to: recipient, amount: amount, data: "", optInStatus: optInStatus }); return true; } // Not opted-in, but we still need to check approval of the given spender _approve( holder, spender, _allowances[holder][spender].sub(amount, "ERC20-4") ); _move({from: holder, to: recipient, amount: amount}); return true; } /** * @dev Burn tokens * @param from address token holder address * @param amount uint256 amount of tokens to burn * @param data bytes extra information provided by the token holder * @param incrementNonce whether to increment the nonce or not - only true for boosted burns */ function _burn( address from, uint256 amount, bytes memory data, bool incrementNonce ) internal virtual { require(from != address(0), "ERC20-8"); UnpackedData memory unpacked = _unpackPackedData(_packedData[from]); // Empty fuel burn FuelBurn memory fuelBurn; _burnUnpacked({ from: from, unpacked: unpacked, amount: amount, data: data, incrementNonce: incrementNonce, fuelBurn: fuelBurn }); } function _burnUnpacked( address from, UnpackedData memory unpacked, uint256 amount, bytes memory data, bool incrementNonce, FuelBurn memory fuelBurn ) internal { // _beforeBurn allows deriving contracts to run additional logic and affect the amount // that is actually getting burned. E.g. when burning PRPS, a portion of it might be taken // from the `hodlBalance`. Thus the returned `burnAmount` overrides `amount` and will be // subtracted from the actual `balance`. uint96 actualBurnAmount = _beforeBurn({ from: from, unpacked: unpacked, transferAmount: uint96(amount), occupiedAmount: 0, createdAt: uint32(block.timestamp), fuelBurn: fuelBurn, finalizing: false }); // Update to new balance if (incrementNonce) { // The nonce uses 64 bits, so a overflow is pretty much impossible // via increments of 1. unpacked.nonce++; } if (actualBurnAmount > 0) { require(unpacked.balance >= actualBurnAmount, "ERC20-9"); unpacked.balance -= actualBurnAmount; } // Update packed data by writing to storage _packedData[from] = _packUnpackedData(unpacked); // Total supply can be updated in batches elsewhere, shaving off another >5k gas. // _totalSupply = _totalSupply.sub(amount); // The `Burned` event is emitted with the total amount that got burned. // Furthermore, the fuel used is encoded in the upper bits. uint256 amountAndFuel; // Set first 96 bits to amount amountAndFuel |= uint96(amount); // Set next 3 bits to fuel type uint8 fuelType = uint8(fuelBurn.fuelType); amountAndFuel |= uint256(fuelType) << 96; // Set next 96 bits to fuel amount amountAndFuel |= uint256(fuelBurn.amount) << (96 + 3); emit Burned(amountAndFuel, data); // We emit a transfer event with the actual burn amount excluding burned `hodlBalance`. emit Transfer(from, address(0), actualBurnAmount); } /** * @dev Allow deriving contracts to prepare a burn. By default it behaves like an identity function * and just returns the amount passed in. */ function _beforeBurn( address from, UnpackedData memory unpacked, uint96 transferAmount, uint96 occupiedAmount, uint32 createdAt, FuelBurn memory fuelBurn, bool finalizing ) internal virtual returns (uint96) { return transferAmount; } function _move( address from, address to, uint256 amount ) internal { UnpackedData memory unpackedFrom = _unpackPackedData(_packedData[from]); UnpackedData memory unpackedTo = _unpackPackedData(_packedData[to]); // Empty fuel burn FuelBurn memory fuelBurn; _moveUnpacked({ from: from, unpackedFrom: unpackedFrom, to: to, unpackedTo: unpackedTo, amount: amount, incrementNonce: false, fuelBurn: fuelBurn }); } function _moveUnpacked( address from, UnpackedData memory unpackedFrom, address to, UnpackedData memory unpackedTo, uint256 amount, bool incrementNonce, FuelBurn memory fuelBurn ) internal { require(from != to, "ERC20-19"); // Increment nonce of sender if it's a boosted send if (incrementNonce) { // The nonce uses 64 bits, so a overflow is pretty much impossible // via increments of 1. unpackedFrom.nonce++; } // Check if sender has enough tokens uint96 transferAmount = uint96(amount); require(unpackedFrom.balance >= transferAmount, "ERC20-10"); // Subtract transfer amount from sender balance unpackedFrom.balance -= transferAmount; // Check that recipient balance doesn't overflow uint96 updatedRecipientBalance = unpackedTo.balance + transferAmount; require(updatedRecipientBalance >= unpackedTo.balance, "ERC20-12"); unpackedTo.balance = updatedRecipientBalance; _packedData[from] = _packUnpackedData(unpackedFrom); _packedData[to] = _packUnpackedData(unpackedTo); // The transfer amount does not include any used fuel emit Transfer(from, to, transferAmount); } /** * @dev See {ERC20-_approve}. */ function _approve( address holder, address spender, uint256 value ) internal { _allowances[holder][spender] = value; emit Approval(holder, spender, value); } function _assertSenderRecipient(address sender, address recipient) private pure { require(sender != address(0) && recipient != address(0), "ERC20-13"); } /** * @dev Checks whether msg.sender is a deploy-time known contract or not. */ function _callerIsDeployTimeKnownContract() internal virtual view returns (bool) { if (msg.sender == _hodlAddress) { return true; } if (msg.sender == _externalAddress1) { return true; } if (msg.sender == _externalAddress2) { return true; } if (msg.sender == _externalAddress3) { return true; } return false; } //--------------------------------------------------------------- // Pending ops //--------------------------------------------------------------- /** * @dev Create a pending transfer */ function _createPendingTransfer( uint8 opType, address spender, address from, address to, uint256 amount, bytes memory data, IOptIn.OptInStatus memory optInStatus ) private { OpHandle memory opHandle = _createNewOpHandle( optInStatus, from, opType ); PendingTransfer memory pendingTransfer = _createPendingTransferInternal( opHandle, spender, from, to, amount, data ); _pendingTransfers[_getOpKey(from, opHandle.opId)] = pendingTransfer; // Emit PendingOp event emit PendingOp(from, opHandle.opId, opHandle.opType); } /** * @dev Create a pending transfer by moving the funds of `spender` to this contract. * Deriving contracts may override this function. */ function _createPendingTransferInternal( OpHandle memory opHandle, address spender, address from, address to, uint256 amount, bytes memory data ) internal virtual returns (PendingTransfer memory) { // Move funds into this contract // Reverts if `from` has less than `amount` tokens. _move({from: from, to: address(this), amount: amount}); // Create op PendingTransfer memory pendingTransfer = PendingTransfer({ transferAmount: uint96(amount), spender: spender, occupiedAmount: 0, to: to, data: data }); return pendingTransfer; } /** * @dev Finalize a pending op */ function finalizePendingOp(address user, OpHandle memory opHandle) public { uint8 opType = opHandle.opType; // Assert that the caller (msg.sender) is allowed to finalize the given op uint32 createdAt = uint32(_assertCanFinalize(user, opHandle)); // Reverts if opId doesn't exist PendingTransfer storage pendingTransfer = _safeGetPendingTransfer( user, opHandle.opId ); // Cleanup // NOTE: We do not delete the pending transfer struct, because it only makes it // more expensive since we already hit the gas refund limit. // // delete _pendingTransfers[_getOpKey(user, opHandle.opId)]; // // The difference is ~13k gas. // // Deleting the op handle is enough to invalidate an opId forever: _deleteOpHandle(user, opHandle); // Call op type specific finalize if (opType == OP_TYPE_SEND) { _finalizeTransferOp(pendingTransfer, user, createdAt); } else if (opType == OP_TYPE_BURN) { _finalizePendingBurn(pendingTransfer, user, createdAt); } else { revert("ERC20-15"); } // Emit event emit FinalizedOp(user, opHandle.opId, opType); } /** * @dev Finalize a pending transfer */ function _finalizeTransferOp( PendingTransfer storage pendingTransfer, address from, uint32 createdAt ) private { address to = pendingTransfer.to; uint96 transferAmount = pendingTransfer.transferAmount; address _this = address(this); UnpackedData memory unpackedThis = _unpackPackedData( _packedData[_this] ); UnpackedData memory unpackedTo = _unpackPackedData(_packedData[to]); // Check that sender balance does not overflow require(unpackedThis.balance >= transferAmount, "ERC20-2"); unpackedThis.balance -= transferAmount; // Check that recipient doesn't overflow uint96 updatedBalanceRecipient = unpackedTo.balance + transferAmount; require(updatedBalanceRecipient >= unpackedTo.balance, "ERC20-2"); unpackedTo.balance = updatedBalanceRecipient; _packedData[_this] = _packUnpackedData(unpackedThis); _packedData[to] = _packUnpackedData(unpackedTo); // Transfer event is emitted with original sender emit Transfer(from, to, transferAmount); } /** * @dev Finalize a pending burn */ function _finalizePendingBurn( PendingTransfer storage pendingTransfer, address from, uint32 createdAt ) private { uint96 transferAmount = pendingTransfer.transferAmount; // We pass the packedData of `from` to `_beforeBurn`, because it PRPS needs to update // the `hodlBalance` which is NOT on the contract's own packedData. UnpackedData memory unpackedFrom = _unpackPackedData(_packedData[from]); // Empty fuel burn FuelBurn memory fuelBurn; uint96 burnAmountExcludingLockedPrps = _beforeBurn({ from: from, unpacked: unpackedFrom, transferAmount: transferAmount, occupiedAmount: pendingTransfer.occupiedAmount, createdAt: createdAt, fuelBurn: fuelBurn, finalizing: true }); // Update to new balance // NOTE: We change the balance of this contract, because that's where // the pending PRPS went to. address _this = address(this); UnpackedData memory unpackedOfContract = _unpackPackedData( _packedData[_this] ); require( unpackedOfContract.balance >= burnAmountExcludingLockedPrps, "ERC20-2" ); unpackedOfContract.balance -= burnAmountExcludingLockedPrps; _packedData[_this] = _packUnpackedData(unpackedOfContract); _packedData[from] = _packUnpackedData(unpackedFrom); // Furthermore, total supply can be updated elsewhere, shaving off another >5k gas. // _totalSupply = _totalSupply.sub(amount); // Emit events using the same `transferAmount` instead of what `_beforeBurn` // returned which is only used for updating the balance correctly. emit Burned(transferAmount, pendingTransfer.data); emit Transfer(from, address(0), transferAmount); } /** * @dev Revert a pending operation. * * Only the opted-in booster can revert a transaction if it provides a signed and still valid booster message * from the original sender. */ function revertPendingOp( address user, OpHandle memory opHandle, bytes memory boosterMessage, Signature memory signature ) public { // Prepare revert, including permission check and prevents reentrancy for same opHandle. _prepareOpRevert({ user: user, opHandle: opHandle, boosterMessage: boosterMessage, signature: signature }); // Now perform the actual revert of the pending op _revertPendingOp(user, opHandle.opType, opHandle.opId); } /** * @dev Revert a pending transfer */ function _revertPendingOp( address user, uint8 opType, uint64 opId ) private { PendingTransfer storage pendingTransfer = _safeGetPendingTransfer( user, opId ); uint96 transferAmount = pendingTransfer.transferAmount; uint96 occupiedAmount = pendingTransfer.occupiedAmount; // Move funds from this contract back to the original sender. Transfers and burns // are reverted the same way. We only transfer back the `transferAmount` - that is the amount // that actually got moved into this contract. The occupied amount is released during `onRevertPendingOp` // by the deriving contract. _move({from: address(this), to: user, amount: transferAmount}); // Call hook to allow deriving contracts to perform additional cleanup _onRevertPendingOp(user, opType, opId, transferAmount, occupiedAmount); // NOTE: we do not clean up the ops mapping, because we already hit the // gas refund limit. // delete _pendingTransfers[_getOpKey(user, opHandle.opId)]; // Emit event emit RevertedOp(user, opId, opType); } /** * @dev Hook that is called during revert of a pending transfer. * Allows deriving contracts to perform additional cleanup. */ function _onRevertPendingOp( address user, uint8 opType, uint64 opId, uint96 transferAmount, uint96 occupiedAmount ) internal virtual {} /** * @dev Safely get a pending transfer. Reverts if it doesn't exist. */ function _safeGetPendingTransfer(address user, uint64 opId) private view returns (PendingTransfer storage) { PendingTransfer storage pendingTransfer = _pendingTransfers[_getOpKey( user, opId )]; require(pendingTransfer.spender != address(0), "ERC20-16"); return pendingTransfer; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "./ERC20.sol"; import "./Purpose.sol"; contract Dubi is ERC20 { Purpose private immutable _prps; constructor( uint256 initialSupply, address optIn, address purpose, address hodl, address externalAddress1, address externalAddress2, address externalAddress3 ) public ERC20( "Decentralized Universal Basic Income", "DUBI", optIn, hodl, externalAddress1, externalAddress2, externalAddress3 ) { _mintInitialSupply(msg.sender, initialSupply); _prps = Purpose(purpose); } function hodlMint(address to, uint256 amount) public { require(msg.sender == _hodlAddress, "DUBI-2"); _mint(to, amount); } function purposeMint(address to, uint256 amount) public { require(msg.sender == address(_prps), "DUBI-3"); _mint(to, amount); } function _callerIsDeployTimeKnownContract() internal override view returns (bool) { if (msg.sender == address(_prps)) { return true; } return super._callerIsDeployTimeKnownContract(); } //--------------------------------------------------------------- // Fuel //--------------------------------------------------------------- /** * @dev Burns `fuel` from `from`. Can only be called by one of the deploy-time known contracts. */ function burnFuel(address from, TokenFuel memory fuel) public override { require(_callerIsDeployTimeKnownContract(), "DUBI-1"); _burnFuel(from, fuel); } function _burnFuel(address from, TokenFuel memory fuel) private { require(fuel.amount <= MAX_BOOSTER_FUEL, "DUBI-5"); require(from != address(0) && from != msg.sender, "DUBI-6"); if (fuel.tokenAlias == TOKEN_FUEL_ALIAS_DUBI) { // Burn fuel from DUBI UnpackedData memory unpacked = _unpackPackedData(_packedData[from]); require(unpacked.balance >= fuel.amount, "DUBI-7"); unpacked.balance -= fuel.amount; _packedData[from] = _packUnpackedData(unpacked); return; } revert("DUBI-8"); } /** *@dev Burn the fuel of a `boostedSend` */ function _burnBoostedSendFuel( address from, BoosterFuel memory fuel, UnpackedData memory unpacked ) internal override returns (FuelBurn memory) { FuelBurn memory fuelBurn; if (fuel.dubi > 0) { require(fuel.dubi <= MAX_BOOSTER_FUEL, "DUBI-5"); // From uses his own DUBI to fuel the boost require(unpacked.balance >= fuelBurn.amount, "DUBI-7"); unpacked.balance -= fuel.dubi; fuelBurn.amount = fuel.dubi; fuelBurn.fuelType = FuelType.DUBI; return fuelBurn; } // If the fuel is PRPS, then we have to reach out to the PRPS contract. if (fuel.unlockedPrps > 0) { // Reverts if the requested amount cannot be burned _prps.burnFuel( from, TokenFuel({ tokenAlias: TOKEN_FUEL_ALIAS_UNLOCKED_PRPS, amount: fuel.unlockedPrps }) ); fuelBurn.amount = fuel.unlockedPrps; fuelBurn.fuelType = FuelType.UNLOCKED_PRPS; return fuelBurn; } if (fuel.lockedPrps > 0) { // Reverts if the requested amount cannot be burned _prps.burnFuel( from, TokenFuel({ tokenAlias: TOKEN_FUEL_ALIAS_LOCKED_PRPS, amount: fuel.lockedPrps }) ); fuelBurn.amount = fuel.lockedPrps; fuelBurn.fuelType = FuelType.LOCKED_PRPS; return fuelBurn; } // No fuel at all return fuelBurn; } /** *@dev Burn the fuel of a `boostedBurn` */ function _burnBoostedBurnFuel( address from, BoosterFuel memory fuel, UnpackedData memory unpacked ) internal override returns (FuelBurn memory) { FuelBurn memory fuelBurn; // If the fuel is DUBI, then we can remove it directly if (fuel.dubi > 0) { require(fuel.dubi <= MAX_BOOSTER_FUEL, "DUBI-5"); require(unpacked.balance >= fuel.dubi, "DUBI-7"); unpacked.balance -= fuel.dubi; fuelBurn.amount = fuel.dubi; fuelBurn.fuelType = FuelType.DUBI; return fuelBurn; } // If the fuel is PRPS, then we have to reach out to the PRPS contract. if (fuel.unlockedPrps > 0) { // Reverts if the requested amount cannot be burned _prps.burnFuel( from, TokenFuel({ tokenAlias: TOKEN_FUEL_ALIAS_UNLOCKED_PRPS, amount: fuel.unlockedPrps }) ); fuelBurn.amount = fuel.unlockedPrps; fuelBurn.fuelType = FuelType.UNLOCKED_PRPS; return fuelBurn; } if (fuel.lockedPrps > 0) { // Reverts if the requested amount cannot be burned _prps.burnFuel( from, TokenFuel({ tokenAlias: TOKEN_FUEL_ALIAS_LOCKED_PRPS, amount: fuel.lockedPrps }) ); // No direct fuel, but we still return a indirect fuel so that it can be added // to the burn event. fuelBurn.amount = fuel.lockedPrps; fuelBurn.fuelType = FuelType.LOCKED_PRPS; return fuelBurn; } // DUBI has no intrinsic fuel if (fuel.intrinsicFuel > 0) { revert("DUBI-8"); } // No fuel at all return fuelBurn; } //--------------------------------------------------------------- // Pending ops //--------------------------------------------------------------- function _getHasherContracts() internal override returns (address[] memory) { address[] memory hashers = new address[](5); hashers[0] = address(this); hashers[1] = address(_prps); hashers[2] = _hodlAddress; hashers[3] = _externalAddress1; hashers[4] = _externalAddress2; return hashers; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IHodl { /** * @dev Lock the given amount of PRPS for the specified period (or infinitely) * for DUBI. */ function hodl( uint24 id, uint96 amountPrps, uint16 duration, address dubiBeneficiary, address prpsBeneficiary ) external; /** * @dev Release a hodl of `prpsBeneficiary` with the given `creator` and `id`. */ function release( uint24 id, address prpsBeneficiary, address creator ) external; /** * @dev Withdraw can be used to withdraw DUBI from infinitely locked PRPS. * The amount of DUBI withdrawn depends on the time passed since the last withdrawal. */ function withdraw( uint24 id, address prpsBeneficiary, address creator ) external; /** * @dev Burn `amount` of `from`'s locked and/or pending PRPS. * * This function is supposed to be only called by the PRPS contract. * * Returns the amount of DUBI that needs to be minted. */ function burnLockedPrps( address from, uint96 amount, uint32 dubiMintTimestamp, bool burnPendingLockedPrps ) external returns (uint96); /** * @dev Set `amount` of `from`'s locked PRPS to pending. * * This function is supposed to be only called by the PRPS contract. * * Returns the amount of locked PRPS that could be set to pending. */ function setLockedPrpsToPending(address from, uint96 amount) external; /** * @dev Revert `amount` of `from`'s pending locked PRPS to not pending. * * This function is supposed to be only called by the PRPS contract and returns */ function revertLockedPrpsSetToPending(address account, uint96 amount) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; // NOTE: we ignore leap-seconds etc. library MintMath { // The maximum number of seconds per month (365 * 24 * 60 * 60 / 12) uint32 public constant SECONDS_PER_MONTH = 2628000; // The maximum number of days PRPS can be finitely locked for uint16 public constant MAX_FINITE_LOCK_DURATION_DAYS = 365; // The maximum number of seconds PRPS can be finitely locked for uint32 public constant MAX_FINITE_LOCK_DURATION_SECONDS = uint32( MAX_FINITE_LOCK_DURATION_DAYS ) * 24 * 60 * 60; /** * @dev Calculates the DUBI to mint based on the given amount of PRPS and duration in days. * NOTE: We trust the caller to ensure that the duration between 1 and 365. */ function calculateDubiToMintByDays( uint256 amountPrps, uint16 durationInDays ) internal pure returns (uint96) { uint32 durationInSeconds = uint32(durationInDays) * 24 * 60 * 60; return calculateDubiToMintBySeconds(amountPrps, durationInSeconds); } /** * @dev Calculates the DUBI to mint based on the given amount of PRPS and duration in seconds. */ function calculateDubiToMintBySeconds( uint256 amountPrps, uint32 durationInSeconds ) internal pure returns (uint96) { // NOTE: We do not use safe math for efficiency reasons uint256 _percentage = percentage( durationInSeconds, MAX_FINITE_LOCK_DURATION_SECONDS, 18 // precision in WEI, 10^18 ) * 4; // A full lock grants 4%, so multiply by 4. // Multiply PRPS by the percentage and then divide by the precision (=10^8) // from the previous step uint256 _dubiToMint = (amountPrps * _percentage) / (1 ether * 100); // multiply by 100, because we deal with percentages // Assert that the calculated DUBI never overflows uint96 assert(_dubiToMint < 2**96); return uint96(_dubiToMint); } function calculateDubiToMintMax(uint96 amount) internal pure returns (uint96) { return calculateDubiToMintBySeconds( amount, MAX_FINITE_LOCK_DURATION_SECONDS ); } function calculateMintDuration(uint32 _now, uint32 lastWithdrawal) internal pure returns (uint32) { require(lastWithdrawal > 0 && lastWithdrawal <= _now, "MINT-1"); // NOTE: we don't use any safe math here for efficiency reasons. The assert above // is already a pretty good guarantee that nothing goes wrong. Also, all numbers involved // are very well smaller than uint256 in the first place. uint256 _elapsedTotal = _now - lastWithdrawal; uint256 _proRatedYears = _elapsedTotal / SECONDS_PER_MONTH / 12; uint256 _elapsedInYear = _elapsedTotal % MAX_FINITE_LOCK_DURATION_SECONDS; // // Examples (using months instead of seconds): // calculation formula: (monthsSinceWithdrawal % 12) + (_proRatedYears * 12) // 1) Burn after 11 months since last withdrawal (number of years = 11 / 12 + 1 = 1) // => (11 % 12) + (years * 12) => 23 months worth of DUBI // => 23 months // 1) Burn after 4 months since last withdrawal (number of years = 4 / 12 + 1 = 1) // => (4 % 12) + (years * 12) => 16 months worth of DUBI // => 16 months // 2) Burn 0 months after withdrawal after 4 months (number of years = 0 / 12 + 1 = 1): // => (0 % 12) + (years * 12) => 12 months worth of DUBI (+ 4 months worth of withdrawn DUBI) // => 16 months // 3) Burn after 36 months since last withdrawal (number of years = 36 / 12 + 1 = 4) // => (36 % 12) + (years * 12) => 48 months worth of DUBI // => 48 months // 4) Burn 1 month after withdrawal after 35 months (number of years = 1 / 12 + 1 = 1): // => (1 % 12) + (years * 12) => 12 month worth of DUBI (+ 35 months worth of withdrawn DUBI) // => 47 months uint32 _mintDuration = uint32( _elapsedInYear + _proRatedYears * MAX_FINITE_LOCK_DURATION_SECONDS ); return _mintDuration; } function percentage( uint256 numerator, uint256 denominator, uint256 precision ) internal pure returns (uint256) { return ((numerator * (uint256(10)**(precision + 1))) / denominator + 5) / uint256(10); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.0; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } /** * @dev Returns the integer division of two unsigned integers. Reverts with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, "SafeMath: modulo by zero"); } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts with custom message when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.0; import "../GSN/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor () internal { address msgSender = _msgSender(); _owner = msgSender; emit OwnershipTransferred(address(0), msgSender); } /** * @dev Returns the address of the current owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(_owner == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.0; /** * @dev Interface of the global ERC1820 Registry, as defined in the * https://eips.ethereum.org/EIPS/eip-1820[EIP]. Accounts may register * implementers for interfaces in this registry, as well as query support. * * Implementers may be shared by multiple accounts, and can also implement more * than a single interface for each account. Contracts can implement interfaces * for themselves, but externally-owned accounts (EOA) must delegate this to a * contract. * * {IERC165} interfaces can also be queried via the registry. * * For an in-depth explanation and source code analysis, see the EIP text. */ interface IERC1820Registry { /** * @dev Sets `newManager` as the manager for `account`. A manager of an * account is able to set interface implementers for it. * * By default, each account is its own manager. Passing a value of `0x0` in * `newManager` will reset the manager to this initial state. * * Emits a {ManagerChanged} event. * * Requirements: * * - the caller must be the current manager for `account`. */ function setManager(address account, address newManager) external; /** * @dev Returns the manager for `account`. * * See {setManager}. */ function getManager(address account) external view returns (address); /** * @dev Sets the `implementer` contract as ``account``'s implementer for * `interfaceHash`. * * `account` being the zero address is an alias for the caller's address. * The zero address can also be used in `implementer` to remove an old one. * * See {interfaceHash} to learn how these are created. * * Emits an {InterfaceImplementerSet} event. * * Requirements: * * - the caller must be the current manager for `account`. * - `interfaceHash` must not be an {IERC165} interface id (i.e. it must not * end in 28 zeroes). * - `implementer` must implement {IERC1820Implementer} and return true when * queried for support, unless `implementer` is the caller. See * {IERC1820Implementer-canImplementInterfaceForAddress}. */ function setInterfaceImplementer(address account, bytes32 interfaceHash, address implementer) external; /** * @dev Returns the implementer of `interfaceHash` for `account`. If no such * implementer is registered, returns the zero address. * * If `interfaceHash` is an {IERC165} interface id (i.e. it ends with 28 * zeroes), `account` will be queried for support of it. * * `account` being the zero address is an alias for the caller's address. */ function getInterfaceImplementer(address account, bytes32 interfaceHash) external view returns (address); /** * @dev Returns the interface hash for an `interfaceName`, as defined in the * corresponding * https://eips.ethereum.org/EIPS/eip-1820#interface-name[section of the EIP]. */ function interfaceHash(string calldata interfaceName) external pure returns (bytes32); /** * @notice Updates the cache with whether the contract implements an ERC165 interface or not. * @param account Address of the contract for which to update the cache. * @param interfaceId ERC165 interface for which to update the cache. */ function updateERC165Cache(address account, bytes4 interfaceId) external; /** * @notice Checks whether a contract implements an ERC165 interface or not. * If the result is not cached a direct lookup on the contract address is performed. * If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling * {updateERC165Cache} with the contract address. * @param account Address of the contract to check. * @param interfaceId ERC165 interface to check. * @return True if `account` implements `interfaceId`, false otherwise. */ function implementsERC165Interface(address account, bytes4 interfaceId) external view returns (bool); /** * @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache. * @param account Address of the contract to check. * @param interfaceId ERC165 interface to check. * @return True if `account` implements `interfaceId`, false otherwise. */ function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool); event InterfaceImplementerSet(address indexed account, bytes32 indexed interfaceHash, address indexed implementer); event ManagerChanged(address indexed account, address indexed newManager); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; pragma experimental ABIEncoderV2; // Token agnostic fuel struct that is passed around when the fuel is burned by a different (token) contract. // The contract has to explicitely support the desired token that should be burned. struct TokenFuel { // A token alias that must be understood by the target contract uint8 tokenAlias; uint96 amount; } /** * @dev Extends the interface of the ERC20 standard as defined in the EIP with * `boostedTransferFrom` to perform transfers without having to rely on an allowance. */ interface IBoostableERC20 { // ERC20 function totalSupply() external view returns (uint256); function balanceOf(address account) external view returns (uint256); function transfer(address recipient, uint256 amount) external returns (bool); function allowance(address owner, address spender) external view returns (uint256); function approve(address spender, uint256 amount) external returns (bool); function transferFrom( address sender, address recipient, uint256 amount ) external returns (bool); event Transfer(address indexed from, address indexed to, uint256 value); event Approval( address indexed owner, address indexed spender, uint256 value ); // Extension /** * @dev Moves `amount` tokens from `sender` to `recipient`. * * If the caller is known by the callee, then the implementation should skip approval checks. * Also accepts a data payload, similar to ERC721's `safeTransferFrom` to pass arbitrary data. * */ function boostedTransferFrom( address sender, address recipient, uint256 amount, bytes calldata data ) external returns (bool); /** * @dev Burns `fuel` from `from`. */ function burnFuel(address from, TokenFuel memory fuel) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "./Boostable.sol"; import "./BoostableLib.sol"; /** * @dev EIP712 boostable primitives related to ERC20 for the Purpose domain */ abstract contract BoostableERC20 is Boostable { /** * @dev A struct representing the payload of the ERC20 `boostedSend` function. */ struct BoostedSend { uint8 tag; address sender; address recipient; uint256 amount; bytes data; BoosterFuel fuel; BoosterPayload boosterPayload; } /** * @dev A struct representing the payload of the ERC20 `boostedBurn` function. */ struct BoostedBurn { uint8 tag; address account; uint256 amount; bytes data; BoosterFuel fuel; BoosterPayload boosterPayload; } uint8 internal constant BOOST_TAG_SEND = 0; uint8 internal constant BOOST_TAG_BURN = 1; bytes32 internal constant BOOSTED_SEND_TYPEHASH = keccak256( "BoostedSend(uint8 tag,address sender,address recipient,uint256 amount,bytes data,BoosterFuel fuel,BoosterPayload boosterPayload)BoosterFuel(uint96 dubi,uint96 unlockedPrps,uint96 lockedPrps,uint96 intrinsicFuel)BoosterPayload(address booster,uint64 timestamp,uint64 nonce,bool isLegacySignature)" ); bytes32 internal constant BOOSTED_BURN_TYPEHASH = keccak256( "BoostedBurn(uint8 tag,address account,uint256 amount,bytes data,BoosterFuel fuel,BoosterPayload boosterPayload)BoosterFuel(uint96 dubi,uint96 unlockedPrps,uint96 lockedPrps,uint96 intrinsicFuel)BoosterPayload(address booster,uint64 timestamp,uint64 nonce,bool isLegacySignature)" ); constructor(address optIn) public Boostable(optIn) {} /** * @dev Returns the hash of `boostedSend`. */ function hashBoostedSend(BoostedSend memory send, address booster) internal view returns (bytes32) { return BoostableLib.hashWithDomainSeparator( _DOMAIN_SEPARATOR, keccak256( abi.encode( BOOSTED_SEND_TYPEHASH, BOOST_TAG_SEND, send.sender, send.recipient, send.amount, keccak256(send.data), BoostableLib.hashBoosterFuel(send.fuel), BoostableLib.hashBoosterPayload( send.boosterPayload, booster ) ) ) ); } /** * @dev Returns the hash of `boostedBurn`. */ function hashBoostedBurn(BoostedBurn memory burn, address booster) internal view returns (bytes32) { return BoostableLib.hashWithDomainSeparator( _DOMAIN_SEPARATOR, keccak256( abi.encode( BOOSTED_BURN_TYPEHASH, BOOST_TAG_BURN, burn.account, burn.amount, keccak256(burn.data), BoostableLib.hashBoosterFuel(burn.fuel), BoostableLib.hashBoosterPayload( burn.boosterPayload, booster ) ) ) ); } /** * @dev Tries to interpret the given boosterMessage and * return it's hash plus creation timestamp. */ function decodeAndHashBoosterMessage( address targetBooster, bytes memory boosterMessage ) external override view returns (bytes32, uint64) { require(boosterMessage.length > 0, "PB-7"); uint8 tag = _readBoosterTag(boosterMessage); if (tag == BOOST_TAG_SEND) { BoostedSend memory send = abi.decode(boosterMessage, (BoostedSend)); return ( hashBoostedSend(send, targetBooster), send.boosterPayload.timestamp ); } if (tag == BOOST_TAG_BURN) { BoostedBurn memory burn = abi.decode(boosterMessage, (BoostedBurn)); return ( hashBoostedBurn(burn, targetBooster), burn.boosterPayload.timestamp ); } // Unknown tag, so just return an empty result return ("", 0); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.0; /* * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with GSN meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "./ProtectedBoostable.sol"; /** * @dev Purpose Boostable primitives using the EIP712 standard */ abstract contract Boostable is ProtectedBoostable { // "Purpose", "Dubi" and "Hodl" are all under the "Purpose" umbrella constructor(address optIn) public ProtectedBoostable( optIn, keccak256( abi.encode( EIP712_DOMAIN_TYPEHASH, keccak256("Purpose"), keccak256("1"), _getChainId(), address(this) ) ) ) {} // Fuel alias constants - used when fuel is burned from external contract calls uint8 internal constant TOKEN_FUEL_ALIAS_UNLOCKED_PRPS = 0; uint8 internal constant TOKEN_FUEL_ALIAS_LOCKED_PRPS = 1; uint8 internal constant TOKEN_FUEL_ALIAS_DUBI = 2; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; pragma experimental ABIEncoderV2; struct BoosterFuel { uint96 dubi; uint96 unlockedPrps; uint96 lockedPrps; uint96 intrinsicFuel; } struct BoosterPayload { address booster; uint64 timestamp; uint64 nonce; // Fallback for 'personal_sign' when e.g. using hardware wallets that don't support // EIP712 signing (yet). bool isLegacySignature; } // Library for Boostable hash functions that are completely inlined. library BoostableLib { bytes32 private constant BOOSTER_PAYLOAD_TYPEHASH = keccak256( "BoosterPayload(address booster,uint64 timestamp,uint64 nonce,bool isLegacySignature)" ); bytes32 internal constant BOOSTER_FUEL_TYPEHASH = keccak256( "BoosterFuel(uint96 dubi,uint96 unlockedPrps,uint96 lockedPrps,uint96 intrinsicFuel)" ); /** * @dev Returns the hash of the packed DOMAIN_SEPARATOR and `messageHash` and is used for verifying * a signature. */ function hashWithDomainSeparator( bytes32 domainSeparator, bytes32 messageHash ) internal pure returns (bytes32) { return keccak256( abi.encodePacked("\x19\x01", domainSeparator, messageHash) ); } /** * @dev Returns the hash of `payload` using the provided booster (i.e. `msg.sender`). */ function hashBoosterPayload(BoosterPayload memory payload, address booster) internal pure returns (bytes32) { return keccak256( abi.encode( BOOSTER_PAYLOAD_TYPEHASH, booster, payload.timestamp, payload.nonce, payload.isLegacySignature ) ); } function hashBoosterFuel(BoosterFuel memory fuel) internal pure returns (bytes32) { return keccak256( abi.encode( BOOSTER_FUEL_TYPEHASH, fuel.dubi, fuel.unlockedPrps, fuel.lockedPrps, fuel.intrinsicFuel ) ); } /** * @dev Returns the tag found in the given `boosterMessage`. */ function _readBoosterTag(bytes memory boosterMessage) internal pure returns (uint8) { // The tag is either the 32th byte or the 64th byte depending on whether // the booster message contains dynamic bytes or not. // // If it contains a dynamic byte array, then the first word points to the first // data location. // // Therefore, we read the 32th byte and check if it's >= 32 and if so, // simply read the (32 + first word)th byte to get the tag. // // This imposes a limit on the number of tags we can support (<32), but // given that it is very unlikely for so many tags to exist it is fine. // // Read the 32th byte to get the tag, because it is a uint8 padded to 32 bytes. // i.e. // -----------------------------------------------------------------v // 0x0000000000000000000000000000000000000000000000000000000000000001 // ... // uint8 tag = uint8(boosterMessage[31]); if (tag >= 32) { // Read the (32 + tag) byte. E.g. if tag is 32, then we read the 64th: // -------------------------------------------------------------------- // 0x0000000000000000000000000000000000000000000000000000000000000020 | // 0000000000000000000000000000000000000000000000000000000000000001 < // ... // tag = uint8(boosterMessage[31 + tag]); } return tag; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "./EIP712Boostable.sol"; import "./IOptIn.sol"; import "./ProtectedBoostableLib.sol"; abstract contract ProtectedBoostable is EIP712Boostable { //--------------------------------------------------------------- // State for non-boosted operations while opted-in and the OPT_IN permaboost is active //--------------------------------------------------------------- uint256 private constant MAX_PENDING_OPS = 25; // A mapping of account to an opCounter. mapping(address => OpCounter) internal _opCounters; // A mapping of account to an array containing all it's pending ops. mapping(address => OpHandle[]) internal _pendingOpsByAddress; // A mapping of keccak256(address,opId) to a struct holding metadata like the associated user account and creation timestamp. mapping(bytes32 => OpMetadata) internal _opMetadata; // Event that is emitted whenever a pending op is created // NOTE: returning an OpHandle in the event flattens it into an array for some reason // i.e. emit PendingOp(0x123.., OpHandle(1, 0)) => { from: 0x123, opHandle: ['1', '0']} event PendingOp(address from, uint64 opId, uint8 opType); // Event that is emitted whenever a pending op is finalized event FinalizedOp(address from, uint64 opId, uint8 opType); // Event that is emitted whenever a pending op is reverted event RevertedOp(address from, uint64 opId, uint8 opType); constructor(address optIn, bytes32 domainSeparator) public EIP712Boostable(optIn, domainSeparator) {} //--------------------------------------------------------------- // Pending ops //--------------------------------------------------------------- /** * @dev Returns the metadata of an op. Returns a zero struct if it doesn't exist. */ function getOpMetadata(address user, uint64 opId) public virtual view returns (OpMetadata memory) { return _opMetadata[_getOpKey(user, opId)]; } /** * @dev Returns the metadata of an op. Returns a zero struct if it doesn't exist. */ function getOpCounter(address user) public virtual view returns (OpCounter memory) { return _opCounters[user]; } /** * @dev Returns the metadata of an op. Reverts if it doesn't exist or * the opType mismatches. */ function safeGetOpMetadata(address user, OpHandle memory opHandle) public virtual view returns (OpMetadata memory) { OpMetadata storage metadata = _opMetadata[_getOpKey( user, opHandle.opId )]; // If 'createdAt' is zero, then it's non-existent for us require(metadata.createdAt > 0, "PB-1"); require(metadata.opType == opHandle.opType, "PB-2"); return metadata; } /** * @dev Get the next op id for `user` */ function _getNextOpId(address user) internal returns (uint64) { OpCounter storage counter = _opCounters[user]; // NOTE: we always increase by 1, so it cannot overflow as long as this // is the only place increasing the counter. uint64 nextOpId = counter.value + 1; // This also updates the nextFinalize/Revert values if (counter.nextFinalize == 0) { // Only gets updated if currently pointing to "nothing", because FIFO counter.nextFinalize = nextOpId; } // nextRevert is always updated to the new opId, because LIFO counter.nextRevert = nextOpId; counter.value = nextOpId; // NOTE: It is safe to downcast to uint64 since it's practically impossible to overflow. return nextOpId; } /** * @dev Creates a new opHandle with the given type for `user`. */ function _createNewOpHandle( IOptIn.OptInStatus memory optInStatus, address user, uint8 opType ) internal virtual returns (OpHandle memory) { uint64 nextOpId = _getNextOpId(user); OpHandle memory opHandle = OpHandle({opId: nextOpId, opType: opType}); // NOTE: we have a hard limit of 25 pending OPs and revert if that // limit is exceeded. require(_pendingOpsByAddress[user].length < MAX_PENDING_OPS, "PB-3"); address booster = optInStatus.optedInTo; _pendingOpsByAddress[user].push(opHandle); _opMetadata[_getOpKey(user, nextOpId)] = OpMetadata({ createdAt: uint64(block.timestamp), booster: booster, opType: opType }); return opHandle; } /** * @dev Delete the given `opHandle` from `user`. */ function _deleteOpHandle(address user, OpHandle memory opHandle) internal virtual { OpHandle[] storage _opHandles = _pendingOpsByAddress[user]; OpCounter storage opCounter = _opCounters[user]; ProtectedBoostableLib.deleteOpHandle( user, opHandle, _opHandles, opCounter, _opMetadata ); } /** * @dev Assert that the caller is allowed to finalize a pending op. * * Returns the user and createdAt timestamp of the op on success in order to * save some gas by minimizing redundant look-ups. */ function _assertCanFinalize(address user, OpHandle memory opHandle) internal returns (uint64) { OpMetadata memory metadata = safeGetOpMetadata(user, opHandle); uint64 createdAt = metadata.createdAt; // First check if the user is still opted-in. If not, then anyone // can finalize since it is no longer associated with the original booster. IOptIn.OptInStatus memory optInStatus = getOptInStatus(user); if (!optInStatus.isOptedIn) { return createdAt; } // Revert if not FIFO order _assertFinalizeFIFO(user, opHandle.opId); return ProtectedBoostableLib.assertCanFinalize(metadata, optInStatus); } /** * @dev Asserts that the caller (msg.sender) is allowed to revert a pending operation. * The caller must be opted-in by user and provide a valid signature from the user * that hasn't expired yet. */ function _assertCanRevert( address user, OpHandle memory opHandle, uint64 opTimestamp, bytes memory boosterMessage, Signature memory signature ) internal { // Revert if not LIFO order _assertRevertLIFO(user, opHandle.opId); IOptIn.OptInStatus memory optInStatus = getOptInStatus(user); require( optInStatus.isOptedIn && msg.sender == optInStatus.optedInTo, "PB-6" ); // In order to verify the boosterMessage, we need the hash and timestamp of when it // was signed. To interpret the boosterMessage, consult all available hasher contracts and // take the first non-zero result. address[] memory hasherContracts = _getHasherContracts(); // Call external library function, which performs the actual assertion. The reason // why it is not inlined, is that the need to reduce bytecode size. ProtectedBoostableLib.verifySignatureForRevert( user, opTimestamp, optInStatus, boosterMessage, hasherContracts, signature ); } function _getHasherContracts() internal virtual returns (address[] memory); /** * @dev Asserts that the given opId is the next to be finalized for `user`. */ function _assertFinalizeFIFO(address user, uint64 opId) internal virtual { OpCounter storage counter = _opCounters[user]; require(counter.nextFinalize == opId, "PB-9"); } /** * @dev Asserts that the given opId is the next to be reverted for `user`. */ function _assertRevertLIFO(address user, uint64 opId) internal virtual { OpCounter storage counter = _opCounters[user]; require(counter.nextRevert == opId, "PB-10"); } /** * @dev Prepare an op revert. * - Asserts that the caller is allowed to revert the given op * - Deletes the op handle to minimize risks of reentrancy */ function _prepareOpRevert( address user, OpHandle memory opHandle, bytes memory boosterMessage, Signature memory signature ) internal { OpMetadata memory metadata = safeGetOpMetadata(user, opHandle); _assertCanRevert( user, opHandle, metadata.createdAt, boosterMessage, signature ); // Delete opHandle, which prevents reentrancy since `safeGetOpMetadata` // will fail afterwards. _deleteOpHandle(user, opHandle); } /** * @dev Returns the hash of (user, opId) which is used as a look-up * key in the `_opMetadata` mapping. */ function _getOpKey(address user, uint64 opId) internal pure returns (bytes32) { return keccak256(abi.encodePacked(user, opId)); } /** * @dev Deriving contracts can override this function to accept a boosterMessage for a given booster and * interpret it into a hash and timestamp. */ function decodeAndHashBoosterMessage( address targetBooster, bytes memory boosterMessage ) external virtual view returns (bytes32, uint64) {} /** * @dev Returns the tag found in the given `boosterMesasge`. */ function _readBoosterTag(bytes memory boosterMessage) internal pure returns (uint8) { // The tag is either the 32th byte or the 64th byte depending on whether // the booster message contains dynamic bytes or not. // // If it contains a dynamic byte array, then the first word points to the first // data location. // // Therefore, we read the 32th byte and check if it's >= 32 and if so, // simply read the (32 + first word)th byte to get the tag. // // This imposes a limit on the number of tags we can support (<32), but // given that it is very unlikely for so many tags to exist it is fine. // // Read the 32th byte to get the tag, because it is a uint8 padded to 32 bytes. // i.e. // -----------------------------------------------------------------v // 0x0000000000000000000000000000000000000000000000000000000000000001 // ... // uint8 tag = uint8(boosterMessage[31]); if (tag >= 32) { // Read the (32 + tag) byte. E.g. if tag is 32, then we read the 64th: // -------------------------------------------------------------------- // 0x0000000000000000000000000000000000000000000000000000000000000020 | // 0000000000000000000000000000000000000000000000000000000000000001 < // ... // tag = uint8(boosterMessage[31 + tag]); } return tag; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/cryptography/ECDSA.sol"; import "./IOptIn.sol"; import "./BoostableLib.sol"; import "./IBoostableERC20.sol"; /** * @dev Boostable base contract * * All deriving contracts are expected to implement EIP712 for the message signing. * */ abstract contract EIP712Boostable { using ECDSA for bytes32; // solhint-disable-next-line var-name-mixedcase IOptIn internal immutable _OPT_IN; // solhint-disable-next-line var-name-mixedcase bytes32 internal immutable _DOMAIN_SEPARATOR; bytes32 internal constant EIP712_DOMAIN_TYPEHASH = keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" ); bytes32 private constant BOOSTER_PAYLOAD_TYPEHASH = keccak256( "BoosterPayload(address booster,uint64 timestamp,uint64 nonce,bool isLegacySignature)" ); bytes32 internal constant BOOSTER_FUEL_TYPEHASH = keccak256( "BoosterFuel(uint96 dubi,uint96 unlockedPrps,uint96 lockedPrps,uint96 intrinsicFuel)" ); // The boost fuel is capped to 10 of the respective token that will be used for payment. uint96 internal constant MAX_BOOSTER_FUEL = 10 ether; // A magic booster permission prefix bytes6 private constant MAGIC_BOOSTER_PERMISSION_PREFIX = "BOOST-"; constructor(address optIn, bytes32 domainSeparator) public { _OPT_IN = IOptIn(optIn); _DOMAIN_SEPARATOR = domainSeparator; } // A mapping of mappings to keep track of used nonces by address to // protect against replays. Each 'Boostable' contract maintains it's own // state for nonces. mapping(address => uint64) private _nonces; //--------------------------------------------------------------- function getNonce(address account) external virtual view returns (uint64) { return _nonces[account]; } function getOptInStatus(address account) internal view returns (IOptIn.OptInStatus memory) { return _OPT_IN.getOptInStatus(account); } /** * @dev Called by every 'boosted'-function to ensure that `msg.sender` (i.e. a booster) is * allowed to perform the call for `from` (the origin) by verifying that `messageHash` * has been signed by `from`. Additionally, `from` provides a nonce to prevent * replays. Boosts cannot be verified out of order. * * @param from the address that the boost is made for * @param messageHash the reconstructed message hash based on the function input * @param payload the booster payload * @param signature the signature of `from` */ function verifyBoost( address from, bytes32 messageHash, BoosterPayload memory payload, Signature memory signature ) internal { uint64 currentNonce = _nonces[from]; require(currentNonce == payload.nonce - 1, "AB-1"); _nonces[from] = currentNonce + 1; _verifyBoostWithoutNonce(from, messageHash, payload, signature); } /** * @dev Verify a boost without verifying the nonce. */ function _verifyBoostWithoutNonce( address from, bytes32 messageHash, BoosterPayload memory payload, Signature memory signature ) internal view { // The sender must be the booster specified in the payload require(msg.sender == payload.booster, "AB-2"); (bool isOptedInToSender, uint256 optOutPeriod) = _OPT_IN.isOptedInBy( msg.sender, from ); // `from` must be opted-in to booster require(isOptedInToSender, "AB-3"); // The given timestamp must not be greater than `block.timestamp + 1 hour` // and at most `optOutPeriod(booster)` seconds old. uint64 _now = uint64(block.timestamp); uint64 _optOutPeriod = uint64(optOutPeriod); bool notTooFarInFuture = payload.timestamp <= _now + 1 hours; bool belowMaxAge = true; // Calculate the absolute difference. Because of the small tolerance, `payload.timestamp` // may be greater than `_now`: if (payload.timestamp <= _now) { belowMaxAge = _now - payload.timestamp <= _optOutPeriod; } // Signature must not be expired require(notTooFarInFuture && belowMaxAge, "AB-4"); // NOTE: Currently, hardware wallets (e.g. Ledger, Trezor) do not support EIP712 signing (specifically `signTypedData_v4`). // However, a user can still sign the EIP712 hash with the caveat that it's signed using `personal_sign` which prepends // the prefix '"\x19Ethereum Signed Message:\n" + len(message)'. // // To still support that, we add the prefix to the hash if `isLegacySignature` is true. if (payload.isLegacySignature) { messageHash = messageHash.toEthSignedMessageHash(); } // Valid, if the recovered address from `messageHash` with the given `signature` matches `from`. address signer = ecrecover( messageHash, signature.v, signature.r, signature.s ); if (!payload.isLegacySignature && signer != from) { // As a last resort we try anyway, in case the caller simply forgot the `isLegacySignature` flag. signer = ecrecover( messageHash.toEthSignedMessageHash(), signature.v, signature.r, signature.s ); } require(from == signer, "AB-5"); } /** * @dev Returns the hash of `payload` using the provided booster (i.e. `msg.sender`). */ function hashBoosterPayload(BoosterPayload memory payload, address booster) internal pure returns (bytes32) { return keccak256( abi.encode( BOOSTER_PAYLOAD_TYPEHASH, booster, payload.timestamp, payload.nonce ) ); } function _getChainId() internal pure returns (uint256) { uint256 chainId; assembly { chainId := chainid() } return chainId; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; pragma experimental ABIEncoderV2; struct Signature { bytes32 r; bytes32 s; uint8 v; } interface IOptIn { struct OptInStatus { bool isOptedIn; bool permaBoostActive; address optedInTo; uint32 optOutPeriod; } function getOptInStatusPair(address accountA, address accountB) external view returns (OptInStatus memory, OptInStatus memory); function getOptInStatus(address account) external view returns (OptInStatus memory); function isOptedInBy(address _sender, address _account) external view returns (bool, uint256); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/cryptography/ECDSA.sol"; import "./IOptIn.sol"; struct OpHandle { uint8 opType; uint64 opId; } struct OpMetadata { uint8 opType; // the operation type uint64 createdAt; // the creation timestamp of an op address booster; // the booster at the time of when the op has been created } struct OpCounter { // The current value of the counter uint64 value; // Contains the opId that is to be finalized next - i.e. FIFO order uint64 nextFinalize; // Contains the opId that is to be reverted next - i.e. LIFO order uint64 nextRevert; } // Library containing public functions for pending ops - those will never be inlined // to reduce the bytecode size of individual contracts. library ProtectedBoostableLib { using ECDSA for bytes32; function deleteOpHandle( address user, OpHandle memory opHandle, OpHandle[] storage opHandles, OpCounter storage opCounter, mapping(bytes32 => OpMetadata) storage opMetadata ) public { uint256 length = opHandles.length; assert(length > 0); uint64 minOpId; // becomes next LIFO uint64 maxOpId; // becomes next FIFO // Pending ops are capped to MAX_PENDING_OPS. We always perform // MIN(length, MAX_PENDING_OPS) look-ups to do a "swap-and-pop" and // for updating the opCounter LIFO/FIFO pointers. for (uint256 i = 0; i < length; i++) { uint64 currOpId = opHandles[i].opId; if (currOpId == opHandle.opId) { // Overwrite item at i with last opHandles[i] = opHandles[length - 1]; // Continue, to ignore this opId when updating // minOpId and maxOpId. continue; } // Update minOpId if (minOpId == 0 || currOpId < minOpId) { minOpId = currOpId; } // Update maxOpId if (currOpId > maxOpId) { maxOpId = currOpId; } } // Might be 0 when everything got finalized/reverted opCounter.nextFinalize = minOpId; // Might be 0 when everything got finalized/reverted opCounter.nextRevert = maxOpId; // Remove the last item opHandles.pop(); // Remove metadata delete opMetadata[_getOpKey(user, opHandle.opId)]; } function assertCanFinalize( OpMetadata memory metadata, IOptIn.OptInStatus memory optInStatus ) public view returns (uint64) { // Now there are three valid scenarios remaining: // // - msg.sender is the original booster // - op is expired // - getBoosterAddress returns a different booster than the original booster // // In the second and third case, anyone can call finalize. address originalBooster = metadata.booster; if (originalBooster == msg.sender) { return metadata.createdAt; // First case } address currentBooster = optInStatus.optedInTo; uint256 optOutPeriod = optInStatus.optOutPeriod; bool isExpired = block.timestamp >= metadata.createdAt + optOutPeriod; if (isExpired) { return metadata.createdAt; // Second case } if (currentBooster != originalBooster) { return metadata.createdAt; // Third case } revert("PB-4"); } function verifySignatureForRevert( address user, uint64 opTimestamp, IOptIn.OptInStatus memory optInStatus, bytes memory boosterMessage, address[] memory hasherContracts, Signature memory signature ) public { require(hasherContracts.length > 0, "PB-12"); // Result of hasher contract call uint64 signedAt; bytes32 boosterHash; bool signatureVerified; for (uint256 i = 0; i < hasherContracts.length; i++) { // Call into the hasher contract and take the first non-zero result. // The contract must implement the following function: // // decodeAndHashBoosterMessage( // address targetBooster, // bytes memory boosterMessage // ) // // If it doesn't, then the call will fail (success=false) and we try the next one. // If it succeeds (success = true), then we try to decode the result. // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory result) = address(hasherContracts[i]) .call( // keccak256("decodeAndHashBoosterMessage(address,bytes)") abi.encodeWithSelector( 0xaf6eec54, msg.sender, /* msg.sender becomes the target booster */ boosterMessage ) ); if (!success) { continue; } // The result is exactly 2 words long = 512 bits = 64 bytes // 32 bytes for the expected message hash // 8 bytes (padded to 32 bytes) for the expected timestamp if (result.length != 64) { continue; } // NOTE: A contract with malintent could return any hash that we would // try to recover against. But there is no harm done in doing so since // the user must have signed it. // // However, it might return an unrelated timestamp, that the user hasn't // signed - so it could prolong the expiry of a signature which is a valid // concern whose risk we minimize by using also the op timestamp which guarantees // that a signature eventually expires. // Decode and recover signer (boosterHash, signedAt) = abi.decode(result, (bytes32, uint64)); address signer = ecrecover( boosterHash, signature.v, signature.r, signature.s ); if (user != signer) { // NOTE: Currently, hardware wallets (e.g. Ledger, Trezor) do not support EIP712 signing (specifically `signTypedData_v4`). // However, a user can still sign the EIP712 hash with the caveat that it's signed using `personal_sign` which prepends // the prefix '"\x19Ethereum Signed Message:\n" + len(message)'. // // To still support that, we also add the prefix and try to use the recovered address instead: signer = ecrecover( boosterHash.toEthSignedMessageHash(), signature.v, signature.r, signature.s ); } // If we recovered `user` from the signature, then we have a valid signature. if (user == signer) { signatureVerified = true; break; } // Keep trying } // Revert if signature couldn't be verified with any of the returned hashes require(signatureVerified, "PB-8"); // Lastly, the current time must not be older than: // MIN(opTimestamp, signedAt) + optOutPeriod * 3 uint64 _now = uint64(block.timestamp); // The maximum age is equal to whichever is lowest: // opTimestamp + optOutPeriod * 3 // signedAt + optOutPeriod * 3 uint64 maximumAge; if (opTimestamp > signedAt) { maximumAge = signedAt + uint64(optInStatus.optOutPeriod * 3); } else { maximumAge = opTimestamp + uint64(optInStatus.optOutPeriod * 3); } require(_now <= maximumAge, "PB-11"); } function _getOpKey(address user, uint64 opId) internal pure returns (bytes32) { return keccak256(abi.encodePacked(user, opId)); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.0; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { // Check the signature length if (signature.length != 65) { revert("ECDSA: invalid signature length"); } // Divide the signature in r, s and v variables bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. // solhint-disable-next-line no-inline-assembly assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { revert("ECDSA: invalid signature 's' value"); } if (v != 27 && v != 28) { revert("ECDSA: invalid signature 'v' value"); } // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); require(signer != address(0), "ECDSA: invalid signature"); return signer; } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * replicates the behavior of the * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`] * JSON-RPC method. * * See {recover}. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { // 32 is the length in bytes of hash, // enforced by the type signature above return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); } }
{ "optimizer": { "enabled": true, "runs": 200 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "abi" ] } }, "libraries": { "contracts/ProtectedBoostableLib.sol": { "ProtectedBoostableLib": "0x85ae45a05971170b70744292e2f051c0c49cf909" } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"uint256","name":"initialSupply","type":"uint256"},{"internalType":"address","name":"optIn","type":"address"},{"internalType":"address","name":"dubi","type":"address"},{"internalType":"address","name":"hodl","type":"address"},{"internalType":"address","name":"externalAddress1","type":"address"},{"internalType":"address","name":"externalAddress2","type":"address"},{"internalType":"address","name":"externalAddress3","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amountAndFuel","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"Burned","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint64","name":"opId","type":"uint64"},{"indexed":false,"internalType":"uint8","name":"opType","type":"uint8"}],"name":"FinalizedOp","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint64","name":"opId","type":"uint64"},{"indexed":false,"internalType":"uint8","name":"opType","type":"uint8"}],"name":"PendingOp","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint64","name":"opId","type":"uint64"},{"indexed":false,"internalType":"uint8","name":"opType","type":"uint8"}],"name":"RevertedOp","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"holder","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint64","name":"opId","type":"uint64"}],"name":"assertFinalizeFIFOShared","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint64","name":"opId","type":"uint64"}],"name":"assertRevertLIFOShared","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenHolder","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint8","name":"tag","type":"uint8"},{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"components":[{"internalType":"uint96","name":"dubi","type":"uint96"},{"internalType":"uint96","name":"unlockedPrps","type":"uint96"},{"internalType":"uint96","name":"lockedPrps","type":"uint96"},{"internalType":"uint96","name":"intrinsicFuel","type":"uint96"}],"internalType":"struct BoosterFuel","name":"fuel","type":"tuple"},{"components":[{"internalType":"address","name":"booster","type":"address"},{"internalType":"uint64","name":"timestamp","type":"uint64"},{"internalType":"uint64","name":"nonce","type":"uint64"},{"internalType":"bool","name":"isLegacySignature","type":"bool"}],"internalType":"struct BoosterPayload","name":"boosterPayload","type":"tuple"}],"internalType":"struct BoostableERC20.BoostedBurn","name":"message","type":"tuple"},{"components":[{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"}],"internalType":"struct Signature","name":"signature","type":"tuple"}],"name":"boostedBurn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint8","name":"tag","type":"uint8"},{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"components":[{"internalType":"uint96","name":"dubi","type":"uint96"},{"internalType":"uint96","name":"unlockedPrps","type":"uint96"},{"internalType":"uint96","name":"lockedPrps","type":"uint96"},{"internalType":"uint96","name":"intrinsicFuel","type":"uint96"}],"internalType":"struct BoosterFuel","name":"fuel","type":"tuple"},{"components":[{"internalType":"address","name":"booster","type":"address"},{"internalType":"uint64","name":"timestamp","type":"uint64"},{"internalType":"uint64","name":"nonce","type":"uint64"},{"internalType":"bool","name":"isLegacySignature","type":"bool"}],"internalType":"struct BoosterPayload","name":"boosterPayload","type":"tuple"}],"internalType":"struct BoostableERC20.BoostedBurn[]","name":"burns","type":"tuple[]"},{"components":[{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"}],"internalType":"struct Signature[]","name":"signatures","type":"tuple[]"}],"name":"boostedBurnBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint8","name":"tag","type":"uint8"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"components":[{"internalType":"uint96","name":"dubi","type":"uint96"},{"internalType":"uint96","name":"unlockedPrps","type":"uint96"},{"internalType":"uint96","name":"lockedPrps","type":"uint96"},{"internalType":"uint96","name":"intrinsicFuel","type":"uint96"}],"internalType":"struct BoosterFuel","name":"fuel","type":"tuple"},{"components":[{"internalType":"address","name":"booster","type":"address"},{"internalType":"uint64","name":"timestamp","type":"uint64"},{"internalType":"uint64","name":"nonce","type":"uint64"},{"internalType":"bool","name":"isLegacySignature","type":"bool"}],"internalType":"struct BoosterPayload","name":"boosterPayload","type":"tuple"}],"internalType":"struct BoostableERC20.BoostedSend","name":"send","type":"tuple"},{"components":[{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"}],"internalType":"struct Signature","name":"signature","type":"tuple"}],"name":"boostedSend","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint8","name":"tag","type":"uint8"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"components":[{"internalType":"uint96","name":"dubi","type":"uint96"},{"internalType":"uint96","name":"unlockedPrps","type":"uint96"},{"internalType":"uint96","name":"lockedPrps","type":"uint96"},{"internalType":"uint96","name":"intrinsicFuel","type":"uint96"}],"internalType":"struct BoosterFuel","name":"fuel","type":"tuple"},{"components":[{"internalType":"address","name":"booster","type":"address"},{"internalType":"uint64","name":"timestamp","type":"uint64"},{"internalType":"uint64","name":"nonce","type":"uint64"},{"internalType":"bool","name":"isLegacySignature","type":"bool"}],"internalType":"struct BoosterPayload","name":"boosterPayload","type":"tuple"}],"internalType":"struct BoostableERC20.BoostedSend[]","name":"sends","type":"tuple[]"},{"components":[{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"}],"internalType":"struct Signature[]","name":"signatures","type":"tuple[]"}],"name":"boostedSendBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"boostedTransferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"components":[{"internalType":"uint8","name":"tokenAlias","type":"uint8"},{"internalType":"uint96","name":"amount","type":"uint96"}],"internalType":"struct TokenFuel","name":"fuel","type":"tuple"}],"name":"burnFuel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bool","name":"isOptedIn","type":"bool"},{"internalType":"bool","name":"permaBoostActive","type":"bool"},{"internalType":"address","name":"optedInTo","type":"address"},{"internalType":"uint32","name":"optOutPeriod","type":"uint32"}],"internalType":"struct IOptIn.OptInStatus","name":"optInStatus","type":"tuple"},{"internalType":"address","name":"user","type":"address"},{"internalType":"uint8","name":"opType","type":"uint8"}],"name":"createNewOpHandleShared","outputs":[{"components":[{"internalType":"uint8","name":"opType","type":"uint8"},{"internalType":"uint64","name":"opId","type":"uint64"}],"internalType":"struct OpHandle","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"targetBooster","type":"address"},{"internalType":"bytes","name":"boosterMessage","type":"bytes"}],"name":"decodeAndHashBoosterMessage","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint96","name":"hodlAmount","type":"uint96"},{"internalType":"uint96","name":"refundAmount","type":"uint96"}],"name":"decreaseHodlBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"components":[{"internalType":"uint8","name":"opType","type":"uint8"},{"internalType":"uint64","name":"opId","type":"uint64"}],"internalType":"struct OpHandle","name":"opHandle","type":"tuple"}],"name":"deleteOpHandleShared","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"components":[{"internalType":"uint8","name":"opType","type":"uint8"},{"internalType":"uint64","name":"opId","type":"uint64"}],"internalType":"struct OpHandle","name":"opHandle","type":"tuple"}],"name":"finalizePendingOp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getNonce","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getOpCounter","outputs":[{"components":[{"internalType":"uint64","name":"value","type":"uint64"},{"internalType":"uint64","name":"nextFinalize","type":"uint64"},{"internalType":"uint64","name":"nextRevert","type":"uint64"}],"internalType":"struct OpCounter","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint64","name":"opId","type":"uint64"}],"name":"getOpMetadata","outputs":[{"components":[{"internalType":"uint8","name":"opType","type":"uint8"},{"internalType":"uint64","name":"createdAt","type":"uint64"},{"internalType":"address","name":"booster","type":"address"}],"internalType":"struct OpMetadata","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hodl","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenHolder","type":"address"}],"name":"hodlBalanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint96","name":"amount","type":"uint96"}],"name":"hodlTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint96","name":"amount","type":"uint96"}],"name":"increaseHodlBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint96","name":"hodlAmount","type":"uint96"}],"name":"migrateHodlBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint96","name":"amount","type":"uint96"}],"name":"revertHodlBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"components":[{"internalType":"uint8","name":"opType","type":"uint8"},{"internalType":"uint64","name":"opId","type":"uint64"}],"internalType":"struct OpHandle","name":"opHandle","type":"tuple"},{"internalType":"bytes","name":"boosterMessage","type":"bytes"},{"components":[{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"}],"internalType":"struct Signature","name":"signature","type":"tuple"}],"name":"revertPendingOp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"components":[{"internalType":"uint8","name":"opType","type":"uint8"},{"internalType":"uint64","name":"opId","type":"uint64"}],"internalType":"struct OpHandle","name":"opHandle","type":"tuple"}],"name":"safeGetOpMetadata","outputs":[{"components":[{"internalType":"uint8","name":"opType","type":"uint8"},{"internalType":"uint64","name":"createdAt","type":"uint64"},{"internalType":"address","name":"booster","type":"address"}],"internalType":"struct OpMetadata","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenHolder","type":"address"}],"name":"unpackedDataOf","outputs":[{"components":[{"internalType":"uint96","name":"balance","type":"uint96"},{"internalType":"uint96","name":"hodlBalance","type":"uint96"},{"internalType":"uint64","name":"nonce","type":"uint64"}],"internalType":"struct ERC20.UnpackedData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
6101806040523480156200001257600080fd5b50604051620061ff380380620061ff8339810160408190526200003591620005dc565b60405180604001604052806007815260200166507572706f736560c81b815250604051806040016040528060048152602001635052505360e01b81525087868686868480807f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f7fc941133d32664f2f3a7ee899586c7b1f0212a1ab605ead437725dcc4c5b199f97fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6620000ed6200034960201b60201c565b30604051602001620001049594939291906200069d565b60408051808303601f19018152919052805160209091012060609190911b6001600160601b03191660805260a0525060009050620001416200034d565b600480546001600160a01b0319166001600160a01b038316908117909155604051919250906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3508651620001a49060069060208a019062000520565b508551620001ba90600790602089019062000520565b506001600160601b0319606085811b821660c05284811b821660e05283811b82166101005282901b16610120526040516329965a1d60e01b8152731820a4b7618bde71dce8cdc73aab6c95905fad24906329965a1d90620002449030907f51d0ab336ae4fc621cb076e5c123b2236d97b9709e1ff2e304c6d727b35c6e4b9082906004016200067a565b600060405180830381600087803b1580156200025f57600080fd5b505af115801562000274573d6000803e3d6000fd5b50506040516329965a1d60e01b8152731820a4b7618bde71dce8cdc73aab6c95905fad2492506329965a1d9150620002d59030907faea199e31a596269b42cdafd93407f14436db6e4cad65417994c2eb37381e05a9082906004016200067a565b600060405180830381600087803b158015620002f057600080fd5b505af115801562000305573d6000803e3d6000fd5b5050506001600160601b031960608e811b8216610140528d901b1661016052506200033c96503395508d9450506200035192505050565b5050505050505062000738565b4690565b3390565b606081901b1762000363828262000367565b5050565b6001600160a01b038216620003995760405162461bcd60e51b81526004016200039090620006ea565b60405180910390fd5b80606081901c6001600160601b03811615620003bd576001600160601b0381166005555b620003c7620005a5565b6001600160a01b038516600090815260086020526040902054620003eb90620004a3565b80516020820151919250808501916001600160601b0391821690830190911610156200042b5760405162461bcd60e51b81526004016200039090620006c9565b6001600160601b03811682526200044282620004de565b6001600160a01b0387166000818152600860205260408082209390935591519091907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90620004939088906200070b565b60405180910390a3505050505050565b620004ad620005a5565b620004b7620005a5565b6001600160601b038381168252606084901c16602082015260c09290921c60408301525090565b805160208201516040909201516001600160601b0390911660609290921b600160601b600160c01b03169190911760c09190911b6001600160c01b0319161790565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200056357805160ff191683800117855562000593565b8280016001018555821562000593579182015b828111156200059357825182559160200191906001019062000576565b50620005a1929150620005c5565b5090565b604080516060810182526000808252602082018190529181019190915290565b5b80821115620005a15760008155600101620005c6565b600080600080600080600060e0888a031215620005f7578283fd5b8751965060208801516200060b816200071f565b60408901519096506200061e816200071f565b606089015190955062000631816200071f565b608089015190945062000644816200071f565b60a089015190935062000657816200071f565b60c08901519092506200066a816200071f565b8091505092959891949750929550565b6001600160a01b0393841681526020810192909252909116604082015260600190565b9485526020850193909352604084019190915260608301526001600160a01b0316608082015260a00190565b60208082526007908201526622a9219918169960c91b604082015260600190565b60208082526007908201526645524332302d3160c81b604082015260600190565b6001600160601b0391909116815260200190565b6001600160a01b03811681146200073557600080fd5b50565b60805160601c60a05160c05160601c60e05160601c6101005160601c6101205160601c6101405160601c6101605160601c6159dc6200082360003980610c4152806116245280611cdc528061319a52806135de5280613b125280613c04525080611add5280612b3f52806138155280613d545250806119f352806134db5250806134a25280613e3e5250806134695280613df05250806109bc5280610bf85280610e1e5280610f8d528061104652806111d2528061138f52806115e1528061171452806134305280613da25250806125de5280612f8452508061194d52806126b052506159dc6000f3fe608060405234801561001057600080fd5b50600436106102535760003560e01c8063715018a611610146578063a9059cbb116100c3578063dc3b52d711610087578063dc3b52d71461053f578063dd62ed3e14610552578063f28adcbc14610565578063f2fde38b14610578578063f6979d201461058b578063fe9d93031461059e57610253565b8063a9059cbb146104d2578063af6eec54146104e5578063b44be68c14610506578063b63306d814610519578063ca56f1771461052c57610253565b80638da5cb5b1161010a5780638da5cb5b1461047c57806395d89b41146104845780639cc0b8b31461048c578063a457c2d7146104ac578063a4b2184f146104bf57610253565b8063715018a61461041b5780637675dda4146104235780637b67a64a146104365780637e94bfd2146104495780638a4ab6041461045c57610253565b80632ec4ac0a116101d45780634cc95123116101985780634cc95123146103ba5780635214e7ba146103cd57806360f14509146103e0578063688d872c146103f557806370a082311461040857610253565b80632ec4ac0a1461034c578063313ce5671461036c578063395093511461038157806340c10f1914610394578063470e5a92146103a757610253565b806323b872dd1161021b57806323b872dd146102d3578063240fa6de146102e6578063252b2d20146103065780632d0335ab146103195780632d168b791461033957610253565b806306fdde0314610258578063095ea7b31461027657806309ee40a01461029657806310b4a23d146102a957806318160ddd146102be575b600080fd5b6102606105b1565b60405161026d9190615199565b60405180910390f35b610289610284366004614816565b610648565b60405161026d9190615073565b6102896102a43660046145d7565b61066c565b6102bc6102b73660046147ac565b61070d565b005b6102c661073f565b60405161026d919061577f565b6102896102e1366004614597565b610745565b6102f96102f4366004614707565b61084b565b60405161026d919061571e565b6102f9610314366004614841565b61090e565b61032c610327366004614543565b610978565b60405161026d91906157a1565b610289610347366004614841565b6109af565b61035f61035a366004614543565b610a0c565b60405161026d9190615747565b610374610a36565b60405161026d91906157b5565b61028961038f366004614816565b610a3b565b6102bc6103a2366004614816565b610a7c565b6102bc6103b5366004614707565b610abb565b6102bc6103c8366004614970565b610b74565b6102896103db366004614707565b610beb565b6103e8610c3f565b60405161026d9190614eaa565b6102bc610403366004614add565b610c63565b6102c6610416366004614543565b610cfe565b6102bc610d22565b6102bc6104313660046148ca565b610da1565b6102bc610444366004614670565b610e13565b6102bc61045736600461486e565b610f82565b61046f61046a366004614c8b565b611033565b60405161026d9190615710565b6103e8611096565b6102606110a5565b61049f61049a366004614543565b611106565b60405161026d91906156e1565b6102896104ba366004614816565b611165565b6102896104cd366004614841565b6111c5565b6102896104e0366004614816565b611219565b6104f86104f33660046146ba565b611292565b60405161026d9291906150b5565b6102bc61051436600461473c565b61135f565b6102bc610527366004614670565b611384565b6102c661053a366004614543565b6114b5565b6102bc61054d366004614bf0565b6114dc565b6102c661056036600461455f565b6115ab565b6102bc61057336600461486e565b6115d6565b6102bc610586366004614543565b611652565b6102bc61059936600461489b565b611709565b6102bc6105ac366004614d1e565b61183e565b60068054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561063d5780601f106106125761010080835404028352916020019161063d565b820191906000526020600020905b81548152906001019060200180831161062057829003601f168201915b505050505090505b90565b600033610655818561188a565b6106608185856118c6565b60019150505b92915050565b6000610678868661188a565b610680613ef4565b6106898761192e565b90506106ce8787878488888080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506119d292505050565b6106f35760405162461bcd60e51b81526004016106ea906153a6565b60405180910390fd5b6106fe878787611a58565b60019150505b95945050505050565b610715611ad0565b6107315760405162461bcd60e51b81526004016106ea90615623565b61073b8282611b18565b5050565b60055490565b6000610751848461188a565b3361075a613ef4565b6107638661192e565b80519091508015610775575080602001515b156107d557816001600160a01b0316866001600160a01b0316146107ab5760405162461bcd60e51b81526004016106ea9061532b565b6107ca6000838888886040518060200160405280600081525087611d92565b600192505050610844565b6040805180820182526007815266115490cc8c0b4d60ca1b6020808301919091526001600160a01b03808a16600090815260098352848120918716815291529190912054610832918891859161082d91908990611ea9565b6118c6565b61083d868686611a58565b6001925050505b9392505050565b610853613f1b565b600060036000610867868660200151611ed5565b81526020810191909152604001600020805490915061010090046001600160401b03166108a65760405162461bcd60e51b81526004016106ea90615230565b8251815460ff9081169116146108ce5760405162461bcd60e51b81526004016106ea9061524e565b60408051606081018252915460ff8116835261010081046001600160401b03166020840152600160481b90046001600160a01b0316908201529392505050565b610916613f1b565b600360006109248585611ed5565b815260208082019290925260409081016000208151606081018352905460ff8116825261010081046001600160401b031693820193909352600160481b9092046001600160a01b0316908201529392505050565b6000610982613f1b565b6001600160a01b0383166000908152600860205260409020546109a490611f08565b604001519392505050565b6000336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146109f95760405162461bcd60e51b81526004016106ea90615603565b610a038383611f3f565b50600192915050565b610a14613f1b565b6001600160a01b03821660009081526008602052604090205461066690611f08565b601290565b6000610a47338461188a565b3360008181526009602090815260408083206001600160a01b0388168452909152902054610a039190859061082d9086611f8a565b610a84611faf565b6004546001600160a01b03908116911614610ab15760405162461bcd60e51b81526004016106ea9061552c565b61073b8282611fb3565b80516000610ac984846120c5565b90506000610adb8585602001516121a6565b9050610ae785856121f1565b60ff8316610aff57610afa81868461228d565b610b2e565b60ff831660011415610b1657610afa818684612405565b60405162461bcd60e51b81526004016106ea906153e6565b7ffd1941e7a2d0c6f1cc53e8a73873275917cb8a1cf357e47f86f6e681e5d0f0f185856020015185604051610b6593929190614fed565b60405180910390a15050505050565b60008251118015610b86575080518251145b610ba25760405162461bcd60e51b81526004016106ea906151ef565b60005b8251811015610be657610bde838281518110610bbd57fe5b6020026020010151838381518110610bd157fe5b60200260200101516114dc565b600101610ba5565b505050565b6000336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610c355760405162461bcd60e51b81526004016106ea90615603565b610a0383836121f1565b7f000000000000000000000000000000000000000000000000000000000000000090565b6020820151610c70613f1b565b6001600160a01b038216600090815260086020526040902054610c9290611f08565b9050610ca68460a00151826040015161259f565b610cc38460200151610cb886336125d7565b8660a0015186612682565b610ccb613f3b565b610cda83866080015184612916565b9050610cf785602001518387604001518860600151600186612bbf565b5050505050565b6001600160a01b03166000908152600860205260409020546001600160601b031690565b610d2a611faf565b6004546001600160a01b03908116911614610d575760405162461bcd60e51b81526004016106ea9061552c565b6004546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600480546001600160a01b0319169055565b60008251118015610db3575080518251145b610dcf5760405162461bcd60e51b81526004016106ea906151ef565b60005b8251811015610be657610e0b838281518110610dea57fe5b6020026020010151838381518110610dfe57fe5b6020026020010151610c63565b600101610dd2565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610e5b5760405162461bcd60e51b81526004016106ea90615603565b610e63613f1b565b6001600160a01b038416600090815260086020526040902054610e8590611f08565b9050610e8f613f1b565b836001600160a01b0316856001600160a01b031614610ed1576001600160a01b038416600090815260086020526040902054610eca90611f08565b9050610ed4565b50805b81516001600160601b0380851691161015610f015760405162461bcd60e51b81526004016106ea9061530b565b81516001600160601b039084900381168352602082018051850190911690526001600160a01b0385811690851614610f5657610f3c81612d09565b6001600160a01b0385166000908152600860205260409020555b610f5f82612d09565b6001600160a01b0390951660009081526008602052604090209490945550505050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610fca5760405162461bcd60e51b81526004016106ea90615603565b610fd2613f1b565b6001600160a01b038316600090815260086020526040902054610ff490611f08565b60208101805184016001600160601b03169052905061101281612d09565b6001600160a01b039093166000908152600860205260409020929092555050565b61103b613f3b565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146110835760405162461bcd60e51b81526004016106ea90615603565b61108e848484612d50565b949350505050565b6004546001600160a01b031690565b60078054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561063d5780601f106106125761010080835404028352916020019161063d565b61110e613f1b565b506001600160a01b0316600090815260016020908152604091829020825160608101845290546001600160401b038082168352600160401b8204811693830193909352600160801b90049091169181019190915290565b6000611171338461188a565b604080518082018252600881526708aa48664605a62760c31b602080830191909152336000818152600983528481206001600160a01b038916825290925292902054610a039291869161082d918790611ea9565b6000336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461120f5760405162461bcd60e51b81526004016106ea90615603565b610a038383612eee565b6000611225338461188a565b61122d611ad0565b611287573361123a613ef4565b6112438261192e565b80519091508015611255575080602001515b15611284576112796000333388886040518060200160405280600081525087611d92565b600192505050610666565b50505b610a03338484611a58565b60008060008351116112b65760405162461bcd60e51b81526004016106ea9061536a565b60006112c184612f39565b905060ff8116611307576112d3613f52565b848060200190518101906112e79190614b20565b90506112f38187612f7d565b8160c0015160200151935093505050611358565b60ff81166001141561134f5761131b613faf565b8480602001905181019061132f9190614a1e565b905061133b81876125d7565b8160a0015160200151935093505050611358565b60008092509250505b9250929050565b61136b84848484613013565b61137e8484600001518560200151613042565b50505050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146113cc5760405162461bcd60e51b81526004016106ea90615603565b6113d4613f1b565b6001600160a01b0384166000908152600860205260409020546113f690611f08565b9050611400613f1b565b836001600160a01b0316856001600160a01b031614611442576001600160a01b03841660009081526008602052604090205461143b90611f08565b9050611445565b50805b826001600160601b031681602001516001600160601b0316101561147b5760405162461bcd60e51b81526004016106ea90615428565b6020810180516001600160601b03908590038116909152825184011682526001600160a01b0385811690851614610f5657610f3c81612d09565b6001600160a01b031660009081526008602052604090205460601c6001600160601b031690565b602082015160408301516114ee613f1b565b6001600160a01b03831660009081526008602052604090205461151090611f08565b905061151a613f1b565b6001600160a01b03831660009081526008602052604090205461153c90611f08565b90506115508660c00151836040015161259f565b61156d86602001516115628833612f7d565b8860c0015188612682565b611575613f3b565b611584858860a00151856130cc565b90506115a28760200151848960400151858b6060015160018761328c565b50505050505050565b6001600160a01b03918216600090815260096020908152604080832093909416825291909152205490565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461161e5760405162461bcd60e51b81526004016106ea90615603565b61073b827f0000000000000000000000000000000000000000000000000000000000000000836001600160601b0316611a58565b61165a611faf565b6004546001600160a01b039081169116146116875760405162461bcd60e51b81526004016106ea9061552c565b6001600160a01b0381166116ad5760405162461bcd60e51b81526004016106ea9061526c565b6004546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600480546001600160a01b0319166001600160a01b0392909216919091179055565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146117515760405162461bcd60e51b81526004016106ea90615603565b806001600160601b0316826001600160601b031610156117835760405162461bcd60e51b81526004016106ea90615408565b61178b613f1b565b6001600160a01b0384166000908152600860205260409020546117ad90611f08565b9050826001600160601b031681602001516001600160601b031610156117e55760405162461bcd60e51b81526004016106ea90615428565b6020810180516001600160601b0390859003811690915282161561181357805182016001600160601b031681525b61181c81612d09565b6001600160a01b03909416600090815260086020526040902093909355505050565b611846613ef4565b61184f3361192e565b80519091508015611861575080602001515b1561187d57611877600133336000878787611d92565b5061073b565b610be633848460006133bb565b6001600160a01b038216158015906118aa57506001600160a01b03811615155b61073b5760405162461bcd60e51b81526004016106ea906154ec565b6001600160a01b0380841660008181526009602090815260408083209487168084529490915290819020849055517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259061192190859061577f565b60405180910390a3505050565b611936613ef4565b604051630f425aed60e11b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690631e84b5da90611982908590600401614eaa565b60806040518083038186803b15801561199a57600080fd5b505afa1580156119ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106669190614c23565b60006119dc611ad0565b6119e857506000610704565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614611a2057506001610704565b600182810151908181161415611a3a576001915050610704565b83602001518015611a49575083515b156106fe576000915050610704565b611a60613f1b565b6001600160a01b038416600090815260086020526040902054611a8290611f08565b9050611a8c613f1b565b6001600160a01b038416600090815260086020526040902054611aae90611f08565b9050611ab8613f3b565b611ac8868487858860008761328c565b505050505050565b6000336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161415611b0b57506001610645565b611b13613423565b905090565b678ac7230489e800006001600160601b031681602001516001600160601b03161115611b565760405162461bcd60e51b81526004016106ea906155a3565b6001600160a01b03821615801590611b7757506001600160a01b0382163314155b611b935760405162461bcd60e51b81526004016106ea906151ac565b805160ff16611c3d57611ba4613f1b565b6001600160a01b038316600090815260086020526040902054611bc690611f08565b905081602001516001600160601b031681600001516001600160601b03161015611c025760405162461bcd60e51b81526004016106ea90615448565b60208201518151036001600160601b03168152611c1e81612d09565b6001600160a01b0384166000908152600860205260409020555061073b565b805160ff1660011415611d7a57611c52613f1b565b6001600160a01b038316600090815260086020526040902054611c7490611f08565b905081602001516001600160601b031681602001516001600160601b03161015611cb05760405162461bcd60e51b81526004016106ea90615448565b602082810180519183018051929092036001600160601b03169091525160405163248de12b60e21b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169163923784ac91611d1e918791429060009060040161503c565b602060405180830381600087803b158015611d3857600080fd5b505af1158015611d4c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d709190614d68565b50611c1e81612d09565b60405162461bcd60e51b81526004016106ea906155c4565b611d9a613f3b565b611da582878a612d50565b9050611daf613fea565b611dbd82898989898961350f565b905080600a6000611dd28a8660200151611ed5565b8152602080820192909252604090810160002083518154858501516001600160601b03908116600160a01b9081026001600160a01b039485166001600160a01b03199485161785161785559487015160018501805460608a0151909316909602908416919092161790911617909155608083015180519192611e5c9260028501929091019061401a565b505050602082015182516040517fa824f616acaccd5102c745c13e260dc1ca704e95425ebc56e73a6e495d5a3d8992611e96928b92614fed565b60405180910390a1505050505050505050565b60008184841115611ecd5760405162461bcd60e51b81526004016106ea9190615199565b505050900390565b60008282604051602001611eea929190614e2f565b60405160208183030381529060405280519060200120905092915050565b611f10613f1b565b611f18613f1b565b6001600160601b038381168252606084901c16602082015260c09290921c60408301525090565b6001600160a01b038216600090815260016020526040902080546001600160401b03838116600160401b9092041614610be65760405162461bcd60e51b81526004016106ea90615643565b6000828201838110156108445760405162461bcd60e51b81526004016106ea906152d4565b3390565b6001600160a01b038216611fd95760405162461bcd60e51b81526004016106ea906156a2565b80606081901c6001600160601b03811615611ffc576001600160601b0381166005555b612004613f1b565b6001600160a01b03851660009081526008602052604090205461202690611f08565b80516020820151919250808501916001600160601b0391821690830190911610156120635760405162461bcd60e51b81526004016106ea90615561565b6001600160601b038116825261207882612d09565b6001600160a01b038716600081815260086020526040808220939093559151909190600080516020615987833981519152906120b59088906157c3565b60405180910390a3505050505050565b60006120cf613f1b565b6120d9848461084b565b60208101519091506120e9613ef4565b6120f28661192e565b8051909150612105575091506106669050565b612113868660200151611f3f565b60405163c2288c7160e01b81527385ae45a05971170b70744292e2f051c0c49cf9099063c2288c719061214c908690859060040161572c565b60206040518083038186803b15801561216457600080fd5b505af4158015612178573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061219c9190614d4c565b9695505050505050565b600080600a60006121b78686611ed5565b8152602081019190915260400160002080549091506001600160a01b03166108445760405162461bcd60e51b81526004016106ea906154ca565b6001600160a01b0382166000908152600260209081526040808320600190925291829020915163c112c77160e01b81529091907385ae45a05971170b70744292e2f051c0c49cf9099063c112c77190612257908790879087908790600390600401614ed8565b60006040518083038186803b15801561226f57600080fd5b505af4158015612283573d6000803e3d6000fd5b5050505050505050565b600183015483546001600160a01b0390911690600160a01b90046001600160601b0316306122b9613f1b565b6001600160a01b0382166000908152600860205260409020546122db90611f08565b90506122e5613f1b565b6001600160a01b03851660009081526008602052604090205461230790611f08565b9050836001600160601b031682600001516001600160601b0316101561233f5760405162461bcd60e51b81526004016106ea90615561565b81516001600160601b039085900381168352815180860191908116908216101561237b5760405162461bcd60e51b81526004016106ea90615561565b6001600160601b038116825261239083612d09565b6001600160a01b0385166000908152600860205260409020556123b282612d09565b6001600160a01b0380881660008181526008602052604090819020939093559151908a1690600080516020615987833981519152906123f29089906157c3565b60405180910390a3505050505050505050565b8254600160a01b90046001600160601b031661241f613f1b565b6001600160a01b03841660009081526008602052604090205461244190611f08565b905061244b613f3b565b60006124748684868a60010160149054906101000a90046001600160601b031689876001613692565b90503061247f613f1b565b6001600160a01b0382166000908152600860205260409020546124a190611f08565b9050826001600160601b031681600001516001600160601b031610156124d95760405162461bcd60e51b81526004016106ea90615561565b80518390036001600160601b031681526124f281612d09565b6001600160a01b03831660009081526008602052604090205561251485612d09565b6001600160a01b0389166000908152600860205260409081902091909155517fe7b1342ce7f88416536f0a97fd9274421e3718dd094f96e9cefec28f6d7002c19061256590889060028d01906157d7565b60405180910390a160006001600160a01b0316886001600160a01b0316600080516020615987833981519152886040516123f291906157c3565b60018260400151036001600160401b0316816001600160401b03161461073b5760405162461bcd60e51b81526004016106ea90615661565b60006108447f00000000000000000000000000000000000000000000000000000000000000007f72079e1ca444dd2cbc0e28bb80962e9f4fbafb3705580b6b9f66186befba03956001866020015187604001518860600151805190602001206126438a6080015161388d565b6126518b60a001518b6138f5565b6040516020016126679796959493929190615111565b6040516020818303038152906040528051906020012061393c565b81516001600160a01b031633146126ab5760405162461bcd60e51b81526004016106ea906153c8565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316630296287733886040518363ffffffff1660e01b81526004016126fc929190614ebe565b604080518083038186803b15801561271357600080fd5b505afa158015612727573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061274b91906149f1565b915091508161276c5760405162461bcd60e51b81526004016106ea90615388565b6020840151429082906001600160401b03610e108401811691811691821115916001918516106127b557826001600160401b0316886020015185036001600160401b0316111590505b8180156127bf5750805b6127db5760405162461bcd60e51b81526004016106ea906155e5565b8760600151156127f1576127ee89613951565b98505b600060018a89604001518a600001518b6020015160405160008152602001604052604051612822949392919061514d565b6020604051602081039080840390855afa158015612844573d6000803e3d6000fd5b505050602060405103519050886060015115801561287457508a6001600160a01b0316816001600160a01b031614155b156128d85760016128848b613951565b6040808b01518b516020808e0151845160008152909101938490526128a9949361514d565b6020604051602081039080840390855afa1580156128cb573d6000803e3d6000fd5b5050506020604051035190505b806001600160a01b03168b6001600160a01b0316146129095760405162461bcd60e51b81526004016106ea9061550e565b5050505050505050505050565b61291e613f3b565b612926613f3b565b60208401516001600160601b0316156129ea57678ac7230489e800006001600160601b031684602001516001600160601b031611156129775760405162461bcd60e51b81526004016106ea906155a3565b83602001516001600160601b031683600001516001600160601b031610156129b15760405162461bcd60e51b81526004016106ea90615448565b602084810180518551036001600160601b039081168652905116908201528060015b908160048111156129e057fe5b9052509050610844565b60408401516001600160601b031615612a9057678ac7230489e800006001600160601b031684604001516001600160601b03161115612a3b5760405162461bcd60e51b81526004016106ea906155a3565b83604001516001600160601b031683602001516001600160601b03161015612a755760405162461bcd60e51b81526004016106ea90615448565b60408401516001600160601b031660208201528060026129d3565b60608401516001600160601b031615612afc57678ac7230489e800006001600160601b031684606001516001600160601b03161115612ae15760405162461bcd60e51b81526004016106ea906155a3565b60608401516001600160601b031660208201528060046129d3565b83516001600160601b03161561108e576040805180820182526002815285516001600160601b0316602082015290516310b4a23d60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916310b4a23d91612b74918991600401614f11565b600060405180830381600087803b158015612b8e57600080fd5b505af1158015612ba2573d6000803e3d6000fd5b505085516001600160601b031660208401525081905060036129d3565b6000612bd2878787600042876000613692565b90508215612bef576040860180516001016001600160401b031690525b6001600160601b03811615612c3c5785516001600160601b0380831691161015612c2b5760405162461bcd60e51b81526004016106ea90615582565b85518190036001600160601b031686525b612c4586612d09565b6001600160a01b03881660009081526008602052604081209190915582516001600160601b03871691906004811115612c7a57fe5b905060608160ff16901b82179150606384602001516001600160601b0316901b821791507fe7b1342ce7f88416536f0a97fd9274421e3718dd094f96e9cefec28f6d7002c18287604051612ccf929190615788565b60405180910390a160006001600160a01b0316896001600160a01b0316600080516020615987833981519152856040516123f291906157c3565b805160208201516040909201516001600160601b0390911660609290921b6bffffffffffffffffffffffff60601b169190911760c09190911b6001600160c01b0319161790565b612d58613f3b565b6000612d6384613964565b9050612d6d613f3b565b5060408051808201825260ff851681526001600160401b0383166020808301919091526001600160a01b038716600090815260029091529190912054601911612dc85760405162461bcd60e51b81526004016106ea9061534c565b6040868101516001600160a01b0387811660009081526002602090815284822080546001810182559083528183208751910180548884015160ff1990911660ff9384161768ffffffffffffffff0019166101006001600160401b0392831602179091558651606081018852918b168252421691810191909152918316938201939093529091600390612e5a8987611ed5565b815260208082019290925260409081016000208351815493850151949092015160ff1990931660ff9092169190911768ffffffffffffffff0019166101006001600160401b0390941693909302929092177fffffff0000000000000000000000000000000000000000ffffffffffffffffff16600160481b6001600160a01b03909216919091021790555095945050505050565b6001600160a01b038216600090815260016020526040902080546001600160401b03838116600160801b9092041614610be65760405162461bcd60e51b81526004016106ea90615489565b60008082601f81518110612f4957fe5b0160209081015160f81c91508110610666578281601f0160ff1681518110612f6d57fe5b016020015160f81c905092915050565b60006108447f00000000000000000000000000000000000000000000000000000000000000007f7d1602bfd7297f6720514f0c3dabe77313ce0d25cd5085644e3fcdc5635df6606000866020015187604001518860600151896080015180519060200120612fee8b60a0015161388d565b612ffc8c60c001518c6138f5565b6040516020016126679897969594939291906150cc565b61301b613f1b565b613025858561084b565b9050613038858583602001518686613a01565b610cf785856121f1565b600061304e84836121a6565b805460018201549192506001600160601b03600160a01b91829004811692919091041661307c308784611a58565b6130898686868585613adc565b7f74c334759f9bfb4a4342d46ed6f4dfb2a3c6b7fe1c7c2288988e8b44bc281b8a8685876040516130bc93929190614fed565b60405180910390a1505050505050565b6130d4613f3b565b6130dc613f3b565b60208401516001600160601b03161561312d57678ac7230489e800006001600160601b031684602001516001600160601b031611156129775760405162461bcd60e51b81526004016106ea906155a3565b60408401516001600160601b031615612afc57678ac7230489e800006001600160601b031684604001516001600160601b0316111561317e5760405162461bcd60e51b81526004016106ea906155a3565b604080850151905163248de12b60e21b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163923784ac916131d4918991429060009060040161503c565b602060405180830381600087803b1580156131ee57600080fd5b505af1158015613202573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132269190614d68565b5083604001516001600160601b031683602001516001600160601b031610156132615760405162461bcd60e51b81526004016106ea90615448565b604084018051602080860180516001600160601b0393900383169052915116908201528060026129d3565b846001600160a01b0316876001600160a01b031614156132be5760405162461bcd60e51b81526004016106ea906152b2565b81156132d9576040860180516001016001600160401b031690525b855183906001600160601b03808316911610156133085760405162461bcd60e51b81526004016106ea906151cd565b86516001600160601b03908290038116885285518083019190811690821610156133445760405162461bcd60e51b81526004016106ea906154a8565b6001600160601b038116865261335988612d09565b6001600160a01b038a1660009081526008602052604090205561337b86612d09565b6001600160a01b0380891660008181526008602052604090819020939093559151908b1690600080516020615987833981519152906123f29086906157c3565b6001600160a01b0384166133e15760405162461bcd60e51b81526004016106ea90615468565b6133e9613f1b565b6001600160a01b03851660009081526008602052604090205461340b90611f08565b9050613415613f3b565b611ac8868387878786612bbf565b6000336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016141561345e57506001610645565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016141561349757506001610645565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614156134d057506001610645565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016141561350957506001610645565b50600090565b613517613fea565b865160ff1660011461353857613531878787878787613b82565b905061219c565b600160601b831061354557fe5b828061354f613f1b565b6001600160a01b03881660009081526008602052604090205461357190611f08565b805190915083906001600160601b039081169082161115613590575080515b91829003916001600160601b038116156135b8576135b88930836001600160601b0316611a58565b6001600160601b0383161561364857604051637354578360e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690637354578390613615908c90879060040161501a565b600060405180830381600087803b15801561362f57600080fd5b505af1158015613643573d6000803e3d6000fd5b505050505b50506040805160a0810182526001600160a01b03998a1681526001600160601b03938416602082015296909816978601979097529590951660608401526080830152509392505050565b60008060008084156136c957879150876001600160601b0316896001600160601b031610156136bd57fe5b509686900396876136d0565b5050875187905b6001600160601b0381161561371257886001600160601b0316816001600160601b031611156136fc5750875b61370581613be6565b9250846137125780820391505b60028651600481111561372157fe5b1415613731578560200151820191505b6001600160601b0382161561378957600061374f8c8c858b8a613bff565b90508381016001600160601b0380861690821610156137805760405162461bcd60e51b81526004016106ea90615682565b935061379a9050565b6001600160601b0388161561379a57fe5b6004865160048111156137a957fe5b14156137ef5785602001516001600160601b0316836001600160601b031610156137e55760405162461bcd60e51b81526004016106ea90615448565b8560200151830392505b6001600160601b0383161561387f57604051632d57fe2160e21b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063b55ff8849061384c908e90879060040161501a565b600060405180830381600087803b15801561386657600080fd5b505af115801561387a573d6000803e3d6000fd5b505050505b9a9950505050505050505050565b60007ff1f7d09b7527273cfd8370620354f0cd9a62e7bd3efaafdeca15664a7b3e045c82600001518360200151846040015185606001516040516020016138d895949392919061516b565b604051602081830303815290604052805190602001209050919050565b60007ff45b7e63030d0a046d85957a672f9a0806d76fff4cc32355d75f64eb6e83d00882846020015185604001518660600151604051602001611eea95949392919061507e565b60008282604051602001611eea929190614e8f565b6000816040516020016138d89190614e5e565b6001600160a01b03811660009081526001602081905260408220805490916001600160401b0380831690910191600160401b9004166139c65781546fffffffffffffffff00000000000000001916600160401b6001600160401b038316021782555b815467ffffffffffffffff60801b1916600160801b6001600160401b0383169081029190911767ffffffffffffffff19161790915592915050565b613a0f858560200151612eee565b613a17613ef4565b613a208661192e565b80519091508015613a46575080604001516001600160a01b0316336001600160a01b0316145b613a625760405162461bcd60e51b81526004016106ea906156c3565b6060613a6c613cfe565b60405163e17e413160e01b81529091507385ae45a05971170b70744292e2f051c0c49cf9099063e17e413190613ab0908a90899087908a9088908b90600401614f43565b60006040518083038186803b158015613ac857600080fd5b505af4158015612909573d6000803e3d6000fd5b60ff8416600114613aec57610cf7565b6001600160601b03811615610cf7576040516343ad8a9160e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906343ad8a9190613b49908890859060040161501a565b600060405180830381600087803b158015613b6357600080fd5b505af1158015613b77573d6000803e3d6000fd5b505050505050505050565b613b8a613fea565b613b95853085611a58565b613b9d613fea565b50506040805160a0810182526001600160a01b0380881682526001600160601b038516602083015285169181019190915260006060820152608081018290529695505050505050565b60006106666001600160601b0383166301e13380613e87565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663923784ac888787876040518563ffffffff1660e01b8152600401613c54949392919061503c565b602060405180830381600087803b158015613c6e57600080fd5b505af1158015613c82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ca69190614d68565b9050846001600160601b031686602001516001600160601b03161015613cde5760405162461bcd60e51b81526004016106ea90615210565b6020860180516001600160601b0390879003169052905095945050505050565b60408051600580825260c082019092526060918291906020820160a0803683370190505090503081600081518110613d3257fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507f000000000000000000000000000000000000000000000000000000000000000081600181518110613d8057fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507f000000000000000000000000000000000000000000000000000000000000000081600281518110613dce57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507f000000000000000000000000000000000000000000000000000000000000000081600381518110613e1c57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507f000000000000000000000000000000000000000000000000000000000000000081600481518110613e6a57fe5b6001600160a01b0390921660209283029190910190910152905090565b600080613ea063ffffffff84166301e133806012613ecb565b6004029050600068056bc75e2d6310000082860281613ebb57fe5b049050600160601b811061108e57fe5b6000600a8383600101600a0a860281613ee057fe5b0460050181613eeb57fe5b04949350505050565b60408051608081018252600080825260208201819052918101829052606081019190915290565b604080516060810182526000808252602082018190529181019190915290565b604080518082019091526000808252602082015290565b6040518060e00160405280600060ff16815260200160006001600160a01b0316815260200160006001600160a01b031681526020016000815260200160608152602001613f9d613ef4565b8152602001613faa613ef4565b905290565b6040518060c00160405280600060ff16815260200160006001600160a01b031681526020016000815260200160608152602001613f9d613ef4565b6040805160a081018252600080825260208201819052918101829052606080820192909252608081019190915290565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061405b57805160ff1916838001178555614088565b82800160010185558215614088579182015b8281111561408857825182559160200191906001019061406d565b50614094929150614098565b5090565b5b808211156140945760008155600101614099565b803561066681615915565b805161066681615915565b600082601f8301126140d3578081fd5b81356140e66140e18261589b565b615875565b818152915060208083019084810160608085028701830188101561410957600080fd5b60005b858110156141305761411e89846144e9565b8552938301939181019160010161410c565b50505050505092915050565b600082601f83011261414c578081fd5b813561415a6140e1826158ba565b915080825283602082850101111561417157600080fd5b8060208401602084013760009082016020015292915050565b600082601f83011261419a578081fd5b81516141a86140e1826158ba565b91508082528360208285010111156141bf57600080fd5b6141d08160208401602086016158e9565b5092915050565b600061018082840312156141e9578081fd5b6141f360c0615875565b9050813561420081615962565b8152602082013561421081615915565b60208201526040828101359082015260608201356001600160401b0381111561423857600080fd5b6142448482850161413c565b6060830152506142578360808401614323565b608082015261426a8361010084016143ea565b60a082015292915050565b60006101a08284031215614287578081fd5b61429160e0615875565b905061429d838361452d565b81526142ac83602084016140ad565b60208201526142be83604084016140ad565b60408201526060820135606082015260808201356001600160401b038111156142e657600080fd5b6142f28482850161413c565b6080830152506143058360a08401614323565b60a08201526143188361012084016143ea565b60c082015292915050565b600060808284031215614334578081fd5b61433e6080615875565b9050813561434b81615971565b8152602082013561435b81615971565b6020820152604082013561436e81615971565b6040820152606082013561438181615971565b606082015292915050565b60006080828403121561439d578081fd5b6143a76080615875565b905081516143b481615971565b815260208201516143c481615971565b602082015260408201516143d781615971565b6040820152606082015161438181615971565b6000608082840312156143fb578081fd5b6144056080615875565b9050813561441281615915565b815260208201356144228161594d565b602082015260408201356144358161594d565b604082015260608201356143818161592d565b600060808284031215614459578081fd5b6144636080615875565b9050815161447081615915565b815260208201516144808161594d565b602082015260408201516144938161594d565b604082015260608201516143818161592d565b6000604082840312156144b7578081fd5b6144c16040615875565b905081356144ce81615962565b815260208201356144de8161594d565b602082015292915050565b6000606082840312156144fa578081fd5b6145046060615875565b90508135815260208201356020820152604082013561452281615962565b604082015292915050565b803561066681615962565b805161066681615962565b600060208284031215614554578081fd5b813561084481615915565b60008060408385031215614571578081fd5b823561457c81615915565b9150602083013561458c81615915565b809150509250929050565b6000806000606084860312156145ab578081fd5b83356145b681615915565b925060208401356145c681615915565b929592945050506040919091013590565b6000806000806000608086880312156145ee578283fd5b85356145f981615915565b9450602086013561460981615915565b93506040860135925060608601356001600160401b038082111561462b578283fd5b818801915088601f83011261463e578283fd5b81358181111561464c578384fd5b89602082850101111561465d578384fd5b9699959850939650602001949392505050565b600080600060608486031215614684578081fd5b833561468f81615915565b9250602084013561469f81615915565b915060408401356146af81615971565b809150509250925092565b600080604083850312156146cc578182fd5b82356146d781615915565b915060208301356001600160401b038111156146f1578182fd5b6146fd8582860161413c565b9150509250929050565b60008060608385031215614719578182fd5b823561472481615915565b915061473384602085016144a6565b90509250929050565b60008060008060e08587031215614751578182fd5b843561475c81615915565b935061476b86602087016144a6565b925060608501356001600160401b03811115614785578283fd5b6147918782880161413c565b9250506147a186608087016144e9565b905092959194509250565b60008082840360608112156147bf578283fd5b83356147ca81615915565b92506040601f19820112156147dd578182fd5b506147e86040615875565b60208401356147f681615962565b8152604084013561480681615971565b6020820152919491935090915050565b60008060408385031215614828578182fd5b823561483381615915565b946020939093013593505050565b60008060408385031215614853578182fd5b823561485e81615915565b9150602083013561458c8161594d565b60008060408385031215614880578182fd5b823561488b81615915565b9150602083013561458c81615971565b6000806000606084860312156148af578081fd5b83356148ba81615915565b9250602084013561469f81615971565b600080604083850312156148dc578182fd5b82356001600160401b03808211156148f2578384fd5b818501915085601f830112614905578384fd5b81356149136140e18261589b565b81815260208082019190858101885b8581101561494b576149398c8484358b01016141d7565b85529382019390820190600101614922565b50919750880135945050505080821115614963578283fd5b506146fd858286016140c3565b60008060408385031215614982578182fd5b82356001600160401b0380821115614998578384fd5b818501915085601f8301126149ab578384fd5b81356149b96140e18261589b565b81815260208082019190858101885b8581101561494b576149df8c8484358b0101614275565b855293820193908201906001016149c8565b60008060408385031215614a03578182fd5b8251614a0e8161592d565b6020939093015192949293505050565b600060208284031215614a2f578081fd5b81516001600160401b0380821115614a45578283fd5b908301906101808286031215614a59578283fd5b614a6360c0615875565b8251614a6e81615962565b8152614a7d86602085016140b8565b602082015260408301516040820152606083015182811115614a9d578485fd5b614aa98782860161418a565b606083015250614abc866080850161438c565b6080820152614acf866101008501614448565b60a082015295945050505050565b60008060808385031215614aef578182fd5b82356001600160401b03811115614b04578283fd5b614b10858286016141d7565b92505061473384602085016144e9565b600060208284031215614b31578081fd5b81516001600160401b0380821115614b47578283fd5b908301906101a08286031215614b5b578283fd5b614b6560e0615875565b614b6f8684614538565b8152614b7e86602085016140b8565b6020820152614b9086604085016140b8565b604082015260608301516060820152608083015182811115614bb0578485fd5b614bbc8782860161418a565b608083015250614bcf8660a0850161438c565b60a0820152614be2866101208501614448565b60c082015295945050505050565b60008060808385031215614c02578182fd5b82356001600160401b03811115614c17578283fd5b614b1085828601614275565b600060808284031215614c34578081fd5b614c3e6080615875565b8251614c498161592d565b81526020830151614c598161592d565b60208201526040830151614c6c81615915565b60408201526060830151614c7f8161593b565b60608201529392505050565b600080600083850360c0811215614ca0578182fd5b6080811215614cad578182fd5b50614cb86080615875565b8435614cc38161592d565b81526020850135614cd38161592d565b60208201526040850135614ce681615915565b60408201526060850135614cf98161593b565b606082015292506080840135614d0e81615915565b915060a08401356146af81615962565b60008060408385031215614d30578182fd5b8235915060208301356001600160401b038111156146f1578182fd5b600060208284031215614d5d578081fd5b81516108448161594d565b600060208284031215614d79578081fd5b815161084481615971565b60008151808452614d9c8160208601602086016158e9565b601f01601f19169290920160200192915050565b805160ff1682526020908101516001600160401b0316910152565b805160ff1682526020808201516001600160401b0316908301526040908101516001600160a01b0316910152565b8051151582526020808201511515908301526040808201516001600160a01b03169083015260609081015163ffffffff16910152565b60609290921b6bffffffffffffffffffffffff1916825260c01b6001600160c01b0319166014820152601c0190565b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000008152601c810191909152603c0190565b61190160f01b81526002810192909252602282015260420190565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b038616815260c08101614ef56020830187614db0565b8460608301528360808301528260a08301529695505050505050565b6001600160a01b03929092168252805160ff1660208084019190915201516001600160601b0316604082015260600190565b600061016060018060a01b03808a16845260206001600160401b038a1681860152614f71604086018a614df9565b8260c0860152614f8383860189614d84565b85810360e08701528751808252828901945090820190855b81811015614fb9578551851683529483019491830191600101614f9b565b50508094505085516101008601528086015161012086015250505060ff604084015116610140830152979650505050505050565b6001600160a01b039390931683526001600160401b0391909116602083015260ff16604082015260600190565b6001600160a01b039290921682526001600160601b0316602082015260400190565b6001600160a01b039490941684526001600160601b0392909216602084015263ffffffff1660408301521515606082015260800190565b901515815260200190565b9485526001600160a01b039390931660208501526001600160401b0391821660408501521660608301521515608082015260a00190565b9182526001600160401b0316602082015260400190565b97885260ff9690961660208801526001600160a01b039485166040880152929093166060860152608085015260a084019190915260c083015260e08201526101000190565b96875260ff9590951660208701526001600160a01b039390931660408601526060850191909152608084015260a083015260c082015260e00190565b93845260ff9290921660208401526040830152606082015260800190565b9485526001600160601b03938416602086015291831660408501528216606084015216608082015260a00190565b6000602082526108446020830184614d84565b602080825260079082015266505250532d313160c81b604082015260600190565b602080825260089082015267045524332302d31360c41b604082015260600190565b60208082526007908201526622a9219918169b60c91b604082015260600190565b6020808252600690820152650a0a4a0a65a760d31b604082015260600190565b60208082526004908201526350422d3160e01b604082015260600190565b6020808252600490820152632821169960e11b604082015260600190565b60208082526026908201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160408201526564647265737360d01b606082015260800190565b60208082526008908201526745524332302d313960c01b604082015260600190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b602080825260069082015265505250532d3360d01b604082015260600190565b60208082526007908201526645524332302d3760c81b604082015260600190565b60208082526004908201526350422d3360e01b604082015260600190565b60208082526004908201526350422d3760e01b604082015260600190565b60208082526004908201526341422d3360e01b604082015260600190565b60208082526008908201526745524332302d313760c01b604082015260600190565b60208082526004908201526320a1169960e11b604082015260600190565b60208082526008908201526745524332302d313560c01b604082015260600190565b60208082526006908201526514149414cb4d60d21b604082015260600190565b602080825260069082015265505250532d3560d01b604082015260600190565b602080825260069082015265505250532d3760d01b604082015260600190565b60208082526007908201526608aa48664605a760cb1b604082015260600190565b602080825260059082015264050422d31360dc1b604082015260600190565b60208082526008908201526722a921991816989960c11b604082015260600190565b60208082526008908201526722a921991816989b60c11b604082015260600190565b60208082526008908201526745524332302d313360c01b604082015260600190565b60208082526004908201526341422d3560e01b604082015260600190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60208082526007908201526622a9219918169960c91b604082015260600190565b60208082526007908201526645524332302d3960c81b604082015260600190565b6020808252600790820152660505250532d31360cc1b604082015260600190565b6020808252600790820152662829282996989960c91b604082015260600190565b60208082526004908201526310508b4d60e21b604082015260600190565b602080825260069082015265505250532d3160d01b604082015260600190565b60208082526006908201526528292829969960d11b604082015260600190565b60208082526004908201526350422d3960e01b604082015260600190565b60208082526007908201526645524332302d3560c81b604082015260600190565b60208082526006908201526528292829969b60d11b604082015260600190565b60208082526007908201526645524332302d3160c81b604082015260600190565b6020808252600490820152632821169b60e11b604082015260600190565b81516001600160401b039081168252602080840151821690830152604092830151169181019190915260600190565b604081016106668284614db0565b606081016106668284614dcb565b60e0810161573a8285614dcb565b6108446060830184614df9565b81516001600160601b039081168252602080840151909116908201526040918201516001600160401b03169181019190915260600190565b90815260200190565b60008382526040602083015261108e6040830184614d84565b6001600160401b0391909116815260200190565b60ff91909116815260200190565b6001600160601b0391909116815260200190565b6000604082016001600160601b0385168352602060408185015282855460018082166000811461580e576001811461582c57615867565b60028304607f16865260ff1983166060890152608088019350615867565b6002830461583a818861577f565b6158438b6158dd565b895b8381101561585e57815483820152908501908801615845565b91909101955050505b509198975050505050505050565b6040518181016001600160401b038111828210171561589357600080fd5b604052919050565b60006001600160401b038211156158b0578081fd5b5060209081020190565b60006001600160401b038211156158cf578081fd5b50601f01601f191660200190565b60009081526020902090565b60005b838110156159045781810151838201526020016158ec565b8381111561137e5750506000910152565b6001600160a01b038116811461592a57600080fd5b50565b801515811461592a57600080fd5b63ffffffff8116811461592a57600080fd5b6001600160401b038116811461592a57600080fd5b60ff8116811461592a57600080fd5b6001600160601b038116811461592a57600080fdfeddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa26469706673582212203a93a0f3b850e0334975edea46270309eb57017a2ad224cf372a4178bbda627364736f6c634300060c00330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a916bc21d2429645585abede4ae00742a16dd1c6000000000000000000000000f3d6af45c6dfec43216cc3347ea91fefba0849d1000000000000000000000000ac0122e9258a85ba5479db764dc8ef91cab08db00000000000000000000000003b4da358199060bcc5a527ab60099fb6a908aae9000000000000000000000000025dbd03ed18b4b8425af51b4d05f5b00e78208a000000000000000000000000e7cd2797ac6f08b5721c7e7fcc991251df5e3884
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106102535760003560e01c8063715018a611610146578063a9059cbb116100c3578063dc3b52d711610087578063dc3b52d71461053f578063dd62ed3e14610552578063f28adcbc14610565578063f2fde38b14610578578063f6979d201461058b578063fe9d93031461059e57610253565b8063a9059cbb146104d2578063af6eec54146104e5578063b44be68c14610506578063b63306d814610519578063ca56f1771461052c57610253565b80638da5cb5b1161010a5780638da5cb5b1461047c57806395d89b41146104845780639cc0b8b31461048c578063a457c2d7146104ac578063a4b2184f146104bf57610253565b8063715018a61461041b5780637675dda4146104235780637b67a64a146104365780637e94bfd2146104495780638a4ab6041461045c57610253565b80632ec4ac0a116101d45780634cc95123116101985780634cc95123146103ba5780635214e7ba146103cd57806360f14509146103e0578063688d872c146103f557806370a082311461040857610253565b80632ec4ac0a1461034c578063313ce5671461036c578063395093511461038157806340c10f1914610394578063470e5a92146103a757610253565b806323b872dd1161021b57806323b872dd146102d3578063240fa6de146102e6578063252b2d20146103065780632d0335ab146103195780632d168b791461033957610253565b806306fdde0314610258578063095ea7b31461027657806309ee40a01461029657806310b4a23d146102a957806318160ddd146102be575b600080fd5b6102606105b1565b60405161026d9190615199565b60405180910390f35b610289610284366004614816565b610648565b60405161026d9190615073565b6102896102a43660046145d7565b61066c565b6102bc6102b73660046147ac565b61070d565b005b6102c661073f565b60405161026d919061577f565b6102896102e1366004614597565b610745565b6102f96102f4366004614707565b61084b565b60405161026d919061571e565b6102f9610314366004614841565b61090e565b61032c610327366004614543565b610978565b60405161026d91906157a1565b610289610347366004614841565b6109af565b61035f61035a366004614543565b610a0c565b60405161026d9190615747565b610374610a36565b60405161026d91906157b5565b61028961038f366004614816565b610a3b565b6102bc6103a2366004614816565b610a7c565b6102bc6103b5366004614707565b610abb565b6102bc6103c8366004614970565b610b74565b6102896103db366004614707565b610beb565b6103e8610c3f565b60405161026d9190614eaa565b6102bc610403366004614add565b610c63565b6102c6610416366004614543565b610cfe565b6102bc610d22565b6102bc6104313660046148ca565b610da1565b6102bc610444366004614670565b610e13565b6102bc61045736600461486e565b610f82565b61046f61046a366004614c8b565b611033565b60405161026d9190615710565b6103e8611096565b6102606110a5565b61049f61049a366004614543565b611106565b60405161026d91906156e1565b6102896104ba366004614816565b611165565b6102896104cd366004614841565b6111c5565b6102896104e0366004614816565b611219565b6104f86104f33660046146ba565b611292565b60405161026d9291906150b5565b6102bc61051436600461473c565b61135f565b6102bc610527366004614670565b611384565b6102c661053a366004614543565b6114b5565b6102bc61054d366004614bf0565b6114dc565b6102c661056036600461455f565b6115ab565b6102bc61057336600461486e565b6115d6565b6102bc610586366004614543565b611652565b6102bc61059936600461489b565b611709565b6102bc6105ac366004614d1e565b61183e565b60068054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561063d5780601f106106125761010080835404028352916020019161063d565b820191906000526020600020905b81548152906001019060200180831161062057829003601f168201915b505050505090505b90565b600033610655818561188a565b6106608185856118c6565b60019150505b92915050565b6000610678868661188a565b610680613ef4565b6106898761192e565b90506106ce8787878488888080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506119d292505050565b6106f35760405162461bcd60e51b81526004016106ea906153a6565b60405180910390fd5b6106fe878787611a58565b60019150505b95945050505050565b610715611ad0565b6107315760405162461bcd60e51b81526004016106ea90615623565b61073b8282611b18565b5050565b60055490565b6000610751848461188a565b3361075a613ef4565b6107638661192e565b80519091508015610775575080602001515b156107d557816001600160a01b0316866001600160a01b0316146107ab5760405162461bcd60e51b81526004016106ea9061532b565b6107ca6000838888886040518060200160405280600081525087611d92565b600192505050610844565b6040805180820182526007815266115490cc8c0b4d60ca1b6020808301919091526001600160a01b03808a16600090815260098352848120918716815291529190912054610832918891859161082d91908990611ea9565b6118c6565b61083d868686611a58565b6001925050505b9392505050565b610853613f1b565b600060036000610867868660200151611ed5565b81526020810191909152604001600020805490915061010090046001600160401b03166108a65760405162461bcd60e51b81526004016106ea90615230565b8251815460ff9081169116146108ce5760405162461bcd60e51b81526004016106ea9061524e565b60408051606081018252915460ff8116835261010081046001600160401b03166020840152600160481b90046001600160a01b0316908201529392505050565b610916613f1b565b600360006109248585611ed5565b815260208082019290925260409081016000208151606081018352905460ff8116825261010081046001600160401b031693820193909352600160481b9092046001600160a01b0316908201529392505050565b6000610982613f1b565b6001600160a01b0383166000908152600860205260409020546109a490611f08565b604001519392505050565b6000336001600160a01b037f000000000000000000000000ac0122e9258a85ba5479db764dc8ef91cab08db016146109f95760405162461bcd60e51b81526004016106ea90615603565b610a038383611f3f565b50600192915050565b610a14613f1b565b6001600160a01b03821660009081526008602052604090205461066690611f08565b601290565b6000610a47338461188a565b3360008181526009602090815260408083206001600160a01b0388168452909152902054610a039190859061082d9086611f8a565b610a84611faf565b6004546001600160a01b03908116911614610ab15760405162461bcd60e51b81526004016106ea9061552c565b61073b8282611fb3565b80516000610ac984846120c5565b90506000610adb8585602001516121a6565b9050610ae785856121f1565b60ff8316610aff57610afa81868461228d565b610b2e565b60ff831660011415610b1657610afa818684612405565b60405162461bcd60e51b81526004016106ea906153e6565b7ffd1941e7a2d0c6f1cc53e8a73873275917cb8a1cf357e47f86f6e681e5d0f0f185856020015185604051610b6593929190614fed565b60405180910390a15050505050565b60008251118015610b86575080518251145b610ba25760405162461bcd60e51b81526004016106ea906151ef565b60005b8251811015610be657610bde838281518110610bbd57fe5b6020026020010151838381518110610bd157fe5b60200260200101516114dc565b600101610ba5565b505050565b6000336001600160a01b037f000000000000000000000000ac0122e9258a85ba5479db764dc8ef91cab08db01614610c355760405162461bcd60e51b81526004016106ea90615603565b610a0383836121f1565b7f000000000000000000000000ac0122e9258a85ba5479db764dc8ef91cab08db090565b6020820151610c70613f1b565b6001600160a01b038216600090815260086020526040902054610c9290611f08565b9050610ca68460a00151826040015161259f565b610cc38460200151610cb886336125d7565b8660a0015186612682565b610ccb613f3b565b610cda83866080015184612916565b9050610cf785602001518387604001518860600151600186612bbf565b5050505050565b6001600160a01b03166000908152600860205260409020546001600160601b031690565b610d2a611faf565b6004546001600160a01b03908116911614610d575760405162461bcd60e51b81526004016106ea9061552c565b6004546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600480546001600160a01b0319169055565b60008251118015610db3575080518251145b610dcf5760405162461bcd60e51b81526004016106ea906151ef565b60005b8251811015610be657610e0b838281518110610dea57fe5b6020026020010151838381518110610dfe57fe5b6020026020010151610c63565b600101610dd2565b336001600160a01b037f000000000000000000000000ac0122e9258a85ba5479db764dc8ef91cab08db01614610e5b5760405162461bcd60e51b81526004016106ea90615603565b610e63613f1b565b6001600160a01b038416600090815260086020526040902054610e8590611f08565b9050610e8f613f1b565b836001600160a01b0316856001600160a01b031614610ed1576001600160a01b038416600090815260086020526040902054610eca90611f08565b9050610ed4565b50805b81516001600160601b0380851691161015610f015760405162461bcd60e51b81526004016106ea9061530b565b81516001600160601b039084900381168352602082018051850190911690526001600160a01b0385811690851614610f5657610f3c81612d09565b6001600160a01b0385166000908152600860205260409020555b610f5f82612d09565b6001600160a01b0390951660009081526008602052604090209490945550505050565b336001600160a01b037f000000000000000000000000ac0122e9258a85ba5479db764dc8ef91cab08db01614610fca5760405162461bcd60e51b81526004016106ea90615603565b610fd2613f1b565b6001600160a01b038316600090815260086020526040902054610ff490611f08565b60208101805184016001600160601b03169052905061101281612d09565b6001600160a01b039093166000908152600860205260409020929092555050565b61103b613f3b565b336001600160a01b037f000000000000000000000000ac0122e9258a85ba5479db764dc8ef91cab08db016146110835760405162461bcd60e51b81526004016106ea90615603565b61108e848484612d50565b949350505050565b6004546001600160a01b031690565b60078054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561063d5780601f106106125761010080835404028352916020019161063d565b61110e613f1b565b506001600160a01b0316600090815260016020908152604091829020825160608101845290546001600160401b038082168352600160401b8204811693830193909352600160801b90049091169181019190915290565b6000611171338461188a565b604080518082018252600881526708aa48664605a62760c31b602080830191909152336000818152600983528481206001600160a01b038916825290925292902054610a039291869161082d918790611ea9565b6000336001600160a01b037f000000000000000000000000ac0122e9258a85ba5479db764dc8ef91cab08db0161461120f5760405162461bcd60e51b81526004016106ea90615603565b610a038383612eee565b6000611225338461188a565b61122d611ad0565b611287573361123a613ef4565b6112438261192e565b80519091508015611255575080602001515b15611284576112796000333388886040518060200160405280600081525087611d92565b600192505050610666565b50505b610a03338484611a58565b60008060008351116112b65760405162461bcd60e51b81526004016106ea9061536a565b60006112c184612f39565b905060ff8116611307576112d3613f52565b848060200190518101906112e79190614b20565b90506112f38187612f7d565b8160c0015160200151935093505050611358565b60ff81166001141561134f5761131b613faf565b8480602001905181019061132f9190614a1e565b905061133b81876125d7565b8160a0015160200151935093505050611358565b60008092509250505b9250929050565b61136b84848484613013565b61137e8484600001518560200151613042565b50505050565b336001600160a01b037f000000000000000000000000ac0122e9258a85ba5479db764dc8ef91cab08db016146113cc5760405162461bcd60e51b81526004016106ea90615603565b6113d4613f1b565b6001600160a01b0384166000908152600860205260409020546113f690611f08565b9050611400613f1b565b836001600160a01b0316856001600160a01b031614611442576001600160a01b03841660009081526008602052604090205461143b90611f08565b9050611445565b50805b826001600160601b031681602001516001600160601b0316101561147b5760405162461bcd60e51b81526004016106ea90615428565b6020810180516001600160601b03908590038116909152825184011682526001600160a01b0385811690851614610f5657610f3c81612d09565b6001600160a01b031660009081526008602052604090205460601c6001600160601b031690565b602082015160408301516114ee613f1b565b6001600160a01b03831660009081526008602052604090205461151090611f08565b905061151a613f1b565b6001600160a01b03831660009081526008602052604090205461153c90611f08565b90506115508660c00151836040015161259f565b61156d86602001516115628833612f7d565b8860c0015188612682565b611575613f3b565b611584858860a00151856130cc565b90506115a28760200151848960400151858b6060015160018761328c565b50505050505050565b6001600160a01b03918216600090815260096020908152604080832093909416825291909152205490565b336001600160a01b037f000000000000000000000000ac0122e9258a85ba5479db764dc8ef91cab08db0161461161e5760405162461bcd60e51b81526004016106ea90615603565b61073b827f000000000000000000000000ac0122e9258a85ba5479db764dc8ef91cab08db0836001600160601b0316611a58565b61165a611faf565b6004546001600160a01b039081169116146116875760405162461bcd60e51b81526004016106ea9061552c565b6001600160a01b0381166116ad5760405162461bcd60e51b81526004016106ea9061526c565b6004546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600480546001600160a01b0319166001600160a01b0392909216919091179055565b336001600160a01b037f000000000000000000000000ac0122e9258a85ba5479db764dc8ef91cab08db016146117515760405162461bcd60e51b81526004016106ea90615603565b806001600160601b0316826001600160601b031610156117835760405162461bcd60e51b81526004016106ea90615408565b61178b613f1b565b6001600160a01b0384166000908152600860205260409020546117ad90611f08565b9050826001600160601b031681602001516001600160601b031610156117e55760405162461bcd60e51b81526004016106ea90615428565b6020810180516001600160601b0390859003811690915282161561181357805182016001600160601b031681525b61181c81612d09565b6001600160a01b03909416600090815260086020526040902093909355505050565b611846613ef4565b61184f3361192e565b80519091508015611861575080602001515b1561187d57611877600133336000878787611d92565b5061073b565b610be633848460006133bb565b6001600160a01b038216158015906118aa57506001600160a01b03811615155b61073b5760405162461bcd60e51b81526004016106ea906154ec565b6001600160a01b0380841660008181526009602090815260408083209487168084529490915290819020849055517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259061192190859061577f565b60405180910390a3505050565b611936613ef4565b604051630f425aed60e11b81526001600160a01b037f000000000000000000000000a916bc21d2429645585abede4ae00742a16dd1c61690631e84b5da90611982908590600401614eaa565b60806040518083038186803b15801561199a57600080fd5b505afa1580156119ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106669190614c23565b60006119dc611ad0565b6119e857506000610704565b336001600160a01b037f000000000000000000000000e7cd2797ac6f08b5721c7e7fcc991251df5e38841614611a2057506001610704565b600182810151908181161415611a3a576001915050610704565b83602001518015611a49575083515b156106fe576000915050610704565b611a60613f1b565b6001600160a01b038416600090815260086020526040902054611a8290611f08565b9050611a8c613f1b565b6001600160a01b038416600090815260086020526040902054611aae90611f08565b9050611ab8613f3b565b611ac8868487858860008761328c565b505050505050565b6000336001600160a01b037f000000000000000000000000f3d6af45c6dfec43216cc3347ea91fefba0849d1161415611b0b57506001610645565b611b13613423565b905090565b678ac7230489e800006001600160601b031681602001516001600160601b03161115611b565760405162461bcd60e51b81526004016106ea906155a3565b6001600160a01b03821615801590611b7757506001600160a01b0382163314155b611b935760405162461bcd60e51b81526004016106ea906151ac565b805160ff16611c3d57611ba4613f1b565b6001600160a01b038316600090815260086020526040902054611bc690611f08565b905081602001516001600160601b031681600001516001600160601b03161015611c025760405162461bcd60e51b81526004016106ea90615448565b60208201518151036001600160601b03168152611c1e81612d09565b6001600160a01b0384166000908152600860205260409020555061073b565b805160ff1660011415611d7a57611c52613f1b565b6001600160a01b038316600090815260086020526040902054611c7490611f08565b905081602001516001600160601b031681602001516001600160601b03161015611cb05760405162461bcd60e51b81526004016106ea90615448565b602082810180519183018051929092036001600160601b03169091525160405163248de12b60e21b81527f000000000000000000000000ac0122e9258a85ba5479db764dc8ef91cab08db06001600160a01b03169163923784ac91611d1e918791429060009060040161503c565b602060405180830381600087803b158015611d3857600080fd5b505af1158015611d4c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d709190614d68565b50611c1e81612d09565b60405162461bcd60e51b81526004016106ea906155c4565b611d9a613f3b565b611da582878a612d50565b9050611daf613fea565b611dbd82898989898961350f565b905080600a6000611dd28a8660200151611ed5565b8152602080820192909252604090810160002083518154858501516001600160601b03908116600160a01b9081026001600160a01b039485166001600160a01b03199485161785161785559487015160018501805460608a0151909316909602908416919092161790911617909155608083015180519192611e5c9260028501929091019061401a565b505050602082015182516040517fa824f616acaccd5102c745c13e260dc1ca704e95425ebc56e73a6e495d5a3d8992611e96928b92614fed565b60405180910390a1505050505050505050565b60008184841115611ecd5760405162461bcd60e51b81526004016106ea9190615199565b505050900390565b60008282604051602001611eea929190614e2f565b60405160208183030381529060405280519060200120905092915050565b611f10613f1b565b611f18613f1b565b6001600160601b038381168252606084901c16602082015260c09290921c60408301525090565b6001600160a01b038216600090815260016020526040902080546001600160401b03838116600160401b9092041614610be65760405162461bcd60e51b81526004016106ea90615643565b6000828201838110156108445760405162461bcd60e51b81526004016106ea906152d4565b3390565b6001600160a01b038216611fd95760405162461bcd60e51b81526004016106ea906156a2565b80606081901c6001600160601b03811615611ffc576001600160601b0381166005555b612004613f1b565b6001600160a01b03851660009081526008602052604090205461202690611f08565b80516020820151919250808501916001600160601b0391821690830190911610156120635760405162461bcd60e51b81526004016106ea90615561565b6001600160601b038116825261207882612d09565b6001600160a01b038716600081815260086020526040808220939093559151909190600080516020615987833981519152906120b59088906157c3565b60405180910390a3505050505050565b60006120cf613f1b565b6120d9848461084b565b60208101519091506120e9613ef4565b6120f28661192e565b8051909150612105575091506106669050565b612113868660200151611f3f565b60405163c2288c7160e01b81527385ae45a05971170b70744292e2f051c0c49cf9099063c2288c719061214c908690859060040161572c565b60206040518083038186803b15801561216457600080fd5b505af4158015612178573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061219c9190614d4c565b9695505050505050565b600080600a60006121b78686611ed5565b8152602081019190915260400160002080549091506001600160a01b03166108445760405162461bcd60e51b81526004016106ea906154ca565b6001600160a01b0382166000908152600260209081526040808320600190925291829020915163c112c77160e01b81529091907385ae45a05971170b70744292e2f051c0c49cf9099063c112c77190612257908790879087908790600390600401614ed8565b60006040518083038186803b15801561226f57600080fd5b505af4158015612283573d6000803e3d6000fd5b5050505050505050565b600183015483546001600160a01b0390911690600160a01b90046001600160601b0316306122b9613f1b565b6001600160a01b0382166000908152600860205260409020546122db90611f08565b90506122e5613f1b565b6001600160a01b03851660009081526008602052604090205461230790611f08565b9050836001600160601b031682600001516001600160601b0316101561233f5760405162461bcd60e51b81526004016106ea90615561565b81516001600160601b039085900381168352815180860191908116908216101561237b5760405162461bcd60e51b81526004016106ea90615561565b6001600160601b038116825261239083612d09565b6001600160a01b0385166000908152600860205260409020556123b282612d09565b6001600160a01b0380881660008181526008602052604090819020939093559151908a1690600080516020615987833981519152906123f29089906157c3565b60405180910390a3505050505050505050565b8254600160a01b90046001600160601b031661241f613f1b565b6001600160a01b03841660009081526008602052604090205461244190611f08565b905061244b613f3b565b60006124748684868a60010160149054906101000a90046001600160601b031689876001613692565b90503061247f613f1b565b6001600160a01b0382166000908152600860205260409020546124a190611f08565b9050826001600160601b031681600001516001600160601b031610156124d95760405162461bcd60e51b81526004016106ea90615561565b80518390036001600160601b031681526124f281612d09565b6001600160a01b03831660009081526008602052604090205561251485612d09565b6001600160a01b0389166000908152600860205260409081902091909155517fe7b1342ce7f88416536f0a97fd9274421e3718dd094f96e9cefec28f6d7002c19061256590889060028d01906157d7565b60405180910390a160006001600160a01b0316886001600160a01b0316600080516020615987833981519152886040516123f291906157c3565b60018260400151036001600160401b0316816001600160401b03161461073b5760405162461bcd60e51b81526004016106ea90615661565b60006108447f2fb94db785f0d96efec3dca3f0b9717adb48f7a8d2a0434e8ea28001e36174a27f72079e1ca444dd2cbc0e28bb80962e9f4fbafb3705580b6b9f66186befba03956001866020015187604001518860600151805190602001206126438a6080015161388d565b6126518b60a001518b6138f5565b6040516020016126679796959493929190615111565b6040516020818303038152906040528051906020012061393c565b81516001600160a01b031633146126ab5760405162461bcd60e51b81526004016106ea906153c8565b6000807f000000000000000000000000a916bc21d2429645585abede4ae00742a16dd1c66001600160a01b0316630296287733886040518363ffffffff1660e01b81526004016126fc929190614ebe565b604080518083038186803b15801561271357600080fd5b505afa158015612727573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061274b91906149f1565b915091508161276c5760405162461bcd60e51b81526004016106ea90615388565b6020840151429082906001600160401b03610e108401811691811691821115916001918516106127b557826001600160401b0316886020015185036001600160401b0316111590505b8180156127bf5750805b6127db5760405162461bcd60e51b81526004016106ea906155e5565b8760600151156127f1576127ee89613951565b98505b600060018a89604001518a600001518b6020015160405160008152602001604052604051612822949392919061514d565b6020604051602081039080840390855afa158015612844573d6000803e3d6000fd5b505050602060405103519050886060015115801561287457508a6001600160a01b0316816001600160a01b031614155b156128d85760016128848b613951565b6040808b01518b516020808e0151845160008152909101938490526128a9949361514d565b6020604051602081039080840390855afa1580156128cb573d6000803e3d6000fd5b5050506020604051035190505b806001600160a01b03168b6001600160a01b0316146129095760405162461bcd60e51b81526004016106ea9061550e565b5050505050505050505050565b61291e613f3b565b612926613f3b565b60208401516001600160601b0316156129ea57678ac7230489e800006001600160601b031684602001516001600160601b031611156129775760405162461bcd60e51b81526004016106ea906155a3565b83602001516001600160601b031683600001516001600160601b031610156129b15760405162461bcd60e51b81526004016106ea90615448565b602084810180518551036001600160601b039081168652905116908201528060015b908160048111156129e057fe5b9052509050610844565b60408401516001600160601b031615612a9057678ac7230489e800006001600160601b031684604001516001600160601b03161115612a3b5760405162461bcd60e51b81526004016106ea906155a3565b83604001516001600160601b031683602001516001600160601b03161015612a755760405162461bcd60e51b81526004016106ea90615448565b60408401516001600160601b031660208201528060026129d3565b60608401516001600160601b031615612afc57678ac7230489e800006001600160601b031684606001516001600160601b03161115612ae15760405162461bcd60e51b81526004016106ea906155a3565b60608401516001600160601b031660208201528060046129d3565b83516001600160601b03161561108e576040805180820182526002815285516001600160601b0316602082015290516310b4a23d60e01b81526001600160a01b037f000000000000000000000000f3d6af45c6dfec43216cc3347ea91fefba0849d116916310b4a23d91612b74918991600401614f11565b600060405180830381600087803b158015612b8e57600080fd5b505af1158015612ba2573d6000803e3d6000fd5b505085516001600160601b031660208401525081905060036129d3565b6000612bd2878787600042876000613692565b90508215612bef576040860180516001016001600160401b031690525b6001600160601b03811615612c3c5785516001600160601b0380831691161015612c2b5760405162461bcd60e51b81526004016106ea90615582565b85518190036001600160601b031686525b612c4586612d09565b6001600160a01b03881660009081526008602052604081209190915582516001600160601b03871691906004811115612c7a57fe5b905060608160ff16901b82179150606384602001516001600160601b0316901b821791507fe7b1342ce7f88416536f0a97fd9274421e3718dd094f96e9cefec28f6d7002c18287604051612ccf929190615788565b60405180910390a160006001600160a01b0316896001600160a01b0316600080516020615987833981519152856040516123f291906157c3565b805160208201516040909201516001600160601b0390911660609290921b6bffffffffffffffffffffffff60601b169190911760c09190911b6001600160c01b0319161790565b612d58613f3b565b6000612d6384613964565b9050612d6d613f3b565b5060408051808201825260ff851681526001600160401b0383166020808301919091526001600160a01b038716600090815260029091529190912054601911612dc85760405162461bcd60e51b81526004016106ea9061534c565b6040868101516001600160a01b0387811660009081526002602090815284822080546001810182559083528183208751910180548884015160ff1990911660ff9384161768ffffffffffffffff0019166101006001600160401b0392831602179091558651606081018852918b168252421691810191909152918316938201939093529091600390612e5a8987611ed5565b815260208082019290925260409081016000208351815493850151949092015160ff1990931660ff9092169190911768ffffffffffffffff0019166101006001600160401b0390941693909302929092177fffffff0000000000000000000000000000000000000000ffffffffffffffffff16600160481b6001600160a01b03909216919091021790555095945050505050565b6001600160a01b038216600090815260016020526040902080546001600160401b03838116600160801b9092041614610be65760405162461bcd60e51b81526004016106ea90615489565b60008082601f81518110612f4957fe5b0160209081015160f81c91508110610666578281601f0160ff1681518110612f6d57fe5b016020015160f81c905092915050565b60006108447f2fb94db785f0d96efec3dca3f0b9717adb48f7a8d2a0434e8ea28001e36174a27f7d1602bfd7297f6720514f0c3dabe77313ce0d25cd5085644e3fcdc5635df6606000866020015187604001518860600151896080015180519060200120612fee8b60a0015161388d565b612ffc8c60c001518c6138f5565b6040516020016126679897969594939291906150cc565b61301b613f1b565b613025858561084b565b9050613038858583602001518686613a01565b610cf785856121f1565b600061304e84836121a6565b805460018201549192506001600160601b03600160a01b91829004811692919091041661307c308784611a58565b6130898686868585613adc565b7f74c334759f9bfb4a4342d46ed6f4dfb2a3c6b7fe1c7c2288988e8b44bc281b8a8685876040516130bc93929190614fed565b60405180910390a1505050505050565b6130d4613f3b565b6130dc613f3b565b60208401516001600160601b03161561312d57678ac7230489e800006001600160601b031684602001516001600160601b031611156129775760405162461bcd60e51b81526004016106ea906155a3565b60408401516001600160601b031615612afc57678ac7230489e800006001600160601b031684604001516001600160601b0316111561317e5760405162461bcd60e51b81526004016106ea906155a3565b604080850151905163248de12b60e21b81526001600160a01b037f000000000000000000000000ac0122e9258a85ba5479db764dc8ef91cab08db0169163923784ac916131d4918991429060009060040161503c565b602060405180830381600087803b1580156131ee57600080fd5b505af1158015613202573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132269190614d68565b5083604001516001600160601b031683602001516001600160601b031610156132615760405162461bcd60e51b81526004016106ea90615448565b604084018051602080860180516001600160601b0393900383169052915116908201528060026129d3565b846001600160a01b0316876001600160a01b031614156132be5760405162461bcd60e51b81526004016106ea906152b2565b81156132d9576040860180516001016001600160401b031690525b855183906001600160601b03808316911610156133085760405162461bcd60e51b81526004016106ea906151cd565b86516001600160601b03908290038116885285518083019190811690821610156133445760405162461bcd60e51b81526004016106ea906154a8565b6001600160601b038116865261335988612d09565b6001600160a01b038a1660009081526008602052604090205561337b86612d09565b6001600160a01b0380891660008181526008602052604090819020939093559151908b1690600080516020615987833981519152906123f29086906157c3565b6001600160a01b0384166133e15760405162461bcd60e51b81526004016106ea90615468565b6133e9613f1b565b6001600160a01b03851660009081526008602052604090205461340b90611f08565b9050613415613f3b565b611ac8868387878786612bbf565b6000336001600160a01b037f000000000000000000000000ac0122e9258a85ba5479db764dc8ef91cab08db016141561345e57506001610645565b336001600160a01b037f0000000000000000000000003b4da358199060bcc5a527ab60099fb6a908aae916141561349757506001610645565b336001600160a01b037f000000000000000000000000025dbd03ed18b4b8425af51b4d05f5b00e78208a1614156134d057506001610645565b336001600160a01b037f000000000000000000000000e7cd2797ac6f08b5721c7e7fcc991251df5e388416141561350957506001610645565b50600090565b613517613fea565b865160ff1660011461353857613531878787878787613b82565b905061219c565b600160601b831061354557fe5b828061354f613f1b565b6001600160a01b03881660009081526008602052604090205461357190611f08565b805190915083906001600160601b039081169082161115613590575080515b91829003916001600160601b038116156135b8576135b88930836001600160601b0316611a58565b6001600160601b0383161561364857604051637354578360e01b81526001600160a01b037f000000000000000000000000ac0122e9258a85ba5479db764dc8ef91cab08db01690637354578390613615908c90879060040161501a565b600060405180830381600087803b15801561362f57600080fd5b505af1158015613643573d6000803e3d6000fd5b505050505b50506040805160a0810182526001600160a01b03998a1681526001600160601b03938416602082015296909816978601979097529590951660608401526080830152509392505050565b60008060008084156136c957879150876001600160601b0316896001600160601b031610156136bd57fe5b509686900396876136d0565b5050875187905b6001600160601b0381161561371257886001600160601b0316816001600160601b031611156136fc5750875b61370581613be6565b9250846137125780820391505b60028651600481111561372157fe5b1415613731578560200151820191505b6001600160601b0382161561378957600061374f8c8c858b8a613bff565b90508381016001600160601b0380861690821610156137805760405162461bcd60e51b81526004016106ea90615682565b935061379a9050565b6001600160601b0388161561379a57fe5b6004865160048111156137a957fe5b14156137ef5785602001516001600160601b0316836001600160601b031610156137e55760405162461bcd60e51b81526004016106ea90615448565b8560200151830392505b6001600160601b0383161561387f57604051632d57fe2160e21b81526001600160a01b037f000000000000000000000000f3d6af45c6dfec43216cc3347ea91fefba0849d1169063b55ff8849061384c908e90879060040161501a565b600060405180830381600087803b15801561386657600080fd5b505af115801561387a573d6000803e3d6000fd5b505050505b9a9950505050505050505050565b60007ff1f7d09b7527273cfd8370620354f0cd9a62e7bd3efaafdeca15664a7b3e045c82600001518360200151846040015185606001516040516020016138d895949392919061516b565b604051602081830303815290604052805190602001209050919050565b60007ff45b7e63030d0a046d85957a672f9a0806d76fff4cc32355d75f64eb6e83d00882846020015185604001518660600151604051602001611eea95949392919061507e565b60008282604051602001611eea929190614e8f565b6000816040516020016138d89190614e5e565b6001600160a01b03811660009081526001602081905260408220805490916001600160401b0380831690910191600160401b9004166139c65781546fffffffffffffffff00000000000000001916600160401b6001600160401b038316021782555b815467ffffffffffffffff60801b1916600160801b6001600160401b0383169081029190911767ffffffffffffffff19161790915592915050565b613a0f858560200151612eee565b613a17613ef4565b613a208661192e565b80519091508015613a46575080604001516001600160a01b0316336001600160a01b0316145b613a625760405162461bcd60e51b81526004016106ea906156c3565b6060613a6c613cfe565b60405163e17e413160e01b81529091507385ae45a05971170b70744292e2f051c0c49cf9099063e17e413190613ab0908a90899087908a9088908b90600401614f43565b60006040518083038186803b158015613ac857600080fd5b505af4158015612909573d6000803e3d6000fd5b60ff8416600114613aec57610cf7565b6001600160601b03811615610cf7576040516343ad8a9160e01b81526001600160a01b037f000000000000000000000000ac0122e9258a85ba5479db764dc8ef91cab08db016906343ad8a9190613b49908890859060040161501a565b600060405180830381600087803b158015613b6357600080fd5b505af1158015613b77573d6000803e3d6000fd5b505050505050505050565b613b8a613fea565b613b95853085611a58565b613b9d613fea565b50506040805160a0810182526001600160a01b0380881682526001600160601b038516602083015285169181019190915260006060820152608081018290529695505050505050565b60006106666001600160601b0383166301e13380613e87565b6000807f000000000000000000000000ac0122e9258a85ba5479db764dc8ef91cab08db06001600160a01b031663923784ac888787876040518563ffffffff1660e01b8152600401613c54949392919061503c565b602060405180830381600087803b158015613c6e57600080fd5b505af1158015613c82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ca69190614d68565b9050846001600160601b031686602001516001600160601b03161015613cde5760405162461bcd60e51b81526004016106ea90615210565b6020860180516001600160601b0390879003169052905095945050505050565b60408051600580825260c082019092526060918291906020820160a0803683370190505090503081600081518110613d3257fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507f000000000000000000000000f3d6af45c6dfec43216cc3347ea91fefba0849d181600181518110613d8057fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507f000000000000000000000000ac0122e9258a85ba5479db764dc8ef91cab08db081600281518110613dce57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507f0000000000000000000000003b4da358199060bcc5a527ab60099fb6a908aae981600381518110613e1c57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507f000000000000000000000000025dbd03ed18b4b8425af51b4d05f5b00e78208a81600481518110613e6a57fe5b6001600160a01b0390921660209283029190910190910152905090565b600080613ea063ffffffff84166301e133806012613ecb565b6004029050600068056bc75e2d6310000082860281613ebb57fe5b049050600160601b811061108e57fe5b6000600a8383600101600a0a860281613ee057fe5b0460050181613eeb57fe5b04949350505050565b60408051608081018252600080825260208201819052918101829052606081019190915290565b604080516060810182526000808252602082018190529181019190915290565b604080518082019091526000808252602082015290565b6040518060e00160405280600060ff16815260200160006001600160a01b0316815260200160006001600160a01b031681526020016000815260200160608152602001613f9d613ef4565b8152602001613faa613ef4565b905290565b6040518060c00160405280600060ff16815260200160006001600160a01b031681526020016000815260200160608152602001613f9d613ef4565b6040805160a081018252600080825260208201819052918101829052606080820192909252608081019190915290565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061405b57805160ff1916838001178555614088565b82800160010185558215614088579182015b8281111561408857825182559160200191906001019061406d565b50614094929150614098565b5090565b5b808211156140945760008155600101614099565b803561066681615915565b805161066681615915565b600082601f8301126140d3578081fd5b81356140e66140e18261589b565b615875565b818152915060208083019084810160608085028701830188101561410957600080fd5b60005b858110156141305761411e89846144e9565b8552938301939181019160010161410c565b50505050505092915050565b600082601f83011261414c578081fd5b813561415a6140e1826158ba565b915080825283602082850101111561417157600080fd5b8060208401602084013760009082016020015292915050565b600082601f83011261419a578081fd5b81516141a86140e1826158ba565b91508082528360208285010111156141bf57600080fd5b6141d08160208401602086016158e9565b5092915050565b600061018082840312156141e9578081fd5b6141f360c0615875565b9050813561420081615962565b8152602082013561421081615915565b60208201526040828101359082015260608201356001600160401b0381111561423857600080fd5b6142448482850161413c565b6060830152506142578360808401614323565b608082015261426a8361010084016143ea565b60a082015292915050565b60006101a08284031215614287578081fd5b61429160e0615875565b905061429d838361452d565b81526142ac83602084016140ad565b60208201526142be83604084016140ad565b60408201526060820135606082015260808201356001600160401b038111156142e657600080fd5b6142f28482850161413c565b6080830152506143058360a08401614323565b60a08201526143188361012084016143ea565b60c082015292915050565b600060808284031215614334578081fd5b61433e6080615875565b9050813561434b81615971565b8152602082013561435b81615971565b6020820152604082013561436e81615971565b6040820152606082013561438181615971565b606082015292915050565b60006080828403121561439d578081fd5b6143a76080615875565b905081516143b481615971565b815260208201516143c481615971565b602082015260408201516143d781615971565b6040820152606082015161438181615971565b6000608082840312156143fb578081fd5b6144056080615875565b9050813561441281615915565b815260208201356144228161594d565b602082015260408201356144358161594d565b604082015260608201356143818161592d565b600060808284031215614459578081fd5b6144636080615875565b9050815161447081615915565b815260208201516144808161594d565b602082015260408201516144938161594d565b604082015260608201516143818161592d565b6000604082840312156144b7578081fd5b6144c16040615875565b905081356144ce81615962565b815260208201356144de8161594d565b602082015292915050565b6000606082840312156144fa578081fd5b6145046060615875565b90508135815260208201356020820152604082013561452281615962565b604082015292915050565b803561066681615962565b805161066681615962565b600060208284031215614554578081fd5b813561084481615915565b60008060408385031215614571578081fd5b823561457c81615915565b9150602083013561458c81615915565b809150509250929050565b6000806000606084860312156145ab578081fd5b83356145b681615915565b925060208401356145c681615915565b929592945050506040919091013590565b6000806000806000608086880312156145ee578283fd5b85356145f981615915565b9450602086013561460981615915565b93506040860135925060608601356001600160401b038082111561462b578283fd5b818801915088601f83011261463e578283fd5b81358181111561464c578384fd5b89602082850101111561465d578384fd5b9699959850939650602001949392505050565b600080600060608486031215614684578081fd5b833561468f81615915565b9250602084013561469f81615915565b915060408401356146af81615971565b809150509250925092565b600080604083850312156146cc578182fd5b82356146d781615915565b915060208301356001600160401b038111156146f1578182fd5b6146fd8582860161413c565b9150509250929050565b60008060608385031215614719578182fd5b823561472481615915565b915061473384602085016144a6565b90509250929050565b60008060008060e08587031215614751578182fd5b843561475c81615915565b935061476b86602087016144a6565b925060608501356001600160401b03811115614785578283fd5b6147918782880161413c565b9250506147a186608087016144e9565b905092959194509250565b60008082840360608112156147bf578283fd5b83356147ca81615915565b92506040601f19820112156147dd578182fd5b506147e86040615875565b60208401356147f681615962565b8152604084013561480681615971565b6020820152919491935090915050565b60008060408385031215614828578182fd5b823561483381615915565b946020939093013593505050565b60008060408385031215614853578182fd5b823561485e81615915565b9150602083013561458c8161594d565b60008060408385031215614880578182fd5b823561488b81615915565b9150602083013561458c81615971565b6000806000606084860312156148af578081fd5b83356148ba81615915565b9250602084013561469f81615971565b600080604083850312156148dc578182fd5b82356001600160401b03808211156148f2578384fd5b818501915085601f830112614905578384fd5b81356149136140e18261589b565b81815260208082019190858101885b8581101561494b576149398c8484358b01016141d7565b85529382019390820190600101614922565b50919750880135945050505080821115614963578283fd5b506146fd858286016140c3565b60008060408385031215614982578182fd5b82356001600160401b0380821115614998578384fd5b818501915085601f8301126149ab578384fd5b81356149b96140e18261589b565b81815260208082019190858101885b8581101561494b576149df8c8484358b0101614275565b855293820193908201906001016149c8565b60008060408385031215614a03578182fd5b8251614a0e8161592d565b6020939093015192949293505050565b600060208284031215614a2f578081fd5b81516001600160401b0380821115614a45578283fd5b908301906101808286031215614a59578283fd5b614a6360c0615875565b8251614a6e81615962565b8152614a7d86602085016140b8565b602082015260408301516040820152606083015182811115614a9d578485fd5b614aa98782860161418a565b606083015250614abc866080850161438c565b6080820152614acf866101008501614448565b60a082015295945050505050565b60008060808385031215614aef578182fd5b82356001600160401b03811115614b04578283fd5b614b10858286016141d7565b92505061473384602085016144e9565b600060208284031215614b31578081fd5b81516001600160401b0380821115614b47578283fd5b908301906101a08286031215614b5b578283fd5b614b6560e0615875565b614b6f8684614538565b8152614b7e86602085016140b8565b6020820152614b9086604085016140b8565b604082015260608301516060820152608083015182811115614bb0578485fd5b614bbc8782860161418a565b608083015250614bcf8660a0850161438c565b60a0820152614be2866101208501614448565b60c082015295945050505050565b60008060808385031215614c02578182fd5b82356001600160401b03811115614c17578283fd5b614b1085828601614275565b600060808284031215614c34578081fd5b614c3e6080615875565b8251614c498161592d565b81526020830151614c598161592d565b60208201526040830151614c6c81615915565b60408201526060830151614c7f8161593b565b60608201529392505050565b600080600083850360c0811215614ca0578182fd5b6080811215614cad578182fd5b50614cb86080615875565b8435614cc38161592d565b81526020850135614cd38161592d565b60208201526040850135614ce681615915565b60408201526060850135614cf98161593b565b606082015292506080840135614d0e81615915565b915060a08401356146af81615962565b60008060408385031215614d30578182fd5b8235915060208301356001600160401b038111156146f1578182fd5b600060208284031215614d5d578081fd5b81516108448161594d565b600060208284031215614d79578081fd5b815161084481615971565b60008151808452614d9c8160208601602086016158e9565b601f01601f19169290920160200192915050565b805160ff1682526020908101516001600160401b0316910152565b805160ff1682526020808201516001600160401b0316908301526040908101516001600160a01b0316910152565b8051151582526020808201511515908301526040808201516001600160a01b03169083015260609081015163ffffffff16910152565b60609290921b6bffffffffffffffffffffffff1916825260c01b6001600160c01b0319166014820152601c0190565b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000008152601c810191909152603c0190565b61190160f01b81526002810192909252602282015260420190565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b038616815260c08101614ef56020830187614db0565b8460608301528360808301528260a08301529695505050505050565b6001600160a01b03929092168252805160ff1660208084019190915201516001600160601b0316604082015260600190565b600061016060018060a01b03808a16845260206001600160401b038a1681860152614f71604086018a614df9565b8260c0860152614f8383860189614d84565b85810360e08701528751808252828901945090820190855b81811015614fb9578551851683529483019491830191600101614f9b565b50508094505085516101008601528086015161012086015250505060ff604084015116610140830152979650505050505050565b6001600160a01b039390931683526001600160401b0391909116602083015260ff16604082015260600190565b6001600160a01b039290921682526001600160601b0316602082015260400190565b6001600160a01b039490941684526001600160601b0392909216602084015263ffffffff1660408301521515606082015260800190565b901515815260200190565b9485526001600160a01b039390931660208501526001600160401b0391821660408501521660608301521515608082015260a00190565b9182526001600160401b0316602082015260400190565b97885260ff9690961660208801526001600160a01b039485166040880152929093166060860152608085015260a084019190915260c083015260e08201526101000190565b96875260ff9590951660208701526001600160a01b039390931660408601526060850191909152608084015260a083015260c082015260e00190565b93845260ff9290921660208401526040830152606082015260800190565b9485526001600160601b03938416602086015291831660408501528216606084015216608082015260a00190565b6000602082526108446020830184614d84565b602080825260079082015266505250532d313160c81b604082015260600190565b602080825260089082015267045524332302d31360c41b604082015260600190565b60208082526007908201526622a9219918169b60c91b604082015260600190565b6020808252600690820152650a0a4a0a65a760d31b604082015260600190565b60208082526004908201526350422d3160e01b604082015260600190565b6020808252600490820152632821169960e11b604082015260600190565b60208082526026908201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160408201526564647265737360d01b606082015260800190565b60208082526008908201526745524332302d313960c01b604082015260600190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b602080825260069082015265505250532d3360d01b604082015260600190565b60208082526007908201526645524332302d3760c81b604082015260600190565b60208082526004908201526350422d3360e01b604082015260600190565b60208082526004908201526350422d3760e01b604082015260600190565b60208082526004908201526341422d3360e01b604082015260600190565b60208082526008908201526745524332302d313760c01b604082015260600190565b60208082526004908201526320a1169960e11b604082015260600190565b60208082526008908201526745524332302d313560c01b604082015260600190565b60208082526006908201526514149414cb4d60d21b604082015260600190565b602080825260069082015265505250532d3560d01b604082015260600190565b602080825260069082015265505250532d3760d01b604082015260600190565b60208082526007908201526608aa48664605a760cb1b604082015260600190565b602080825260059082015264050422d31360dc1b604082015260600190565b60208082526008908201526722a921991816989960c11b604082015260600190565b60208082526008908201526722a921991816989b60c11b604082015260600190565b60208082526008908201526745524332302d313360c01b604082015260600190565b60208082526004908201526341422d3560e01b604082015260600190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60208082526007908201526622a9219918169960c91b604082015260600190565b60208082526007908201526645524332302d3960c81b604082015260600190565b6020808252600790820152660505250532d31360cc1b604082015260600190565b6020808252600790820152662829282996989960c91b604082015260600190565b60208082526004908201526310508b4d60e21b604082015260600190565b602080825260069082015265505250532d3160d01b604082015260600190565b60208082526006908201526528292829969960d11b604082015260600190565b60208082526004908201526350422d3960e01b604082015260600190565b60208082526007908201526645524332302d3560c81b604082015260600190565b60208082526006908201526528292829969b60d11b604082015260600190565b60208082526007908201526645524332302d3160c81b604082015260600190565b6020808252600490820152632821169b60e11b604082015260600190565b81516001600160401b039081168252602080840151821690830152604092830151169181019190915260600190565b604081016106668284614db0565b606081016106668284614dcb565b60e0810161573a8285614dcb565b6108446060830184614df9565b81516001600160601b039081168252602080840151909116908201526040918201516001600160401b03169181019190915260600190565b90815260200190565b60008382526040602083015261108e6040830184614d84565b6001600160401b0391909116815260200190565b60ff91909116815260200190565b6001600160601b0391909116815260200190565b6000604082016001600160601b0385168352602060408185015282855460018082166000811461580e576001811461582c57615867565b60028304607f16865260ff1983166060890152608088019350615867565b6002830461583a818861577f565b6158438b6158dd565b895b8381101561585e57815483820152908501908801615845565b91909101955050505b509198975050505050505050565b6040518181016001600160401b038111828210171561589357600080fd5b604052919050565b60006001600160401b038211156158b0578081fd5b5060209081020190565b60006001600160401b038211156158cf578081fd5b50601f01601f191660200190565b60009081526020902090565b60005b838110156159045781810151838201526020016158ec565b8381111561137e5750506000910152565b6001600160a01b038116811461592a57600080fd5b50565b801515811461592a57600080fd5b63ffffffff8116811461592a57600080fd5b6001600160401b038116811461592a57600080fd5b60ff8116811461592a57600080fd5b6001600160601b038116811461592a57600080fdfeddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa26469706673582212203a93a0f3b850e0334975edea46270309eb57017a2ad224cf372a4178bbda627364736f6c634300060c0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a916bc21d2429645585abede4ae00742a16dd1c6000000000000000000000000f3d6af45c6dfec43216cc3347ea91fefba0849d1000000000000000000000000ac0122e9258a85ba5479db764dc8ef91cab08db00000000000000000000000003b4da358199060bcc5a527ab60099fb6a908aae9000000000000000000000000025dbd03ed18b4b8425af51b4d05f5b00e78208a000000000000000000000000e7cd2797ac6f08b5721c7e7fcc991251df5e3884
-----Decoded View---------------
Arg [0] : initialSupply (uint256): 0
Arg [1] : optIn (address): 0xA916Bc21D2429645585aBeDe4aE00742A16DD1C6
Arg [2] : dubi (address): 0xF3D6Af45C6dFeC43216CC3347Ea91fEfBa0849D1
Arg [3] : hodl (address): 0xaC0122e9258a85bA5479DB764DC8eF91caB08db0
Arg [4] : externalAddress1 (address): 0x3b4DA358199060BCc5A527Ab60099Fb6a908AaE9
Arg [5] : externalAddress2 (address): 0x025Dbd03ED18B4b8425af51b4d05F5b00E78208A
Arg [6] : externalAddress3 (address): 0xe7cD2797aC6F08b5721C7e7FCc991251Df5e3884
-----Encoded View---------------
7 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [1] : 000000000000000000000000a916bc21d2429645585abede4ae00742a16dd1c6
Arg [2] : 000000000000000000000000f3d6af45c6dfec43216cc3347ea91fefba0849d1
Arg [3] : 000000000000000000000000ac0122e9258a85ba5479db764dc8ef91cab08db0
Arg [4] : 0000000000000000000000003b4da358199060bcc5a527ab60099fb6a908aae9
Arg [5] : 000000000000000000000000025dbd03ed18b4b8425af51b4d05f5b00e78208a
Arg [6] : 000000000000000000000000e7cd2797ac6f08b5721c7e7fcc991251df5e3884
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.