Transactions
Token Transfers
Tokens
Internal Transactions
Coin Balance History
Logs
Code
Read Contract
Write Contract
- Contract name:
- SlashToken
- Optimization enabled
- true
- Compiler version
- v0.8.19+commit.7dd6d404
- Optimization runs
- 9999999
- EVM Version
- default
- Verified at
- 2023-11-23T11:30:29.355772Z
contracts/handshake-drop/handshake-drop-core/SlashToken.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.19;
import "../handshake-drop-libs/TokenTransferer.sol";
import "../handshake-drop-types/interfaces/SlashTokenEvents.sol";
import "../handshake-drop-libs/NativeTransferer.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
//import "hardhat/console.sol";
import "../handshake-drop-libs/SubscriptionRegistry.sol";
/// @author 0xCocomastoras
/// @custom:version 1.0
/// @title SlashToken
/// @notice SlashToken is a simple, yet powerful tool to airdrop tokens and NFTs.
contract SlashToken is TokenTransferer, NativeTransferer, SlashTokenEvents, SubscriptionRegistry, ReentrancyGuard {
constructor() ReentrancyGuard(){}
/**
@notice ERC-20 token airdrop with same, equal amount to all recipients
@param recipients A list of addresses of the recipients
@param amount Amount of tokens each recipient will be airdropped
@param token Address of the token
*/
function erc20AirdropEqualAmount(address[] calldata recipients, uint256 amount, address token) external payable nonReentrant {
require(frozen == 0, "CF");
require(denylist[msg.sender] == 0, 'UD');
uint256 value = _getBaseFeeForWallet(); //Check if wallet is whitelisted with different value
uint256 purchasedTxns = _getAvailableTxnsForWallet();
uint recipientsLength = recipients.length;
value = value == 0 ? baseFee : value;
if (purchasedTxns == 0) {
require(msg.value == value, "NVV");
} else {
_updateAvailableTxnsForWallet();
}
require(isInitialized != 0, 'NIY');
require(recipientsLength <= 500, 'NVL');
uint recipientsOffset;
assembly {
recipientsOffset := recipients.offset
}
_performMultiERC20Transfer(token, recipientsOffset, recipientsLength, amount);
emit Erc20AirdropEqualAmount(msg.sender, token, recipientsLength, recipientsLength*amount);
}
/**
@notice ERC-20 token airdrop with custom amount for each recipient
@param recipients A list of addresses of the recipients
@param amount A list of amounts of the tokens each recipient will be airdropped
@param token Address of the token
@param totalAmount The sum of all tokens to be airdropped
*/
function erc20AirdropCustomAmount(address[] calldata recipients, uint256[] calldata amount, address token, uint256 totalAmount) external payable nonReentrant {
require(frozen == 0, "CF");
require(denylist[msg.sender] == 0, 'UD');
uint256 value = _getBaseFeeForWallet(); //Check if wallet is whitelisted with different value
uint256 purchasedTxns = _getAvailableTxnsForWallet();
uint recipientsLength = recipients.length;
value = value == 0 ? baseFee : value;
if (purchasedTxns == 0) {
require(msg.value == value, "NVV");
} else {
_updateAvailableTxnsForWallet();
}
require(isInitialized != 0, 'NIY');
require(recipientsLength <= 500 && recipientsLength == amount.length, 'NVL');
uint recipientsOffset;
uint amountsOffset;
assembly {
recipientsOffset := recipients.offset
amountsOffset := amount.offset
}
_performMultiERC20TransferCustom(token, recipientsOffset, recipientsLength, amountsOffset, totalAmount);
emit Erc20AirdropCustomAmount(msg.sender, token, recipientsLength, totalAmount);
}
/**
@notice Native currency airdrop with same, equal amount to all recipients
@param recipients A list of addresses of the recipients
@param amount Amount of tokens each recipient will be airdropped
*/
function nativeAirdropEqualAmount(address[] calldata recipients, uint256 amount) external payable nonReentrant {
require(frozen == 0, "CF");
require(isInitialized != 0, 'NIY');
require(denylist[msg.sender] == 0, 'UD');
uint recipientsOffset;
uint recipientsLength = recipients.length;
uint256 value = _getBaseFeeForWallet(); //Check if wallet is whitelisted with different value
uint256 purchasedTxns = _getAvailableTxnsForWallet();
uint256 recipientsValue = amount * recipientsLength;
value = value == 0 ? (baseFee + recipientsValue) : (value + recipientsValue);
if (purchasedTxns == 0) {
require(msg.value == value, "NVV");
} else {
require(msg.value == recipientsValue, 'NVV');
_updateAvailableTxnsForWallet();
}
require(recipientsLength <= 500, 'NVL');
assembly {
recipientsOffset := recipients.offset
}
_performMultiNativeTransfer(recipientsOffset, recipientsLength, amount);
emit NativeAirdropEqualAmount(msg.sender, recipientsLength, recipientsValue);
}
/**
@notice Native currency airdrop with custom amount for each recipient
@param recipients A list of addresses of the recipients
@param amounts A list of amounts that each recipient will be airdropped
*/
function nativeAirdropCustomAmount(address[] calldata recipients, uint256[] calldata amounts) external payable nonReentrant {
require(frozen == 0, "CF");
require(isInitialized != 0, 'NIY');
require(denylist[msg.sender] == 0, 'UD');
uint256 value = _getBaseFeeForWallet(); //Check if wallet is whitelisted with different value
uint256 purchasedTxns = _getAvailableTxnsForWallet();
uint recipientsOffset;
uint amountsOffset;
uint recipientsLength = recipients.length;
require(recipientsLength <= 500 && recipientsLength == amounts.length, 'NVL');
assembly {
recipientsOffset := recipients.offset
amountsOffset := amounts.offset
}
uint totalAmount = _performMultiNativeTransferCustom(recipientsOffset, recipientsLength, amountsOffset);
value = value == 0 ? (baseFee + totalAmount) : (value + totalAmount);
if (purchasedTxns == 0) {
require(msg.value == value, "NVV");
} else {
require(msg.value == totalAmount, 'NVV');
_updateAvailableTxnsForWallet();
}
emit NativeAirdropCustomAmount(msg.sender, recipientsLength, totalAmount);
}
/**
@notice Basic Airdrop of Erc721 tokens without bundle
@param recipients A list of addresses of the recipients
@param ids A list of ids of the token each recipient will be airdropped
@param token The address of the token
*/
function erc721Airdrop(address[] calldata recipients, uint256[] calldata ids, address token) external payable nonReentrant {
require(permitErc721 != 0, 'NEY');
require(frozen == 0, "CF");
require(isInitialized != 0, 'NIY');
require(denylist[msg.sender] == 0, 'UD');
uint256 value = _getBaseFeeForWallet();
value = value == 0 ? baseFee : value;
uint256 purchasedTxns = _getAvailableTxnsForWallet();
uint recipientsLength = recipients.length;
if (purchasedTxns == 0) {
require(msg.value == value, "NVV");
} else {
_updateAvailableTxnsForWallet();
}
require(recipientsLength <= 500 && recipientsLength == ids.length, 'NVL');
uint recipientsOffset;
uint idsOffset;
assembly {
recipientsOffset := recipients.offset
idsOffset := ids.offset
}
_performMultiERC721Transfer(token, msg.sender, recipientsOffset, recipientsLength, idsOffset);
emit Erc721Airdrop(msg.sender, token, recipientsLength);
}
}
contracts/handshake-drop/handshake-drop-libs/NativeTransferer.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
contract NativeTransferer {
uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;
/**
* @dev Internal function to transfer native tokens from a given originator
* to a multiple recipients
*
* @param offset Calldata offset of the recipients of the transfer.
* @param length Calldata length of the recipients of the transfer.
* @param amount The amount to transfer.
*/
function _performMultiNativeTransfer(uint256 offset, uint256 length, uint256 amount) internal {
assembly {
for { let i := 0 } lt(i, length) { i := add(i, 1) } {
let to := calldataload(add(offset, mul(i, 0x20)))
if iszero( call(
GAS_STIPEND_NO_STORAGE_WRITES,
to,
amount,
0,
0,
0,
0
)) {
revert(0,0)
}
}
}
}
/**
* @dev Internal function to transfer native tokens from a given originator
* to a multiple recipients
*
* @param recipientsOffset Calldata offset of the recipients of the transfer.
* @param length Calldata length of the recipients of the transfer.
* @param amountsOffset Calldata offset of the amounts to transfer
*/
function _performMultiNativeTransferCustom(uint256 recipientsOffset, uint256 length, uint256 amountsOffset) internal returns (uint256 totalAmount){
assembly {
for { let i := 0 } lt(i, length) { i := add(i, 1) } {
let to := calldataload(add(recipientsOffset, mul(i, 0x20)))
let amount := calldataload(add(amountsOffset, mul(i, 0x20)))
totalAmount := add(totalAmount, amount)
if iszero( call(
GAS_STIPEND_NO_STORAGE_WRITES,
to,
amount,
0,
0,
0,
0
)) {
revert(0,0)
}
}
}
}
}
@openzeppelin/contracts/security/ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
}
@openzeppelin/contracts/utils/structs/EnumerableSet.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastValue;
// Update the index for the moved value
set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}
contracts/handshake-drop/handshake-drop-libs/ManagerActions.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
contract ManagerActions{
constructor(){}
address owner;
address feeSink;
uint256 public frozen;
uint256 public permitErc721 = 0;
mapping(address => uint256) denylist;
using EnumerableSet for EnumerableSet.AddressSet;
EnumerableSet.AddressSet deniedAddresses;
/**
@notice Admin freezes / unfreezes contracts
@param value_ 0 = unfreeze, any other value = freeze
*/
function freezeContract(uint256 value_) external {
require(msg.sender == owner, 'NVS');
frozen = value_;
}
/**
@notice Admin permits/freezes erv721Airdrops
@param value_ 0 = freeze, any other value = permit
*/
function handleErc721Flag(uint256 value_) external {
require(msg.sender == owner, 'NVS');
permitErc721 = value_;
}
/**
@notice Admin updates fee sink address
@param feeSink_ The new fee sink address
*/
function updateFeeSink(address feeSink_) external {
require(msg.sender == owner, 'NVS');
feeSink = feeSink_;
}
/**
@notice Admin claims contract fees
*/
function claimFees() external {
address owner_ = owner;
assembly {
if iszero(eq(caller(), owner_)) {
revert(0,0)
}
if iszero(call(gas(), sload(feeSink.slot), selfbalance(), 0, 0, 0, 0)) {
revert(0,0)
}
}
}
function addToDenylist(address[] memory list) external {
require(msg.sender == owner, 'NVS');
uint len = list.length;
for(uint i = 0; i < len;) {
if (!deniedAddresses.contains(list[i])) {
deniedAddresses.add(list[i]);
}
unchecked {
denylist[list[i]] = 1;
i++;
}
}
}
function removeFromDenylist(address[] memory list) external {
require(msg.sender == owner, 'NVS');
uint len = list.length;
for(uint i = 0; i < len;) {
if (deniedAddresses.contains(list[i])) {
deniedAddresses.remove(list[i]);
}
unchecked {
denylist[list[i]] = 0;
i++;
}
}
}
function getDenylist() external view returns (address[] memory) {
require(msg.sender == owner, 'NVS');
return deniedAddresses.values();
}
}
contracts/handshake-drop/handshake-drop-libs/SubscriptionRegistry.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "../handshake-drop-types/interfaces/SlashTokenRegistryEvents.sol";
import "../handshake-drop-types/interfaces/PremiumSubscriptionRegistryEvents.sol";
import "./ManagerActions.sol";
contract SubscriptionRegistry is SlashTokenRegistryEvents, PremiumSubscriptionRegistryEvents, ManagerActions {
// @dev user's adddress to custom base fee for no bundle purchases
mapping (address => uint256) baseFeeWhitelisted;
// @dev user address to total available txns to use
mapping(address => uint256) public userToTxns;
uint256[] availableTxnsBundles;
uint256[] txnsBundlesToPrice;
uint256 isInitialized;
uint256 public baseFee;
using EnumerableSet for EnumerableSet.AddressSet;
EnumerableSet.AddressSet bundleUsers;
function initialize(address admin_, address feeSink_, uint256 baseFeeCostInWei, uint256[] memory availableTxnsBundles_, uint256[] memory txnsBundlesToPrice_ ) external {
require(isInitialized == 0, 'AI');
require(availableTxnsBundles_.length == txnsBundlesToPrice_.length, 'NVD');
owner = admin_;
feeSink = feeSink_;
availableTxnsBundles = availableTxnsBundles_;
txnsBundlesToPrice = txnsBundlesToPrice_;
isInitialized = 1;
baseFee = baseFeeCostInWei;
}
constructor(){}
/**
@notice User buys a bundle of txns
@param bundleIndex The index of the bundle array
@param quantity The number of bundles that the user wants to buy
*/
function buyTxnsBundle(uint256 bundleIndex, uint256 quantity) external payable {
require(frozen == 0, "CF");
require(msg.value == txnsBundlesToPrice[bundleIndex] * quantity, 'NVV');
require(denylist[msg.sender] == 0, 'UD');
require(quantity != 0, 'NVA');
if(!bundleUsers.contains(msg.sender)) {
bundleUsers.add(msg.sender);
}
uint256 total;
unchecked {
total = availableTxnsBundles[bundleIndex] * quantity;
userToTxns[msg.sender] += total;
}
emit TxnsBundleBought(msg.sender, msg.value, total);
}
/**
@notice Admin sets promo fee for wallet instead of default base fee
@param wallet User's wallet address
@param amountInWei New cost per txns
*/
function setBaseFeeForWallet(address wallet, uint256 amountInWei) external {
require(amountInWei < baseFee, "NVV");
require(msg.sender == owner, 'NVS');
baseFeeWhitelisted[wallet] = amountInWei;
emit WalletBaseFeeSet(wallet, amountInWei);
}
/**
@notice Admin resets promo fee for wallet. Default base fee applies
@param wallet User's wallet address
*/
function resetBaseFeeForWallet(address wallet) external {
require(msg.sender == owner, 'NVS');
delete baseFeeWhitelisted[wallet];
emit WalletBaseFeeReset(wallet);
}
/**
@notice Admin adds txns to a user
@param wallets A list of user's wallet address
@param txns A list of txns to be added
*/
function addTxnsToWallets(address[] memory wallets, uint256[] memory txns) external {
require(msg.sender == owner, 'NVS');
require(wallets.length == txns.length, "NVL");
uint len = wallets.length;
for(uint i =0; i<len;){
unchecked {
userToTxns[wallets[i]] += txns[i];
i++;
}
}
emit TxnsAdded(wallets, txns);
}
/**
@notice Admin sets the new base fee for all txns
@param baseFee_ New base fee
*/
function setNewBaseFee(uint256 baseFee_) external {
require(msg.sender == owner, 'NVS');
baseFee = baseFee_;
}
/**
@notice Admin sets the new available txnsBundles and prices
@param availableTxnsBundles_ New available bundles
@param txnsBundlesToPrice_ New price per bundles
*/
function updateBundles(uint256[] memory availableTxnsBundles_, uint256[] memory txnsBundlesToPrice_ ) external {
require(msg.sender == owner, 'NVS');
require(availableTxnsBundles_.length == txnsBundlesToPrice_.length, 'NVD');
availableTxnsBundles = availableTxnsBundles_;
txnsBundlesToPrice = txnsBundlesToPrice_;
emit BundlesUpdated(msg.sender, availableTxnsBundles_, txnsBundlesToPrice_);
}
/**
@notice External view function that returns active bundle offers
*/
function getBundles() external view returns (uint256[] memory AvailableBundles, uint256[] memory BundlesPrices) {
AvailableBundles = availableTxnsBundles;
BundlesPrices = txnsBundlesToPrice;
}
/**
@notice External view function that returns the custom base fee for wallet
*/
function getBaseFeeForWallet() external view returns (uint256) {
return baseFeeWhitelisted[msg.sender];
}
/**
@notice External view function that returns the available txns for wallet
*/
function getAvailableTxnsForWallet() external view returns (uint256) {
return userToTxns[msg.sender];
}
/**
@notice External view function that returns all the users that have bought a bundle
*/
function getUsersThatBoughtBundles() external view returns (address[] memory Users) {
require(msg.sender == owner, 'NVS');
Users = bundleUsers.values();
}
/**
@notice Internal view function that returns the custom base fee for wallet
*/
function _getBaseFeeForWallet() internal view returns (uint256) {
return baseFeeWhitelisted[msg.sender];
}
/**
@notice Internal view function that returns the available txns for wallet
*/
function _updateAvailableTxnsForWallet() internal {
unchecked {
--userToTxns[msg.sender];
}
}
/**
@notice Internal view function that returns the available txns for wallet
*/
function _getAvailableTxnsForWallet() internal view returns (uint256) {
return userToTxns[msg.sender];
}
}
contracts/handshake-drop/handshake-drop-libs/TokenTransferer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
/// @notice Safe ERC20 ,ERC721 multi transfer library that gracefully handles missing return values.
/// @author Cocomastoras
/// @author Modified from Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
///
/// @dev Note:
/// - For ERC20s and ERC721s, this implementation won't check that a token has code,
/// responsibility is delegated to the caller.
contract TokenTransferer {
error TransferFromFailed();
/**
* @dev Internal function to transfer ERC20 tokens from a given originator
* to a multiple recipients. Sufficient approvals must be set on the
* contract performing the transfer.
*
* @param token The ERC20 token to transfer.
* @param recipientsOffset Calldata offset of the recipients of the transfer.
* @param length Calldata length of the recipients of the transfer.
* @param amount The amount to transfer.
*/
function _performMultiERC20Transfer(address token, uint256 recipientsOffset, uint256 length, uint256 amount) internal{
/// @solidity memory-safe-assembly
assembly {
let total := mul(amount, length)
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, total) // Store the `amount` argument.
mstore(0x40, address()) // Store the `to` argument.
mstore(0x2c, shl(96, caller())) // Store the `from` argument.
mstore(0x0c, 0x23b872dd000000000000000000000000)
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(
eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
)
{
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
mstore(0x40, amount) // Store the `amount` argument.
for { let i := 0 } lt(i, length) { i := add(i, 1) } {
let to := calldataload(add(recipientsOffset, mul(i, 0x20)))
mstore(0x2c, shl(96, to)) // Store the `to` argument.
mstore(0x0c, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x1c, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/**
* @dev Internal function to transfer ERC20 tokens from a given originator
* to multiple recipients. Sufficient approvals must be set on the
* contract performing the transfer.
* @param token The ERC20 token to transfer.
* @param recipientsOffset Offset of the recipients of the transfer.
* @param recipientsLength Length of the recipients of the transfer.
* @param amountsOffset Offset of the amounts to transfer.
* @param totalAmount The totalAmount to transfer
*/
function _performMultiERC20TransferCustom(address token, uint256 recipientsOffset, uint256 recipientsLength, uint256 amountsOffset, uint256 totalAmount) internal {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, totalAmount) // Store the `amount` argument.
mstore(0x40, address()) // Store the `to` argument.
mstore(0x2c, shl(96, caller())) // Store the `from` argument.
mstore(0x0c, 0x23b872dd000000000000000000000000)
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(
eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
)
{
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
let sumAmount := 0
for { let i := 0 } lt(i, recipientsLength) { i := add(i, 1) } {
let to := calldataload(add(recipientsOffset, mul(i, 0x20)))
let amount := calldataload(add(amountsOffset, mul(i, 0x20)))
sumAmount := add(sumAmount, amount)
mstore(0x40, amount) // Store the `amount` argument.
mstore(0x2c, shl(96, to)) // Store the `to` argument.
mstore(0x0c, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x1c, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
}
if iszero(eq(totalAmount, sumAmount)) {
revert(0,0)
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/**
* @dev Internal function to transfer batch of ERC721 tokens from a given
* originator to multiple recipients. Sufficient approvals must be set on
* the contract performing the transfer. Note that this function does
* not check whether the receiver can accept the ERC721 token (i.e. it
* does not use `safeTransferFrom`).
*
* @param token The ERC721 token to transfer.
* @param from The originator of the transfer.
* @param recipientsOffset The offset of recipients of the transfer.
* @param recipientsLength The length of tokens to transfer.
* @param idsOffset The offset of tokenIds to transfer.
*/
function _performMultiERC721Transfer(
address token,
address from,
uint256 recipientsOffset,
uint256 recipientsLength,
uint256 idsOffset
) internal {
// Utilize assembly to perform an optimized ERC721 token transfer.
assembly {
let m := mload(0x40) // Cache the free memory pointer.
for { let i := 0 } lt(i, recipientsLength) { i := add(i, 1) } {
let to := calldataload(add(recipientsOffset, mul(i, 0x20)))
let identifier := calldataload(add(idsOffset, mul(i, 0x20)))
mstore(0x60, identifier) // Store the `identifier` argument.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
iszero(returndatasize()), // Returned error.
call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x00)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
}
contracts/handshake-drop/handshake-drop-types/interfaces/PremiumSubscriptionRegistryEvents.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
interface PremiumSubscriptionRegistryEvents {
event WalletBaseFeeSet(address indexed Wallet, uint256 BaseFeeInWei);
event WalletBaseFeeReset(address indexed Wallet);
event TxnsAdded(address[] Wallet, uint256[] Txns);
}
contracts/handshake-drop/handshake-drop-types/interfaces/SlashTokenEvents.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
interface SlashTokenEvents {
event Erc20AirdropEqualAmount(address indexed From, address indexed Token,uint256 RecipientsLength, uint256 TotalAmount);
event Erc20AirdropCustomAmount(address indexed From, address indexed Token, uint256 RecipientsLength, uint256 TotalAmount);
event NativeAirdropEqualAmount(address indexed From,uint256 RecipientsLength, uint256 TotalAmount);
event NativeAirdropCustomAmount(address indexed From, uint256 RecipientsLength, uint256 TotalAmount);
event Erc721Airdrop(address indexed From, address indexed Token, uint256 RecipientsLength);
}
contracts/handshake-drop/handshake-drop-types/interfaces/SlashTokenRegistryEvents.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
interface SlashTokenRegistryEvents {
event TxnsBundleBought(address indexed Buyer, uint256 Amount, uint256 Txns);
event BundlesUpdated(address indexed Operator, uint256[] BundlesAmounts, uint256[] BundlesPrices);
}
Contract ABI
[{"type":"constructor","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"addToDenylist","inputs":[{"type":"address[]","name":"list","internalType":"address[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"addTxnsToWallets","inputs":[{"type":"address[]","name":"wallets","internalType":"address[]"},{"type":"uint256[]","name":"txns","internalType":"uint256[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"baseFee","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[],"name":"buyTxnsBundle","inputs":[{"type":"uint256","name":"bundleIndex","internalType":"uint256"},{"type":"uint256","name":"quantity","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"claimFees","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[],"name":"erc20AirdropCustomAmount","inputs":[{"type":"address[]","name":"recipients","internalType":"address[]"},{"type":"uint256[]","name":"amount","internalType":"uint256[]"},{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"totalAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"erc20AirdropEqualAmount","inputs":[{"type":"address[]","name":"recipients","internalType":"address[]"},{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"address","name":"token","internalType":"address"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"erc721Airdrop","inputs":[{"type":"address[]","name":"recipients","internalType":"address[]"},{"type":"uint256[]","name":"ids","internalType":"uint256[]"},{"type":"address","name":"token","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"freezeContract","inputs":[{"type":"uint256","name":"value_","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"frozen","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getAvailableTxnsForWallet","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getBaseFeeForWallet","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"AvailableBundles","internalType":"uint256[]"},{"type":"uint256[]","name":"BundlesPrices","internalType":"uint256[]"}],"name":"getBundles","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"","internalType":"address[]"}],"name":"getDenylist","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"Users","internalType":"address[]"}],"name":"getUsersThatBoughtBundles","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"handleErc721Flag","inputs":[{"type":"uint256","name":"value_","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initialize","inputs":[{"type":"address","name":"admin_","internalType":"address"},{"type":"address","name":"feeSink_","internalType":"address"},{"type":"uint256","name":"baseFeeCostInWei","internalType":"uint256"},{"type":"uint256[]","name":"availableTxnsBundles_","internalType":"uint256[]"},{"type":"uint256[]","name":"txnsBundlesToPrice_","internalType":"uint256[]"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"nativeAirdropCustomAmount","inputs":[{"type":"address[]","name":"recipients","internalType":"address[]"},{"type":"uint256[]","name":"amounts","internalType":"uint256[]"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"nativeAirdropEqualAmount","inputs":[{"type":"address[]","name":"recipients","internalType":"address[]"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"permitErc721","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"removeFromDenylist","inputs":[{"type":"address[]","name":"list","internalType":"address[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"resetBaseFeeForWallet","inputs":[{"type":"address","name":"wallet","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setBaseFeeForWallet","inputs":[{"type":"address","name":"wallet","internalType":"address"},{"type":"uint256","name":"amountInWei","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setNewBaseFee","inputs":[{"type":"uint256","name":"baseFee_","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateBundles","inputs":[{"type":"uint256[]","name":"availableTxnsBundles_","internalType":"uint256[]"},{"type":"uint256[]","name":"txnsBundlesToPrice_","internalType":"uint256[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateFeeSink","inputs":[{"type":"address","name":"feeSink_","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"userToTxns","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"event","name":"BundlesUpdated","inputs":[{"type":"address","name":"Operator","indexed":true},{"type":"uint256[]","name":"BundlesAmounts","indexed":false},{"type":"uint256[]","name":"BundlesPrices","indexed":false}],"anonymous":false},{"type":"event","name":"Erc20AirdropCustomAmount","inputs":[{"type":"address","name":"From","indexed":true},{"type":"address","name":"Token","indexed":true},{"type":"uint256","name":"RecipientsLength","indexed":false},{"type":"uint256","name":"TotalAmount","indexed":false}],"anonymous":false},{"type":"event","name":"Erc20AirdropEqualAmount","inputs":[{"type":"address","name":"From","indexed":true},{"type":"address","name":"Token","indexed":true},{"type":"uint256","name":"RecipientsLength","indexed":false},{"type":"uint256","name":"TotalAmount","indexed":false}],"anonymous":false},{"type":"event","name":"Erc721Airdrop","inputs":[{"type":"address","name":"From","indexed":true},{"type":"address","name":"Token","indexed":true},{"type":"uint256","name":"RecipientsLength","indexed":false}],"anonymous":false},{"type":"event","name":"NativeAirdropCustomAmount","inputs":[{"type":"address","name":"From","indexed":true},{"type":"uint256","name":"RecipientsLength","indexed":false},{"type":"uint256","name":"TotalAmount","indexed":false}],"anonymous":false},{"type":"event","name":"NativeAirdropEqualAmount","inputs":[{"type":"address","name":"From","indexed":true},{"type":"uint256","name":"RecipientsLength","indexed":false},{"type":"uint256","name":"TotalAmount","indexed":false}],"anonymous":false},{"type":"event","name":"TxnsAdded","inputs":[{"type":"address[]","name":"Wallet","indexed":false},{"type":"uint256[]","name":"Txns","indexed":false}],"anonymous":false},{"type":"event","name":"TxnsBundleBought","inputs":[{"type":"address","name":"Buyer","indexed":true},{"type":"uint256","name":"Amount","indexed":false},{"type":"uint256","name":"Txns","indexed":false}],"anonymous":false},{"type":"event","name":"WalletBaseFeeReset","inputs":[{"type":"address","name":"Wallet","indexed":true}],"anonymous":false},{"type":"event","name":"WalletBaseFeeSet","inputs":[{"type":"address","name":"Wallet","indexed":true},{"type":"uint256","name":"BaseFeeInWei","indexed":false}],"anonymous":false},{"type":"error","name":"TransferFromFailed","inputs":[]}]
Deployed ByteCode
0x6080604052600436106101ac5760003560e01c80636ef25c3a116100ec57806393f9e6f91161008a578063d294f09311610064578063d294f09314610438578063d993cdff1461044d578063e85ddb8a1461047a578063fe7009901461049a57600080fd5b806393f9e6f9146103ed5780639d07a40b14610402578063cddfa1a41461042257600080fd5b80638634fd24116100c65780638634fd241461039457806388f941e8146103a75780638a6ff456146103ba5780638fc8b3b2146103da57600080fd5b80636ef25c3a1461033c5780637850953d14610352578063798ed83d1461037457600080fd5b806333f740a111610159578063586668061161013357806358666806146102e357806362274c2b146102f65780636b55040f146103165780636c11d85a1461032957600080fd5b806333f740a11461028157806334ae8e38146102a157806347ec4ed1146102c157600080fd5b8063244bd9671161018a578063244bd9671461021f578063286ddb681461024157806331fd0d7e1461026157600080fd5b8063054f7d9c146101b157806320a225cb146101da57806322f10e2f146101fd575b600080fd5b3480156101bd57600080fd5b506101c760025481565b6040519081526020015b60405180910390f35b3480156101e657600080fd5b506101ef6104ba565b6040516101d1929190612b34565b34801561020957600080fd5b5061021d610218366004612b62565b610567565b005b34801561022b57600080fd5b50336000908152600860205260409020546101c7565b34801561024d57600080fd5b5061021d61025c366004612b62565b6105f2565b34801561026d57600080fd5b5061021d61027c366004612b62565b610678565b34801561028d57600080fd5b5061021d61029c366004612ba4565b6106fe565b3480156102ad57600080fd5b5061021d6102bc366004612cdb565b610850565b3480156102cd57600080fd5b50336000908152600760205260409020546101c7565b61021d6102f1366004612d8b565b6109ac565b34801561030257600080fd5b5061021d610311366004612df7565b610d3b565b61021d610324366004612e12565b610e0d565b61021d610337366004612e9b565b611131565b34801561034857600080fd5b506101c7600c5481565b34801561035e57600080fd5b506103676114c9565b6040516101d19190612f62565b34801561038057600080fd5b5061021d61038f366004612fd7565b61155c565b61021d6103a2366004613014565b6116a0565b61021d6103b5366004613071565b6119b7565b3480156103c657600080fd5b5061021d6103d5366004612fd7565b611c2c565b61021d6103e8366004613093565b611d54565b3480156103f957600080fd5b506103676120ef565b34801561040e57600080fd5b5061021d61041d3660046130df565b61217d565b34801561042e57600080fd5b506101c760035481565b34801561044457600080fd5b5061021d6122dd565b34801561045957600080fd5b506101c7610468366004612df7565b60086020526000908152604090205481565b34801561048657600080fd5b5061021d61049536600461316e565b61231a565b3480156104a657600080fd5b5061021d6104b5366004612df7565b6124c1565b606080600980548060200260200160405190810160405280929190818152602001828054801561050957602002820191906000526020600020905b8154815260200190600101908083116104f5575b50505050509150600a80548060200260200160405190810160405280929190818152602001828054801561055c57602002820191906000526020600020905b815481526020019060010190808311610548575b505050505090509091565b60005473ffffffffffffffffffffffffffffffffffffffff1633146105ed576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e5653000000000000000000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b600c55565b60005473ffffffffffffffffffffffffffffffffffffffff163314610673576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e5653000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b600255565b60005473ffffffffffffffffffffffffffffffffffffffff1633146106f9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e5653000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b600355565b600c548110610769576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e5656000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b60005473ffffffffffffffffffffffffffffffffffffffff1633146107ea576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e5653000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b73ffffffffffffffffffffffffffffffffffffffff821660008181526007602052604090819020839055517f44784c405393a593553ed0bf0ddb15aa2494ba9b97a8d75dfc9fabb7a64c8ce5906108449084815260200190565b60405180910390a25050565b60005473ffffffffffffffffffffffffffffffffffffffff1633146108d1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e5653000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b805182511461093c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e5644000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b815161094f906009906020850190612a99565b50805161096390600a906020840190612a99565b503373ffffffffffffffffffffffffffffffffffffffff167fed68a7a2289c86234d260dc96066680ff0bcde7d3376a219df515dde860435b18383604051610844929190612b34565b6109b4612589565b60025415610a1e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f434600000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b600b54600003610a8a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e4959000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b3360009081526004602052604090205415610b01576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f554400000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b336000908152600760209081526040808320546008909252822054909180866101f48111801590610b3157508086145b610b97576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e564c000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b8892508691506000610baa8483856125fc565b90508515610bc157610bbc81876131d4565b610bcf565b80600c54610bcf91906131d4565b955084600003610c4757853414610c42576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e5656000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b610cea565b803414610cb0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e5656000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b610cea33600090815260086020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019055565b604080518381526020810183905233917f2c7cd2d52efdb7bcb324ce1fdaad584211503edbbf94fa7a3fde2624da572642910160405180910390a2505050505050610d356001600f55565b50505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610dbc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e5653000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b73ffffffffffffffffffffffffffffffffffffffff8116600081815260076020526040808220829055517f9a93cb5054fe47b6e8ec0e29a26edd6249309d823eccab337b5c81dda65f6ddc9190a250565b610e15612589565b60025415610e7f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f434600000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b3360009081526004602052604090205415610ef6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f554400000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b33600090815260076020908152604080832054600890925290912054868215610f1f5782610f23565b600c545b925081600003610f9b57823414610f96576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e5656000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b610fd5565b610fd533600090815260086020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019055565b600b54600003611041576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e4959000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b6101f4811115801561105257508086145b6110b8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e564c000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b88876110c7878385848a61263f565b604080518481526020810188905273ffffffffffffffffffffffffffffffffffffffff89169133917f0e18fd6025578556afc2d4c83f1e98be2ddb206457774fa37a3a8b1605ad6f79910160405180910390a350505050506111296001600f55565b505050505050565b611139612589565b6003546000036111a5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e4559000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b6002541561120f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f434600000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b600b5460000361127b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e4959000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b33600090815260046020526040902054156112f2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f554400000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b33600090815260076020526040902054801561130e5780611312565b600c545b9050600061132c3360009081526008602052604090205490565b90508560008290036113a6578234146113a1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e5656000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b6113e0565b6113e033600090815260086020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019055565b6101f481111580156113f157508085145b611457576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e564c000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b87866114668633848685612716565b60405183815273ffffffffffffffffffffffffffffffffffffffff87169033907f4bd386a75bd69ced877c29ffb9d2058eeed770d425c9027116b45e6f0fb4645a9060200160405180910390a350505050506114c26001600f55565b5050505050565b60005460609073ffffffffffffffffffffffffffffffffffffffff16331461154d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e5653000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b611557600d61277a565b905090565b60005473ffffffffffffffffffffffffffffffffffffffff1633146115dd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e5653000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b805160005b8181101561169b576116178382815181106115ff576115ff6131e7565b6020026020010151600561278e90919063ffffffff16565b61164a57611648838281518110611630576116306131e7565b602002602001015160056127c290919063ffffffff16565b505b600160046000858481518110611662576116626131e7565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252810191909152604001600020556001016115e2565b505050565b6116a8612589565b60025415611712576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f434600000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b3360009081526004602052604090205415611789576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f554400000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b336000908152600760209081526040808320546008909252909120548482156117b257826117b6565b600c545b92508160000361182e57823414611829576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e5656000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b611868565b61186833600090815260086020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019055565b600b546000036118d4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e4959000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b6101f4811115611940576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e564c000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b8661194d858284896127e4565b73ffffffffffffffffffffffffffffffffffffffff8516337fbfc5bd893849e5d9bf1ac87d7e1fcec4f9afece75f87062d0c2370f1c6b5734b846119918a82613216565b6040805192835260208301919091520160405180910390a350505050610d356001600f55565b60025415611a21576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f434600000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b80600a8381548110611a3557611a356131e7565b9060005260206000200154611a4a9190613216565b3414611ab2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e5656000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b3360009081526004602052604090205415611b29576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f554400000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b80600003611b93576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e5641000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b611b9e600d3361278e565b611baf57611bad600d336127c2565b505b60008160098481548110611bc557611bc56131e7565b600091825260208083209190910154338084526008835260409384902080549590920294850190915582513481529182018490529293507f3686dd4aa3f7c95dcac2734a95dcdcf29641e7e3f71f6ea1b39bc6bea5e36385910160405180910390a2505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314611cad576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e5653000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b805160005b8181101561169b57611ccf8382815181106115ff576115ff6131e7565b15611d0357611d01838281518110611ce957611ce96131e7565b602002602001015160056128a990919063ffffffff16565b505b600060046000858481518110611d1b57611d1b6131e7565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002055600101611cb2565b611d5c612589565b60025415611dc6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f434600000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b600b54600003611e32576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e4959000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b3360009081526004602052604090205415611ea9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f554400000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b60008281611ec33360009081526007602052604090205490565b90506000611edd3360009081526008602052604090205490565b90506000611eeb8487613216565b90508215611f0257611efd81846131d4565b611f10565b80600c54611f1091906131d4565b925081600003611f8857823414611f83576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e5656000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b61202b565b803414611ff1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e5656000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b61202b33600090815260086020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019055565b6101f4841115612097576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e564c000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b8794506120a58585886128cb565b604080518581526020810183905233917fca9fd6c3fcc68235a3c738db806875a8e98e0bcb25f11f56860971296c6de997910160405180910390a2505050505061169b6001600f55565b60005460609073ffffffffffffffffffffffffffffffffffffffff163314612173576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e5653000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b611557600561277a565b600b54156121e7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f414900000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b8051825114612252576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e5644000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b6000805473ffffffffffffffffffffffffffffffffffffffff8088167fffffffffffffffffffffffff000000000000000000000000000000000000000092831617909255600180549287169290911691909117905581516122ba906009906020850190612a99565b5080516122ce90600a906020840190612a99565b50506001600b5550600c555050565b60005473ffffffffffffffffffffffffffffffffffffffff1633811461230257600080fd5b600080600080476001545af161231757600080fd5b50565b60005473ffffffffffffffffffffffffffffffffffffffff16331461239b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e5653000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b8051825114612406576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e564c000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b815160005b8181101561248257828181518110612425576124256131e7565b602002602001015160086000868481518110612443576124436131e7565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040016000208054909101905560010161240b565b507f590a1a0c22d6042324e10baeee375e32704843a4630e1cc9889e30170a5a2efa83836040516124b492919061322d565b60405180910390a1505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314612542576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4e5653000000000000000000000000000000000000000000000000000000000060448201526064016105e4565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6002600f54036125f5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016105e4565b6002600f55565b6000805b83811015612637576020810283810135928301929086013590600080808084866108fcf161262d57600080fd5b5050600101612600565b509392505050565b60405181606052306040523360601b602c526f23b872dd000000000000000000000000600c52602060006064601c60008a5af13d15600160005114171661268e57637939f4246000526004601cfd5b6000805b858110156126f95760208181028681013560408190529089013560601b602c526fa9059cbb000000000000000000000000600c52929092019160006044601c828c5af13d1560016000511417166126f157637939f4246000526004601cfd5b600101612692565b5080831461270657600080fd5b5060006060526040525050505050565b60405160005b8381101561270657602081028381013560609081529086013560405286901b602c526f23b872dd000000000000000000000000600c526000806064601c828b5af13d151661277257637939f4246000526004601cfd5b60010161271c565b60606000612787836128fb565b9392505050565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260018301602052604081205415155b90505b92915050565b60006127b98373ffffffffffffffffffffffffffffffffffffffff8416612957565b81810260405181606052306040523360601b602c526f23b872dd000000000000000000000000600c52602060006064601c60008a5af13d15600160005114171661283657637939f4246000526004601cfd5b82604052600091505b8382101561289a57602082810286013560601b602c526fa9059cbb000000000000000000000000600c5260006044601c828a5af13d15600160005114171661288f57637939f4246000526004601cfd5b60018201915061283f565b60006060526040525050505050565b60006127b98373ffffffffffffffffffffffffffffffffffffffff84166129a6565b60005b82811015610d35576020810284013560008060008086856108fcf16128f257600080fd5b506001016128ce565b60608160000180548060200260200160405190810160405280929190818152602001828054801561294b57602002820191906000526020600020905b815481526020019060010190808311612937575b50505050509050919050565b600081815260018301602052604081205461299e575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556127bc565b5060006127bc565b60008181526001830160205260408120548015612a8f5760006129ca600183613240565b85549091506000906129de90600190613240565b9050818114612a435760008660000182815481106129fe576129fe6131e7565b9060005260206000200154905080876000018481548110612a2157612a216131e7565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080612a5457612a54613253565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506127bc565b60009150506127bc565b828054828255906000526020600020908101928215612ad4579160200282015b82811115612ad4578251825591602001919060010190612ab9565b50612ae0929150612ae4565b5090565b5b80821115612ae05760008155600101612ae5565b600081518084526020808501945080840160005b83811015612b2957815187529582019590820190600101612b0d565b509495945050505050565b604081526000612b476040830185612af9565b8281036020840152612b598185612af9565b95945050505050565b600060208284031215612b7457600080fd5b5035919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114612b9f57600080fd5b919050565b60008060408385031215612bb757600080fd5b612bc083612b7b565b946020939093013593505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715612c4457612c44612bce565b604052919050565b600067ffffffffffffffff821115612c6657612c66612bce565b5060051b60200190565b600082601f830112612c8157600080fd5b81356020612c96612c9183612c4c565b612bfd565b82815260059290921b84018101918181019086841115612cb557600080fd5b8286015b84811015612cd05780358352918301918301612cb9565b509695505050505050565b60008060408385031215612cee57600080fd5b823567ffffffffffffffff80821115612d0657600080fd5b612d1286838701612c70565b93506020850135915080821115612d2857600080fd5b50612d3585828601612c70565b9150509250929050565b60008083601f840112612d5157600080fd5b50813567ffffffffffffffff811115612d6957600080fd5b6020830191508360208260051b8501011115612d8457600080fd5b9250929050565b60008060008060408587031215612da157600080fd5b843567ffffffffffffffff80821115612db957600080fd5b612dc588838901612d3f565b90965094506020870135915080821115612dde57600080fd5b50612deb87828801612d3f565b95989497509550505050565b600060208284031215612e0957600080fd5b6127b982612b7b565b60008060008060008060808789031215612e2b57600080fd5b863567ffffffffffffffff80821115612e4357600080fd5b612e4f8a838b01612d3f565b90985096506020890135915080821115612e6857600080fd5b50612e7589828a01612d3f565b9095509350612e88905060408801612b7b565b9150606087013590509295509295509295565b600080600080600060608688031215612eb357600080fd5b853567ffffffffffffffff80821115612ecb57600080fd5b612ed789838a01612d3f565b90975095506020880135915080821115612ef057600080fd5b50612efd88828901612d3f565b9094509250612f10905060408701612b7b565b90509295509295909350565b600081518084526020808501945080840160005b83811015612b2957815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101612f30565b6020815260006127b96020830184612f1c565b600082601f830112612f8657600080fd5b81356020612f96612c9183612c4c565b82815260059290921b84018101918181019086841115612fb557600080fd5b8286015b84811015612cd057612fca81612b7b565b8352918301918301612fb9565b600060208284031215612fe957600080fd5b813567ffffffffffffffff81111561300057600080fd5b61300c84828501612f75565b949350505050565b6000806000806060858703121561302a57600080fd5b843567ffffffffffffffff81111561304157600080fd5b61304d87828801612d3f565b9095509350506020850135915061306660408601612b7b565b905092959194509250565b6000806040838503121561308457600080fd5b50508035926020909101359150565b6000806000604084860312156130a857600080fd5b833567ffffffffffffffff8111156130bf57600080fd5b6130cb86828701612d3f565b909790965060209590950135949350505050565b600080600080600060a086880312156130f757600080fd5b61310086612b7b565b945061310e60208701612b7b565b935060408601359250606086013567ffffffffffffffff8082111561313257600080fd5b61313e89838a01612c70565b9350608088013591508082111561315457600080fd5b5061316188828901612c70565b9150509295509295909350565b6000806040838503121561318157600080fd5b823567ffffffffffffffff8082111561319957600080fd5b612d1286838701612f75565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808201808211156127bc576127bc6131a5565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b80820281158282048414176127bc576127bc6131a5565b604081526000612b476040830185612f1c565b818103818111156127bc576127bc6131a5565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfea2646970667358221220b114a5b4adee4ebee2f9830beb5b22442460ed8bdccb6b60582d128f465918ff64736f6c63430008130033