Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
NativeTokenVestingManagerFactory
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 2000 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import { INativeTokenVestingManagerFactory } from "contracts/interfaces/INativeTokenVestingManagerFactory.sol";
import { NativeTokenVestingManager } from "contracts/NativeTokenVestingManager.sol";
import { ITypes } from "contracts/interfaces/ITypes.sol";
import { FactoryFeeManager } from "contracts/libraries/FactoryFeeManager.sol";
/// @title NativeTokenVestingManagerFactory Factory
/// @notice Allows anyone to reliably deploy a new `NativeTokenVestingManager` contract using deterministic addresses.
contract NativeTokenVestingManagerFactory is
INativeTokenVestingManagerFactory,
FactoryFeeManager
{
constructor(
address feeCollector_,
uint256 defaultGasFee_,
uint256 defaultTokenFee_
) FactoryFeeManager(feeCollector_, defaultGasFee_, defaultTokenFee_) {}
/// @notice Creates a new TokenVestingManager instance
/// @param fundingType The type of funding to use for this manager (Full or Partial)
/// @return The address of the newly created NativeTokenVestingManager contract
function newNativeTokenVestingManager(
ITypes.FundingType fundingType
) external returns (NativeTokenVestingManager) {
ITypes.FeeType feeType = ITypes.FeeType.Gas;
uint256 fee;
CustomFee memory customFee = _customFees[msg.sender];
if (customFee.enabled && customFee.preferredFeeType == feeType) {
fee = customFee.gasFee;
} else {
fee = defaultGasFee;
}
NativeTokenVestingManager nativeTokenVestingManager = new NativeTokenVestingManager(
fee,
feeCollector,
fundingType
);
emit NativeTokenVestingManagerCreated({
nativeTokenVestingManager: nativeTokenVestingManager,
feeType: feeType,
fee: fee,
feeCollector: feeCollector,
creator: msg.sender,
fundingType: fundingType
});
nativeTokenVestingManager.setAdmin(msg.sender, true);
nativeTokenVestingManager.setAdmin(address(this), false);
return nativeTokenVestingManager;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import { NativeTokenVestingManager } from "contracts/NativeTokenVestingManager.sol";
import { ITypes } from "contracts/interfaces/ITypes.sol";
/// @title Interface for the NativeTokenVestingManagerFactory
/// @notice Provides the functionality to deploy NativeTokenVestingManager contracts deterministically using Create2.
interface INativeTokenVestingManagerFactory {
// Event emitted when a new NativeTokenVestingManager is created
event NativeTokenVestingManagerCreated(
NativeTokenVestingManager nativeTokenVestingManager,
ITypes.FeeType feeType,
uint256 fee,
address feeCollector,
address creator,
ITypes.FundingType fundingType
);
/// @notice Deploys a new NativeTokenVestingManager contract
/// @param fundingType The type of funding to use for this manager (Full or Partial)
/// @return The address of the newly created NativeTokenVestingManager contract
function newNativeTokenVestingManager(
ITypes.FundingType fundingType
) external returns (NativeTokenVestingManager);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import { IERC20, SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { AccessProtected } from "contracts/libraries/AccessProtected.sol";
import { TokenVestingLib } from "contracts/libraries/TokenVestingLib.sol";
import { Errors } from "contracts/libraries/Errors.sol";
import { ITypes } from "contracts/interfaces/ITypes.sol";
import { INativeTokenVestingManager } from "contracts/interfaces/INativeTokenVestingManager.sol";
/**
* @title Native Token Vesting Manager
* @notice Contract to manage ETH/native token vesting
*/
contract NativeTokenVestingManager is
AccessProtected,
INativeTokenVestingManager
{
using TokenVestingLib for TokenVestingLib.Vesting;
using SafeERC20 for IERC20;
/// Fee type for this contract (only Gas fee is supported)
ITypes.FeeType public constant FEE_TYPE = ITypes.FeeType.Gas;
/// Funding type for this contract
ITypes.FundingType public immutable FUNDING_TYPE;
/// Block number when this contract was deployed
uint256 public immutable DEPLOYMENT_BLOCK_NUMBER;
/// Fee amount for this contract
uint256 public immutable FEE;
/// Total number of native tokens reserved for vesting
uint256 public numTokensReservedForVesting;
/// Total number of accumulated fees
uint256 public numTokensReservedForFees;
/// Nonce to generate vesting IDs
uint256 private vestingIdNonce;
/// Address of the fee collector
address public feeCollector;
/// List of all recipients
address[] public recipients;
/// Mapping: recipient address => index+1 in recipients array (0 means not in array)
mapping(address => uint256) private recipientToIndex;
// Mappings for vesting data
/// Mapping: vestingId => funding amount
mapping(bytes32 => uint256) public vestingFunding;
/// Mapping: recipient => vestingIds
mapping(address => bytes32[]) public recipientVestings;
/// Mapping: recipient => vestingId => index+1 in recipientVestings array (0 means not in array)
mapping(address => mapping(bytes32 => uint256))
private vestingToRecipientIndex;
/// Mapping: vestingId => vesting
mapping(bytes32 => TokenVestingLib.Vesting) public vestingById;
/// Mapping: vestingId => newOwner
mapping(bytes32 => address) public pendingVestingTransfers;
/**
* @dev Reverts if the vesting is not active
*/
modifier isVestingActive(bytes32 _vestingId) {
if (vestingById[_vestingId].deactivationTimestamp != 0)
revert Errors.VestingNotActive();
_;
}
/**
* @dev Reverts if caller is not the recipient of the vesting
*/
modifier onlyVestingRecipient(bytes32 _vestingId) {
if (msg.sender != vestingById[_vestingId].recipient)
revert Errors.NotVestingOwner();
_;
}
// This checks if the msg.sender is the fee collector
modifier onlyFeeCollector() {
if (msg.sender != feeCollector) revert Errors.NotFeeCollector();
_;
}
/**
* @notice Initialize the native token vesting manager
* @param fee_ - Fee amount for this contract
* @param feeCollector_ - Address of the fee collector
* @param fundingType_ - Type of funding for this contract (Full or Partial)
*/
constructor(
uint256 fee_,
address feeCollector_,
ITypes.FundingType fundingType_
) {
if (feeCollector_ == address(0)) revert Errors.InvalidAddress();
FEE = fee_;
feeCollector = feeCollector_;
FUNDING_TYPE = fundingType_;
DEPLOYMENT_BLOCK_NUMBER = block.number;
}
/**
* @notice Create a new vesting
* @param _recipient - Address of the recipient
* @param _startTimestamp - Start timestamp of the vesting
* @param _endTimestamp - End timestamp of the vesting
* @param _timelock - Timelock for the vesting
* @param _initialUnlock - Initial unlock amount
* @param _cliffReleaseTimestamp - Cliff release timestamp
* @param _cliffAmount - Cliff amount
* @param _releaseIntervalSecs - Release interval in seconds
* @param _linearVestAmount - Linear vest amount
* @param _isRevocable - Whether the vesting is revocable
*/
function createVesting(
address _recipient,
uint32 _startTimestamp,
uint32 _endTimestamp,
uint32 _timelock,
uint256 _initialUnlock,
uint32 _cliffReleaseTimestamp,
uint256 _cliffAmount,
uint32 _releaseIntervalSecs,
uint256 _linearVestAmount,
bool _isRevocable
) external payable onlyAdmin {
// Partial funding mode doesn't handle sent tokens
if (FUNDING_TYPE == ITypes.FundingType.Partial) {
if (msg.value != 0) revert Errors.InvalidFundingAmount();
} else {
// For full funding, require exact ETH amount to be sent
uint256 totalExpectedAmount = _initialUnlock +
_cliffAmount +
_linearVestAmount;
if (msg.value != totalExpectedAmount)
revert Errors.InsufficientBalance();
numTokensReservedForVesting += totalExpectedAmount;
}
bytes32 vestingId = bytes32(vestingIdNonce++);
// Create the vesting
_createVesting(
TokenVestingLib.VestingParams({
_vestingId: vestingId,
_recipient: _recipient,
_startTimestamp: _startTimestamp,
_endTimestamp: _endTimestamp,
_timelock: _timelock,
_initialUnlock: _initialUnlock,
_cliffReleaseTimestamp: _cliffReleaseTimestamp,
_cliffAmount: _cliffAmount,
_releaseIntervalSecs: _releaseIntervalSecs,
_linearVestAmount: _linearVestAmount,
_isRevocable: _isRevocable
})
);
}
/**
* @notice Create a batch of vestings
* @param params - Parameters for creating the vesting batch
*/
function createVestingBatch(
CreateVestingBatchParams calldata params
) external payable onlyAdmin {
uint256 length = params._recipients.length;
// Check array lengths match
_checkArrayLengthMismatch(params, length);
// For full funding, require exact ETH amount to be sent
if (FUNDING_TYPE == ITypes.FundingType.Partial) {
if (msg.value != 0) revert Errors.InvalidFundingAmount();
} else {
uint256 totalExpectedAmount;
// Calculate total required amount
for (uint256 i; i < length; ++i) {
totalExpectedAmount +=
params._initialUnlocks[i] +
params._cliffAmounts[i] +
params._linearVestAmounts[i];
}
if (msg.value != totalExpectedAmount)
revert Errors.InsufficientFunding();
numTokensReservedForVesting += totalExpectedAmount;
}
// Create all vestings
for (uint256 i; i < length; ++i) {
bytes32 vestingId = bytes32(vestingIdNonce++);
_createVesting(
TokenVestingLib.VestingParams({
_vestingId: vestingId,
_recipient: params._recipients[i],
_startTimestamp: params._startTimestamps[i],
_endTimestamp: params._endTimestamps[i],
_timelock: params._timelocks[i],
_initialUnlock: params._initialUnlocks[i],
_cliffReleaseTimestamp: params._cliffReleaseTimestamps[i],
_cliffAmount: params._cliffAmounts[i],
_releaseIntervalSecs: params._releaseIntervalSecs[i],
_linearVestAmount: params._linearVestAmounts[i],
_isRevocable: params._isRevocables[i]
})
);
}
}
/**
* @notice Fund an existing vesting schedule
* @param _vestingId - Identifier of the vesting to fund
*/
function fundVesting(
bytes32 _vestingId
) external payable isVestingActive(_vestingId) onlyAdmin {
if (FUNDING_TYPE == ITypes.FundingType.Full)
revert Errors.VestingFullyFunded();
if (msg.value == 0) revert Errors.InvalidFundingAmount();
TokenVestingLib.Vesting storage vesting = vestingById[_vestingId];
if (vesting.recipient == address(0)) revert Errors.EmptyVesting();
// Calculate total tokens needed for the vesting
uint256 totalRequired = vesting.initialUnlock +
vesting.cliffAmount +
vesting.linearVestAmount;
uint256 currentFunding = vestingFunding[_vestingId];
// Ensure we don't overfund
if (currentFunding >= totalRequired) revert Errors.VestingFullyFunded();
// Calculate how much more funding is allowed
uint256 remainingFunding = totalRequired - currentFunding;
if (msg.value > remainingFunding) revert Errors.FundingLimitExceeded();
// Update funding records
vestingFunding[_vestingId] += msg.value;
numTokensReservedForVesting += msg.value;
emit VestingFunded(
_vestingId,
msg.sender,
msg.value,
vestingFunding[_vestingId],
totalRequired
);
}
/**
* @notice Fund multiple vestings in batch
* @param _vestingIds - Array of vesting identifiers to fund
* @param _fundingAmounts - Array of funding amounts for each vesting
*/
function fundVestingBatch(
bytes32[] calldata _vestingIds,
uint256[] calldata _fundingAmounts
) external payable onlyAdmin {
if (FUNDING_TYPE == ITypes.FundingType.Full)
revert Errors.VestingFullyFunded();
uint256 length = _vestingIds.length;
if (length == 0) revert Errors.EmptyArray();
if (length != _fundingAmounts.length)
revert Errors.ArrayLengthMismatch();
// Check if total sent ETH is zero
if (msg.value == 0) revert Errors.InsufficientFunding();
uint256 totalFundingAmount;
for (uint256 i; i < length; ++i) {
totalFundingAmount += _fundingAmounts[i];
}
// Check if total sent ETH matches required amount
if (msg.value != totalFundingAmount)
revert Errors.InsufficientFunding();
// validate, update state, and calculate total
for (uint256 i; i < length; ++i) {
bytes32 vestingId = _vestingIds[i];
uint256 fundingAmount = _fundingAmounts[i];
// Skip entries with zero funding amount
if (fundingAmount == 0) continue;
TokenVestingLib.Vesting storage vesting = vestingById[vestingId];
if (vesting.recipient == address(0)) revert Errors.EmptyVesting();
if (vesting.deactivationTimestamp != 0)
revert Errors.VestingNotActive();
// Calculate total tokens needed for the vesting
uint256 totalRequired = vesting.initialUnlock +
vesting.cliffAmount +
vesting.linearVestAmount;
uint256 currentFunding = vestingFunding[vestingId];
// Ensure we don't overfund
if (currentFunding >= totalRequired)
revert Errors.VestingFullyFunded();
// Calculate how much more funding is allowed
uint256 remainingFunding = totalRequired - currentFunding;
if (fundingAmount > remainingFunding)
revert Errors.FundingLimitExceeded();
// Update funding records
vestingFunding[vestingId] += fundingAmount;
numTokensReservedForVesting += fundingAmount;
// Emit event for this funding
emit VestingFunded(
vestingId,
msg.sender,
fundingAmount,
vestingFunding[vestingId],
totalRequired
);
}
}
/**
* @notice Claim vested native tokens
* @param _vestingId - Identifier of the vesting
*/
function claim(
bytes32 _vestingId
) external payable onlyVestingRecipient(_vestingId) {
if (msg.value != FEE) revert Errors.InsufficientFee();
numTokensReservedForFees += msg.value;
_claim(_vestingId);
}
/**
* @notice Admin claims vested tokens on behalf of a recipient (gas sponsoring)
* @param _vestingId - Identifier of the vesting
*/
function adminClaim(bytes32 _vestingId) external payable onlyAdmin {
if (msg.value != FEE) revert Errors.InsufficientFee();
numTokensReservedForFees += msg.value;
_claim(_vestingId);
}
/**
* @notice Admin claims vested tokens for multiple recipients (batch gas sponsoring)
* @param _vestingIds - Array of vesting identifiers to claim
*/
function batchAdminClaim(
bytes32[] calldata _vestingIds
) external payable onlyAdmin {
uint256 length = _vestingIds.length;
if (length == 0) revert Errors.EmptyArray();
if (msg.value != FEE * length) revert Errors.InsufficientFee();
numTokensReservedForFees += msg.value;
for (uint256 i; i < length; ++i) _claim(_vestingIds[i]);
}
/**
* @notice Revoke active Vesting
* @param _vestingId - Vesting Identifier
*/
function revokeVesting(bytes32 _vestingId) external onlyAdmin {
_revokeVesting(_vestingId);
}
/**
* @notice Revoke multiple vestings in batch
* @param _vestingIds - Array of vesting identifiers to revoke
* @dev More gas efficient than calling revokeVesting multiple times
*/
function batchRevokeVestings(
bytes32[] calldata _vestingIds
) external onlyAdmin {
uint256 length = _vestingIds.length;
if (length == 0) revert Errors.EmptyArray();
for (uint256 i; i < length; ++i) _revokeVesting(_vestingIds[i]);
}
/**
* @notice Allow the owner to withdraw any balance not currently tied up in Vestings
* @param _amountRequested - Amount to withdraw
*/
function withdrawAdmin(uint256 _amountRequested) external onlyAdmin {
uint256 amountRemaining = amountAvailableToWithdrawByAdmin();
if (_amountRequested > amountRemaining)
revert Errors.InsufficientBalance();
emit AdminWithdrawn(msg.sender, _amountRequested);
// Use call instead of transfer for better gas optimization and compatibility
(bool success, ) = msg.sender.call{ value: _amountRequested }("");
if (!success) revert Errors.TransferFailed();
}
/**
* @notice Withdraw a token which isn't controlled by the vesting contract. Useful when someone accidentally sends tokens to the contract
* @param _otherTokenAddress - the token which we want to withdraw
*/
function withdrawOtherToken(address _otherTokenAddress) external onlyAdmin {
if (_otherTokenAddress == address(0)) revert Errors.InvalidAddress();
uint256 balance = IERC20(_otherTokenAddress).balanceOf(address(this));
IERC20(_otherTokenAddress).safeTransfer(msg.sender, balance);
}
/**
* @notice Initiate the transfer of vesting ownership to a new address
* @param _vestingId The ID of the vesting to transfer
* @param _newOwner The address of the new owner
*/
function initiateVestingTransfer(
bytes32 _vestingId,
address _newOwner
) external onlyVestingRecipient(_vestingId) isVestingActive(_vestingId) {
if (_newOwner == address(0)) revert Errors.InvalidAddress();
if (pendingVestingTransfers[_vestingId] != address(0))
revert Errors.PendingTransferExists();
pendingVestingTransfers[_vestingId] = _newOwner;
emit VestingTransferInitiated(msg.sender, _newOwner, _vestingId);
}
/**
* @notice Cancel a pending vesting transfer
* @param _vestingId The ID of the vesting with a pending transfer
*/
function cancelVestingTransfer(
bytes32 _vestingId
) external onlyVestingRecipient(_vestingId) {
if (pendingVestingTransfers[_vestingId] == address(0))
revert Errors.NoPendingTransfer();
delete pendingVestingTransfers[_vestingId];
emit VestingTransferCancelled(msg.sender, _vestingId);
}
/**
* @notice Accept a vesting transfer as the new owner
* @param _vestingId The ID of the vesting to accept
*/
function acceptVestingTransfer(
bytes32 _vestingId
) external isVestingActive(_vestingId) {
address pendingOwner = pendingVestingTransfers[_vestingId];
if (pendingOwner != msg.sender)
revert Errors.NotAuthorizedForTransfer();
TokenVestingLib.Vesting storage vesting = vestingById[_vestingId];
address previousOwner = vesting.recipient;
// Remove vesting ID from previous owner's mapping
_removeVestingFromRecipient(previousOwner, _vestingId);
// Update vesting recipient to new owner
vesting.recipient = pendingOwner;
// Add new owner to recipients list if they're not already in it
if (!_isRecipient(pendingOwner)) {
recipients.push(pendingOwner);
recipientToIndex[pendingOwner] = recipients.length;
}
// Add vesting ID to new owner's mapping
recipientVestings[pendingOwner].push(_vestingId);
// Update the vesting index mapping for the new owner
vestingToRecipientIndex[pendingOwner][_vestingId] = recipientVestings[
pendingOwner
].length;
// Clear the pending transfer
delete pendingVestingTransfers[_vestingId];
// Emit transfer event
emit VestingTransferred(previousOwner, pendingOwner, _vestingId);
}
/**
* @notice Direct transfer of vesting ownership by admin (for contract compatibility)
* @param _vestingId The ID of the vesting to transfer
* @param _newOwner The address of the new owner
* @dev This is specifically for compatibility with contracts that cannot call acceptVestingTransfer
*/
function directVestingTransfer(
bytes32 _vestingId,
address _newOwner
) external onlyVestingRecipient(_vestingId) isVestingActive(_vestingId) {
if (_newOwner == address(0)) revert Errors.InvalidAddress();
TokenVestingLib.Vesting storage vesting = vestingById[_vestingId];
address previousOwner = vesting.recipient;
// Remove vesting ID from previous owner's mapping
_removeVestingFromRecipient(previousOwner, _vestingId);
// Update vesting recipient to new owner
vesting.recipient = _newOwner;
// Add new owner to recipients list if they're not already in it
if (!_isRecipient(_newOwner)) {
recipients.push(_newOwner);
recipientToIndex[_newOwner] = recipients.length;
}
// Add vesting ID to new owner's mapping
recipientVestings[_newOwner].push(_vestingId);
// Update the vesting index mapping for the new owner
vestingToRecipientIndex[_newOwner][_vestingId] = recipientVestings[
_newOwner
].length;
// Clear any pending transfer if it exists
if (pendingVestingTransfers[_vestingId] != address(0))
delete pendingVestingTransfers[_vestingId];
// Emit transfer event
emit VestingTransferred(previousOwner, _newOwner, _vestingId);
}
/**
* @notice Allows only fee collector to withdraw collected gas fees
* @dev This function is completely separate from distributor admin controls
* @param recipient Address to receive the fee
* @param amount Amount to withdraw, if 0 withdraws all available fees
*/
function withdrawGasFee(
address recipient,
uint256 amount
) external onlyFeeCollector {
if (recipient == address(0)) revert Errors.InvalidAddress();
if (numTokensReservedForFees == 0) revert Errors.InsufficientFee();
// If amount is 0, withdraw all fees
if (amount == 0) amount = numTokensReservedForFees;
if (amount > numTokensReservedForFees) revert Errors.FeeTooLow();
numTokensReservedForFees -= amount;
emit GasFeeWithdrawn(recipient, amount);
(bool success, ) = recipient.call{ value: amount }("");
if (!success) revert Errors.TransferFailed();
}
/**
* @notice Updates the fee collector address
* @param newFeeCollector Address of the new fee collector
* @dev Can only be called by the fee collector
*/
function transferFeeCollectorRole(
address newFeeCollector
) external onlyFeeCollector {
if (newFeeCollector == address(0)) revert Errors.InvalidAddress();
address oldFeeCollector = feeCollector;
feeCollector = newFeeCollector;
emit FeeCollectorUpdated(oldFeeCollector, newFeeCollector);
}
/**
* @notice Get funding information for a vesting
* @param _vestingId - Identifier of the vesting
* @return fundingType - Type of funding (Full or Partial)
* @return totalFunded - Total amount of tokens funded so far
* @return totalRequired - Total amount of tokens required for full funding
*/
function getVestingFundingInfo(
bytes32 _vestingId
)
external
view
returns (uint8 fundingType, uint256 totalFunded, uint256 totalRequired)
{
TokenVestingLib.Vesting storage vesting = vestingById[_vestingId];
if (vesting.recipient == address(0)) revert Errors.EmptyVesting();
fundingType = uint8(FUNDING_TYPE);
totalRequired =
vesting.initialUnlock +
vesting.cliffAmount +
vesting.linearVestAmount;
totalFunded = FUNDING_TYPE == ITypes.FundingType.Full
? totalRequired
: vestingFunding[_vestingId];
return (fundingType, totalFunded, totalRequired);
}
/**
* @notice Check if a vesting is fully funded
* @param _vestingId - Identifier of the vesting
* @return isFullyFunded - True if the vesting is fully funded
*/
function isVestingFullyFunded(
bytes32 _vestingId
) external view returns (bool) {
TokenVestingLib.Vesting storage vesting = vestingById[_vestingId];
if (vesting.recipient == address(0)) revert Errors.EmptyVesting();
if (FUNDING_TYPE == ITypes.FundingType.Full) return true;
uint256 totalRequired = vesting.initialUnlock +
vesting.cliffAmount +
vesting.linearVestAmount;
uint256 totalFunded = vestingFunding[_vestingId];
return totalFunded >= totalRequired;
}
/**
* @notice Get the vesting information
* @param _vestingId - Identifier of the vesting
* @return vesting - Vesting information
*/
function getVestingInfo(
bytes32 _vestingId
) external view returns (TokenVestingLib.Vesting memory) {
return vestingById[_vestingId];
}
/**
* @notice Get the vested amount for a Vesting, at a given timestamp.
* @param _vestingId The vesting ID
* @param _referenceTimestamp Timestamp for which we're calculating
*/
function getVestedAmount(
bytes32 _vestingId,
uint32 _referenceTimestamp
) external view returns (uint256) {
TokenVestingLib.Vesting memory vesting = vestingById[_vestingId];
return vesting.calculateVestedAmount(_referenceTimestamp);
}
/**
* @notice Get the claimable amount for a vesting
* @param _vestingId - Identifier of the vesting
* @return claimable - The amount of tokens that can be claimed at the current time
*/
function getClaimableAmount(
bytes32 _vestingId
) external view returns (uint256 claimable) {
TokenVestingLib.Vesting storage vesting = vestingById[_vestingId];
if (vesting.recipient == address(0)) revert Errors.EmptyVesting();
// If timelock is active, nothing can be claimed
if (vesting.timelock > uint32(block.timestamp)) return 0;
// Calculate vested amount as of now
uint256 vestedAmount = vesting.calculateVestedAmount(
uint32(block.timestamp)
);
// Calculate claimable amount (vested - already claimed)
claimable = vestedAmount > vesting.claimedAmount
? vestedAmount - vesting.claimedAmount
: 0;
// For partial funding, ensure we don't return more than what's funded
if (FUNDING_TYPE == ITypes.FundingType.Partial) {
uint256 currentFunding = vestingFunding[_vestingId];
// If funding is less than what's vested, adjust claimable
if (currentFunding < vestedAmount) {
claimable = currentFunding > vesting.claimedAmount
? currentFunding - vesting.claimedAmount
: 0;
}
}
return claimable;
}
/**
* @notice Get all recipients
* @return recipients - The list of recipients
*/
function getAllRecipients() external view returns (address[] memory) {
return recipients;
}
/**
* @notice Get the length of all recipients
* @return length - The length of all recipients
*/
function getAllRecipientsLength() external view returns (uint256) {
return recipients.length;
}
/**
* @notice Get all recipients in a range
* @param _from - The start index (inclusive)
* @param _to - The end index (exclusive)
* @return recipientsSliced - The list of recipients in the range
*/
function getAllRecipientsSliced(
uint256 _from,
uint256 _to
) external view returns (address[] memory) {
if (_from >= _to || _to > recipients.length)
revert Errors.InvalidRange();
address[] memory recipientsSliced = new address[](_to - _from);
for (uint256 i = _from; i < _to; ++i)
recipientsSliced[i - _from] = recipients[i];
return recipientsSliced;
}
/**
* @notice Get all vestings for a recipient
* @param _recipient - The recipient address
* @return recipientVestings - The list of vestings for the recipient
*/
function getAllRecipientVestings(
address _recipient
) external view returns (bytes32[] memory) {
return recipientVestings[_recipient];
}
/**
* @notice Get all vestings for a recipient in a range
* @param _from - The start index (inclusive)
* @param _to - The end index (exclusive)
* @param _recipient - The recipient address
* @return recipientVestingsSliced - The list of vestings for the recipient in the range
*/
function getAllRecipientVestingsSliced(
uint256 _from,
uint256 _to,
address _recipient
) external view returns (bytes32[] memory) {
if (_recipient == address(0)) revert Errors.InvalidAddress();
if (_from >= _to || _to > recipientVestings[_recipient].length)
revert Errors.InvalidRange();
bytes32[] memory recipientVestingsSliced = new bytes32[](_to - _from);
for (uint256 i = _from; i < _to; ++i) {
recipientVestingsSliced[i - _from] = recipientVestings[_recipient][
i
];
}
return recipientVestingsSliced;
}
/**
* @notice Get the length of all vestings for a recipient
* @param _recipient - The recipient address
* @return length - The length of all vestings for the recipient
*/
function getAllRecipientVestingsLength(
address _recipient
) external view returns (uint256) {
if (_recipient == address(0)) revert Errors.InvalidAddress();
return recipientVestings[_recipient].length;
}
/**
* @notice Get the pending owner for a vesting transfer
* @param _vestingId The ID of the vesting
* @return The address of the pending owner if there is one, or zero address
*/
function getPendingVestingTransfer(
bytes32 _vestingId
) external view returns (address) {
return pendingVestingTransfers[_vestingId];
}
/**
* @notice Amount of tokens available to withdraw by the admin
* @return The amount of tokens available to withdraw
*/
function amountAvailableToWithdrawByAdmin() public view returns (uint256) {
return
address(this).balance -
numTokensReservedForVesting -
numTokensReservedForFees;
}
/**
* @notice Check if a recipient has vestings
* @param recipient - The recipient address
* @return isRecipient - True if the recipient has vestings
*/
function isRecipient(address recipient) external view returns (bool) {
return _isRecipient(recipient);
}
/**
* @dev Internal function to create a new vesting
* @param params - Vesting parameters
*/
function _createVesting(
TokenVestingLib.VestingParams memory params
) internal {
if (params._recipient == address(0)) revert Errors.InvalidAddress();
if (
params._linearVestAmount +
params._initialUnlock +
params._cliffAmount ==
0
) revert Errors.InvalidVestedAmount();
if (params._startTimestamp == 0) revert Errors.InvalidStartTimestamp();
if (params._startTimestamp > params._endTimestamp)
revert Errors.InvalidEndTimestamp();
if (
params._startTimestamp == params._endTimestamp &&
params._linearVestAmount > 0
) revert Errors.InvalidEndTimestamp();
if (params._releaseIntervalSecs == 0)
revert Errors.InvalidReleaseInterval();
if (params._cliffReleaseTimestamp == 0) {
if (params._cliffAmount != 0) revert Errors.InvalidCliffAmount();
if (
(params._endTimestamp - params._startTimestamp) %
params._releaseIntervalSecs !=
0
) revert Errors.InvalidIntervalLength();
} else {
// Cliff release is set but amount can be zero
if (
((params._startTimestamp > params._cliffReleaseTimestamp) ||
(params._cliffReleaseTimestamp >= params._endTimestamp))
) revert Errors.InvalidCliffRelease();
if (
(params._endTimestamp - params._cliffReleaseTimestamp) %
params._releaseIntervalSecs !=
0
) revert Errors.InvalidIntervalLength();
}
TokenVestingLib.Vesting memory vesting = TokenVestingLib.Vesting({
recipient: params._recipient,
startTimestamp: params._startTimestamp,
endTimestamp: params._endTimestamp,
deactivationTimestamp: 0,
timelock: params._timelock,
initialUnlock: params._initialUnlock,
cliffReleaseTimestamp: params._cliffReleaseTimestamp,
cliffAmount: params._cliffAmount,
releaseIntervalSecs: params._releaseIntervalSecs,
linearVestAmount: params._linearVestAmount,
claimedAmount: 0,
isRevocable: params._isRevocable
});
if (!_isRecipient(params._recipient)) {
// Add the recipient to the array and update the index mapping
recipients.push(params._recipient);
recipientToIndex[params._recipient] = recipients.length; // Store index+1
}
vestingById[params._vestingId] = vesting;
recipientVestings[params._recipient].push(params._vestingId);
// Update the vesting index mapping for efficient removal (store index+1 to distinguish from 0)
vestingToRecipientIndex[params._recipient][
params._vestingId
] = recipientVestings[params._recipient].length;
emit VestingCreated(params._vestingId, params._recipient, vesting);
}
/**
* @dev Internal function to claim vested tokens
* @param _vestingId - Identifier of the vesting
*/
function _claim(bytes32 _vestingId) internal {
TokenVestingLib.Vesting storage vesting = vestingById[_vestingId];
if (vesting.recipient == address(0)) revert Errors.EmptyVesting();
if (vesting.timelock > uint32(block.timestamp))
revert Errors.TimelockEnabled();
uint256 vested = vesting.calculateVestedAmount(uint32(block.timestamp));
uint256 claimable = vested - vesting.claimedAmount;
if (claimable == 0) revert Errors.InsufficientBalance();
// If partial funding is enabled, check if there's enough funding
if (FUNDING_TYPE == ITypes.FundingType.Partial) {
uint256 currentFunding = vestingFunding[_vestingId];
// Check if there's enough funding for the claim
if (currentFunding < vested) {
// If not enough funding, adjust claimable to what's available
claimable = currentFunding > vesting.claimedAmount
? currentFunding - vesting.claimedAmount
: 0;
if (claimable == 0) revert Errors.InsufficientFunding();
}
}
vesting.claimedAmount += claimable;
numTokensReservedForVesting -= claimable;
emit Claimed(_vestingId, vesting.recipient, claimable);
// Send ETH to recipient
(bool success, ) = vesting.recipient.call{ value: claimable }("");
if (!success) revert Errors.TransferFailed();
}
/**
* @dev Internal function to revoke a vesting
* @param _vestingId - Vesting Identifier
*/
function _revokeVesting(
bytes32 _vestingId
) internal isVestingActive(_vestingId) {
TokenVestingLib.Vesting storage vesting = vestingById[_vestingId];
if (block.timestamp >= vesting.endTimestamp)
revert Errors.FullyVested();
if (!vesting.isRevocable) revert Errors.VestingNotRevocable();
uint256 vestedAmountNow = vesting.calculateVestedAmount(
uint32(block.timestamp)
);
uint256 finalVestAmount = vesting.calculateVestedAmount(
vesting.endTimestamp
);
uint256 amountRemaining = finalVestAmount - vestedAmountNow;
// In partial funding mode, we need to adjust the amount based on what was actually funded
if (FUNDING_TYPE == ITypes.FundingType.Partial) {
uint256 totalFunded = vestingFunding[_vestingId];
// If there's not enough funding to cover what's already vested, we need to adjust
if (totalFunded <= vestedAmountNow) {
// All funded tokens are already vested, nothing to release
amountRemaining = 0;
} else {
// Only release what's actually funded and not yet vested
amountRemaining = totalFunded - vestedAmountNow;
}
}
vesting.deactivationTimestamp = uint32(block.timestamp);
numTokensReservedForVesting -= amountRemaining;
emit VestingRevoked(_vestingId, amountRemaining, vesting);
}
/**
* @dev Helper function to remove a vesting ID from a recipient's array
* @param _recipient The address of the recipient
* @param _vestingId The ID of the vesting to remove
*/
function _removeVestingFromRecipient(
address _recipient,
bytes32 _vestingId
) internal {
bytes32[] storage vestingIds = recipientVestings[_recipient];
uint256 indexPlusOne = vestingToRecipientIndex[_recipient][_vestingId];
if (indexPlusOne == 0) return; // Vesting not found
uint256 index = indexPlusOne - 1;
uint256 lastIndex = vestingIds.length - 1;
// If this is not the last element, move the last element to this position
if (index != lastIndex) {
bytes32 lastVestingId = vestingIds[lastIndex];
vestingIds[index] = lastVestingId;
// Update the index mapping for the moved element
vestingToRecipientIndex[_recipient][lastVestingId] = index + 1;
}
// Remove the last element and clear the mapping
vestingIds.pop();
delete vestingToRecipientIndex[_recipient][_vestingId];
// If recipient has no more vestings, remove from recipients array
if (vestingIds.length == 0) _removeRecipient(_recipient);
}
/**
* @dev Helper function to remove a recipient from the recipients array
* @param _recipient The address of the recipient to remove
*/
function _removeRecipient(address _recipient) internal {
uint256 indexPlusOne = recipientToIndex[_recipient];
if (indexPlusOne == 0) return; // Not in array
uint256 index = indexPlusOne - 1; // Convert from index+1 to zero-based index
uint256 lastIndex = recipients.length - 1;
// If this is not the last element, move the last element to this position
if (index != lastIndex) {
address lastRecipient = recipients[lastIndex];
recipients[index] = lastRecipient;
recipientToIndex[lastRecipient] = index + 1; // Update index+1 for the moved element
}
// Remove the last element and clear the mapping
recipients.pop();
delete recipientToIndex[_recipient];
}
/// @dev Internal function to check if an address is a recipient
/// @param recipient The address to check
/// @return True if the address is a recipient, false otherwise
function _isRecipient(address recipient) internal view returns (bool) {
if (recipient == address(0)) revert Errors.InvalidAddress();
return recipientToIndex[recipient] != 0;
}
/**
* @dev Internal function to check if all array lengths in batch params match the expected length
* @param params - Batch parameters to validate
* @param expectedLength - The expected length all arrays should match
*/
function _checkArrayLengthMismatch(
CreateVestingBatchParams calldata params,
uint256 expectedLength
) internal pure {
if (
params._startTimestamps.length != expectedLength ||
params._endTimestamps.length != expectedLength ||
params._timelocks.length != expectedLength ||
params._initialUnlocks.length != expectedLength ||
params._cliffAmounts.length != expectedLength ||
params._cliffReleaseTimestamps.length != expectedLength ||
params._releaseIntervalSecs.length != expectedLength ||
params._linearVestAmounts.length != expectedLength ||
params._isRevocables.length != expectedLength
) {
revert Errors.ArrayLengthMismatch();
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
interface ITypes {
enum FeeType {
Gas,
DistributionToken
}
enum FundingType {
Full,
Partial
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import { ITypes } from "contracts/interfaces/ITypes.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { Errors } from "contracts/libraries/Errors.sol";
/// @title FeeManager
/// @notice Base contract to handle fee management for factory contracts
abstract contract FactoryFeeManager is Ownable {
/// @notice Fee details for a specific campaign creator
struct CustomFee {
bool enabled;
ITypes.FeeType preferredFeeType;
uint256 gasFee;
uint256 tokenFee;
}
uint256 public constant BASIS_POINTS = 10000;
uint256 public defaultGasFee;
uint256 public defaultTokenFee;
address public feeCollector;
ITypes.FeeType public defaultFeeType = ITypes.FeeType.Gas;
mapping(address campaignCreator => CustomFee customFee)
internal _customFees;
/// @dev Emitted when a new fee collector is set
event FeeCollectorSet(address admin, address indexed newFeeCollector);
/// @dev Emitted when the default gas fee is updated
event SetDefaultGasFee(address admin, uint256 oldGasFee, uint256 newGasFee);
/// @dev Emitted when the default token fee is updated
event SetDefaultTokenFee(
address admin,
uint256 oldTokenFee,
uint256 newTokenFee
);
/// @dev Emitted when the default fee type is changed
event ChangeDefaultFeeType(address admin, ITypes.FeeType newFeeType);
/// @dev Emitted when a custom fee is set for a campaign creator
event SetCustomFee(
address admin,
bool enabled,
address indexed campaignCreator,
ITypes.FeeType feeType,
uint256 gasFee,
uint256 tokenFee
);
constructor(
address feeCollector_,
uint256 defaultGasFee_,
uint256 defaultTokenFee_
) {
if (feeCollector_ == address(0)) revert Errors.InvalidAddress();
if (defaultTokenFee_ > BASIS_POINTS) revert Errors.FeeTooHigh();
feeCollector = feeCollector_;
defaultGasFee = defaultGasFee_;
defaultTokenFee = defaultTokenFee_;
}
//================================================
// SETTERS
//================================================
function setFeeCollector(address newFeeCollector) external onlyOwner {
if (newFeeCollector == address(0)) revert Errors.InvalidAddress();
feeCollector = newFeeCollector;
emit FeeCollectorSet({
admin: msg.sender,
newFeeCollector: newFeeCollector
});
}
function setDefaultGasFee(uint256 newGasFee) external onlyOwner {
uint256 oldGasFee = defaultGasFee;
defaultGasFee = newGasFee;
emit SetDefaultGasFee({
admin: msg.sender,
oldGasFee: oldGasFee,
newGasFee: newGasFee
});
}
function setDefaultTokenFee(uint256 newTokenFee) external onlyOwner {
if (newTokenFee > BASIS_POINTS) revert Errors.FeeTooHigh();
uint256 oldTokenFee = defaultTokenFee;
defaultTokenFee = newTokenFee;
emit SetDefaultTokenFee({
admin: msg.sender,
oldTokenFee: oldTokenFee,
newTokenFee: newTokenFee
});
}
function setDefaultFeeType(ITypes.FeeType feeType) external onlyOwner {
defaultFeeType = feeType;
emit ChangeDefaultFeeType({ admin: msg.sender, newFeeType: feeType });
}
function resetGasFee() external onlyOwner {
uint256 oldGasFee = defaultGasFee;
defaultGasFee = 0;
emit SetDefaultGasFee({
admin: msg.sender,
oldGasFee: oldGasFee,
newGasFee: 0
});
}
function resetTokenFee() external onlyOwner {
uint256 oldTokenFee = defaultTokenFee;
defaultTokenFee = 0;
emit SetDefaultTokenFee({
admin: msg.sender,
oldTokenFee: oldTokenFee,
newTokenFee: 0
});
}
function setCustomFee(
address campaignCreator,
ITypes.FeeType feeType,
uint256 gasFee,
uint256 tokenFee
) external onlyOwner {
if (campaignCreator == address(0)) revert Errors.InvalidAddress();
if (tokenFee > BASIS_POINTS) revert Errors.FeeTooHigh();
_customFees[campaignCreator] = CustomFee({
enabled: true,
preferredFeeType: feeType,
gasFee: gasFee,
tokenFee: tokenFee
});
emit SetCustomFee({
admin: msg.sender,
enabled: true,
campaignCreator: campaignCreator,
feeType: feeType,
gasFee: gasFee,
tokenFee: tokenFee
});
}
function disableCustomFee(address campaignCreator) external onlyOwner {
if (campaignCreator == address(0)) revert Errors.InvalidAddress();
if (!_customFees[campaignCreator].enabled)
revert Errors.CustomFeeNotSet();
delete _customFees[campaignCreator];
emit SetCustomFee({
admin: msg.sender,
enabled: false,
campaignCreator: campaignCreator,
feeType: ITypes.FeeType.Gas,
gasFee: 0,
tokenFee: 0
});
}
//================================================
// GETTERS
//================================================
function getCustomFee(
address campaignCreator
) external view returns (CustomFee memory) {
return _customFees[campaignCreator];
}
//================================================
// INTERNAL FUNCTIONS
//================================================
function _getFee(address campaignCreator) internal view returns (uint256) {
CustomFee memory customFee = _customFees[campaignCreator];
if (customFee.enabled) {
if (
customFee.preferredFeeType == ITypes.FeeType.DistributionToken
) {
return customFee.tokenFee;
} else {
return customFee.gasFee;
}
}
return
defaultFeeType == ITypes.FeeType.Gas
? defaultGasFee
: defaultTokenFee;
}
function _getFeeType(
address campaignCreator
) internal view returns (ITypes.FeeType) {
CustomFee memory customFee = _customFees[campaignCreator];
return customFee.enabled ? customFee.preferredFeeType : defaultFeeType;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import "@openzeppelin/contracts/utils/Context.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import { Errors } from "contracts/libraries/Errors.sol";
/**
@title Access Limiter to multiple owner-specified accounts.
@dev Exposes the onlyAdmin modifier, which will revert (AdminAccessRequired) if the caller is not the owner nor the admin.
@notice An address with the role admin can grant that role to or revoke that role from any address via the function setAdmin().
*/
abstract contract AccessProtected is Context {
mapping(address => bool) private _admins; // user address => admin? mapping
uint256 public adminCount;
event AdminAccessSet(address indexed _admin, bool _enabled);
constructor() {
_admins[_msgSender()] = true;
adminCount = 1;
emit AdminAccessSet(_msgSender(), true);
}
/**
* Throws if called by any account that isn't an admin or an owner.
*/
modifier onlyAdmin() {
if (!_admins[_msgSender()]) revert Errors.AdminAccessRequired();
_;
}
function isAdmin(address _addressToCheck) external view returns (bool) {
return _admins[_addressToCheck];
}
/**
* @notice Set/unset Admin Access for a given address.
*
* @param admin - Address of the new admin (or the one to be removed)
* @param isEnabled - Enable/Disable Admin Access
*/
function setAdmin(address admin, bool isEnabled) public onlyAdmin {
if (admin == address(0)) revert Errors.InvalidAddress();
if (_admins[admin] == isEnabled)
revert Errors.AdminStatusAlreadyActive();
if (isEnabled) {
adminCount++;
} else {
if (adminCount <= 1) revert Errors.CannotRemoveLastAdmin();
adminCount--;
}
_admins[admin] = isEnabled;
emit AdminAccessSet(admin, isEnabled);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import { Errors } from "contracts/libraries/Errors.sol";
library TokenVestingLib {
/**
* @notice A structure representing a Vesting - supporting linear and cliff vesting.
* @param releaseIntervalSecs used for calculating the vested amount
* @param linearVestAmount vesting allocation, excluding cliff
* @param claimedAmount claimed so far, excluding cliff
*/
struct Vesting {
address recipient; // 160 bits 160/256 slot space - 1st slot
uint32 startTimestamp; // 32 bits 192/256 slot space - 1st slot
uint32 endTimestamp; // 32 bits 224/256 slot space - 1st slot
uint32 deactivationTimestamp; // 32 bits 256/256 slot space - 1st slot
uint32 timelock; // 32 bits 32/256 slot space - 2nd slot
uint32 releaseIntervalSecs; // 32 bits 64/256 slot space - 2nd slot
uint32 cliffReleaseTimestamp; // 32 bits 96/256 slot space - 2nd slot
uint256 initialUnlock; // 256 bits 256/256 slot space - 3nd slot
uint256 cliffAmount; // 256 bits 256/256 slot space - 4nd slots
uint256 linearVestAmount; // 256 bits 256/256 slot space - 5th slot
uint256 claimedAmount; // 256 bits 256/256 slot space - 6th slot
bool isRevocable; // Flag to determine if vesting can be revoked
}
/**
* @notice A structure representing a Vesting - supporting linear and cliff vesting.
* @param _vestingId The ID of the vesting
* @param _recipient The recipient of the vesting
* @param _startTimestamp The start timestamp of the vesting
* @param _endTimestamp The end timestamp of the vesting
* @param _timelock The timelock period for the vesting
* @param _initialUnlock The initial unlock amount for the vesting
* @param _cliffReleaseTimestamp The cliff release timestamp for the vesting
* @param _cliffAmount The cliff amount for the vesting
* @param _releaseIntervalSecs The release interval in seconds for the vesting
* @param _linearVestAmount The linear vest amount for the vesting
* @param _isRevocable Flag to determine if vesting can be revoked
*/
struct VestingParams {
bytes32 _vestingId;
address _recipient;
uint32 _startTimestamp;
uint32 _endTimestamp;
uint32 _timelock;
uint256 _initialUnlock;
uint32 _cliffReleaseTimestamp;
uint256 _cliffAmount;
uint32 _releaseIntervalSecs;
uint256 _linearVestAmount;
bool _isRevocable;
}
/**
* @notice Calculate the vested amount for a given Vesting, at a given timestamp.
* @param _vesting The vesting in question
* @param _referenceTimestamp Timestamp for which we're calculating
*/
function calculateVestedAmount(
Vesting memory _vesting,
uint32 _referenceTimestamp
) internal pure returns (uint256) {
// Does the Vesting exist?
if (_vesting.deactivationTimestamp != 0) {
if (_referenceTimestamp > _vesting.deactivationTimestamp) {
_referenceTimestamp = _vesting.deactivationTimestamp;
}
}
uint256 vestingAmount;
// Has the Vesting ended?
if (_referenceTimestamp > _vesting.endTimestamp) {
_referenceTimestamp = _vesting.endTimestamp;
}
// Has the start passed?
if (_referenceTimestamp >= _vesting.startTimestamp) {
vestingAmount += _vesting.initialUnlock;
}
// Has the cliff passed?
if (_referenceTimestamp >= _vesting.cliffReleaseTimestamp) {
vestingAmount += _vesting.cliffAmount;
}
// Has the vesting started? If so, calculate the vested amount linearly
uint256 startTimestamp;
if (_vesting.cliffReleaseTimestamp != 0) {
startTimestamp = _vesting.cliffReleaseTimestamp;
} else {
startTimestamp = _vesting.startTimestamp;
}
if (_referenceTimestamp > startTimestamp) {
uint256 currentVestingDurationSecs = _referenceTimestamp -
startTimestamp;
// Round to releaseIntervalSecs
uint256 truncatedCurrentVestingDurationSecs = (currentVestingDurationSecs /
_vesting.releaseIntervalSecs) *
_vesting.releaseIntervalSecs;
uint256 finalVestingDurationSecs = _vesting.endTimestamp -
startTimestamp;
// Calculate vested amount
uint256 linearVestAmount = (_vesting.linearVestAmount *
truncatedCurrentVestingDurationSecs) / finalVestingDurationSecs;
vestingAmount += linearVestAmount;
}
return vestingAmount;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
/**
* @title Errors
* @notice Central library for all custom errors used across the TokenOps vesting contracts
*/
library Errors {
// ============== Access Control Errors ==============
/// @notice Thrown when an operation requires admin access but caller is not an admin
error AdminAccessRequired();
/// @notice Thrown when an operation requires fee collector access but caller is not the fee collector
error NotFeeCollector();
/// @notice Thrown when an operation requires vesting ownership but caller is not the owner
error NotVestingOwner();
/// @notice Thrown when an operation requires milestone ownership but caller is not the owner
error NotMilestoneOwner();
/// @notice Thrown when an operation requires transfer authorization but caller is not authorized
error NotAuthorizedForTransfer();
/// @notice Thrown when a vault operation is attempted by an unauthorized address
error VaultUnauthorized();
/// @notice Thrown when an operation requires at least one admin but would leave none
error CannotRemoveLastAdmin();
// ============== Input Validation Errors ==============
/// @notice Thrown when an invalid address (typically zero address) is provided
error InvalidAddress();
/// @notice Thrown when a range of values is invalid
error InvalidRange();
/// @notice Thrown when arrays in a function call don't have the same length
error ArrayLengthMismatch();
/// @notice Thrown when an empty array is provided but non-empty is required
error EmptyArray();
/// @notice Thrown when a flag is already set with the same value for an address
error AdminStatusAlreadyActive();
/// @notice Thrown when an invalid token address is provided
error InvalidToken();
/// @notice Thrown when an invalid step index is provided
error InvalidStepIndex();
// ============== Fee-Related Errors ==============
/// @notice Thrown when a fee is below the minimum required
error FeeTooLow();
/// @notice Thrown when a fee exceeds the maximum allowed
error FeeTooHigh();
/// @notice Thrown when insufficient fees are provided
error InsufficientFee();
/// @notice Thrown when a custom fee is not set for an address
error CustomFeeNotSet();
// ============== Token Operation Errors ==============
/// @notice Thrown when a transfer operation fails
error TransferFailed();
/// @notice Thrown when there's insufficient balance for an operation
error InsufficientBalance();
/// @notice Thrown when an invalid funding amount is provided
error InvalidFundingAmount();
/// @notice Thrown when trying to exceed a funding limit
error FundingLimitExceeded();
/// @notice Thrown when a vesting is fully funded and additional funding is attempted
error VestingFullyFunded();
/// @notice Thrown when insufficient funding is provided
error InsufficientFunding();
/// @notice Thrown when an operation would delegate to a zero address
error VaultZeroAddressDelegate();
// ============== Vault-Related Errors ==============
/// @notice Thrown when a vault is already initialized
error VaultAlreadyInitialized();
/// @notice Thrown when vault deployment fails
error VaultDeploymentFailed();
/// @notice Thrown when vault initialization fails
error VaultInitializationFailed();
// ============== Vesting State Errors ==============
/// @notice Thrown when a vesting is empty (not initialized)
error EmptyVesting();
/// @notice Thrown when a vesting is not active
error VestingNotActive();
/// @notice Thrown when a vesting is fully vested
error FullyVested();
/// @notice Thrown when a vesting is not revocable but revocation is attempted
error VestingNotRevocable();
/// @notice Thrown when a timelock is enabled but an operation would violate it
error TimelockEnabled();
// ============== Vesting Parameter Errors ==============
/// @notice Thrown when an invalid vested amount is provided
error InvalidVestedAmount();
/// @notice Thrown when an invalid start timestamp is provided
error InvalidStartTimestamp();
/// @notice Thrown when an invalid end timestamp is provided
error InvalidEndTimestamp();
/// @notice Thrown when an invalid release interval is provided
error InvalidReleaseInterval();
/// @notice Thrown when an invalid interval length is provided
error InvalidIntervalLength();
/// @notice Thrown when an invalid cliff release timestamp is provided
error InvalidCliffRelease();
/// @notice Thrown when an invalid cliff release timestamp is provided
error InvalidCliffReleaseTimestamp();
/// @notice Thrown when an invalid cliff amount is provided
error InvalidCliffAmount();
/// @notice Thrown when an invalid unlock timestamp is provided
error InvalidUnlockTimestamp();
// ============== Transfer Ownership Errors ==============
/// @notice Thrown when no pending transfer exists but one is expected
error NoPendingTransfer();
/// @notice Thrown when a pending transfer exists but none is expected
error PendingTransferExists();
// ============== Milestone State Errors ==============
/// @notice Thrown when a milestone with the same ID already exists
error MilestoneAlreadyExists(bytes32 milestoneId);
/// @notice Thrown when a milestone doesn't exist
error MilestoneNotExists();
/// @notice Thrown when a milestone is not active
error MilestoneNotActive();
/// @notice Thrown when a milestone is already revoked
error MilestoneAlreadyRevoked();
/// @notice Thrown when a milestone is revoked but operation assumes it's active
error MilestoneIsRevoked();
/// @notice Thrown when a step is already approved but approval is attempted again
error StepAlreadyApproved();
/// @notice Thrown when a step is already revoked but revocation is attempted again
error StepAlreadyRevoked();
/// @notice Thrown when a step needs to be approved but isn't
error StepNotApproved();
/// @notice Thrown when a milestone is fully funded but additional funding is attempted
error MilestoneFullyFunded();
/// @notice Thrown when a milestone step is fully funded but additional funding is attempted
error StepFullyFunded();
/// @notice Thrown when a start timestamp is not reached but operation requires it
error StartTimestampNotReached();
/// @notice Thrown when a vesting has already ended but operation assumes it's active
error VestingAlreadyEnded();
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import "contracts/libraries/TokenVestingLib.sol";
import "contracts/interfaces/ITypes.sol";
interface INativeTokenVestingManager {
/// @notice Parameters for vesting creation with a predefined ID
struct VestingCreationParams {
bytes32 vestingId;
address recipient;
uint32 startTimestamp;
uint32 endTimestamp;
uint32 timelock;
uint256 initialUnlock;
uint32 cliffReleaseTimestamp;
uint256 cliffAmount;
uint32 releaseIntervalSecs;
uint256 linearVestAmount;
bool isRevocable;
}
/// @notice Parameters for batch creation of vesting schedules
/// @dev Arrays must be of equal length
struct CreateVestingBatchParams {
address[] _recipients;
uint32[] _startTimestamps;
uint32[] _endTimestamps;
uint32[] _timelocks;
uint256[] _initialUnlocks;
uint32[] _cliffReleaseTimestamps;
uint256[] _cliffAmounts;
uint32[] _releaseIntervalSecs;
uint256[] _linearVestAmounts;
bool[] _isRevocables;
}
/// @notice Emitted when an admin withdraws tokens not tied up in vesting
/// @param recipient Address of the recipient (admin) making the withdrawal
/// @param amountRequested Amount of tokens withdrawn by the admin
event AdminWithdrawn(address indexed recipient, uint256 amountRequested);
/// @notice Emitted when a claim is made by a vesting recipient
/// @param vestingId Unique identifier of the recipient's vesting arrangement
/// @param recipient Address of the recipient making the claim
/// @param withdrawalAmount Amount of tokens withdrawn in the claim
event Claimed(
bytes32 indexed vestingId,
address indexed recipient,
uint256 withdrawalAmount
);
/// @notice Emitted when new fee collector is set
/// @param oldFeeCollector Address of the previous fee collector
/// @param newFeeCollector Address of the new fee collector
event FeeCollectorUpdated(
address indexed oldFeeCollector,
address indexed newFeeCollector
);
/// @notice Emitted when gas fees (ETH) are withdrawn by the fee collector
/// @param recipient Address receiving the withdrawn fees
/// @param amount Amount of ETH withdrawn
event GasFeeWithdrawn(address indexed recipient, uint256 amount);
/// @notice Emitted when a new vesting is created
/// @param vestingId Unique identifier for the vesting
/// @param recipient Address of the vesting recipient
/// @param vesting Details of the created vesting
event VestingCreated(
bytes32 indexed vestingId,
address indexed recipient,
TokenVestingLib.Vesting vesting
);
/// @notice Emitted when a vesting is funded or additionally funded
/// @param vestingId Unique identifier of the vesting
/// @param funder Address of the account funding the vesting
/// @param amount Amount of tokens added to the vesting
/// @param totalFunded Total amount funded for this vesting so far
/// @param totalRequired Total amount required by the vesting
event VestingFunded(
bytes32 indexed vestingId,
address indexed funder,
uint256 amount,
uint256 totalFunded,
uint256 totalRequired
);
/// @notice Emitted when a vesting is revoked
/// @param vestingId Identifier of the revoked vesting
/// @param numTokensWithheld Amount of tokens withheld during the revocation
/// @param vesting Details of the revoked vesting
event VestingRevoked(
bytes32 indexed vestingId,
uint256 numTokensWithheld,
TokenVestingLib.Vesting vesting
);
/// @notice Emitted when a vesting ownership is transferred
/// @param previousOwner Address of the previous vesting owner
/// @param newOwner Address of the new vesting owner
/// @param vestingId Unique identifier of the transferred vesting
event VestingTransferred(
address indexed previousOwner,
address indexed newOwner,
bytes32 indexed vestingId
);
/// @notice Emitted when a vesting transfer is initiated
/// @param currentOwner Address of the current vesting owner
/// @param newOwner Address of the proposed new vesting owner
/// @param vestingId Unique identifier of the vesting to be transferred
event VestingTransferInitiated(
address indexed currentOwner,
address indexed newOwner,
bytes32 indexed vestingId
);
/// @notice Emitted when a vesting transfer is cancelled
/// @param currentOwner Address of the current vesting owner
/// @param vestingId Unique identifier of the vesting transfer that was cancelled
event VestingTransferCancelled(
address indexed currentOwner,
bytes32 indexed vestingId
);
/// @notice The block number when this contract was deployed
function DEPLOYMENT_BLOCK_NUMBER() external view returns (uint256);
/// @notice The fee percentage charged for vesting operations
function FEE() external view returns (uint256);
/// @notice The type of fee (flat, percentage, etc)
function FEE_TYPE() external view returns (ITypes.FeeType);
/// @notice The funding type for vestings (full or partial)
function FUNDING_TYPE() external view returns (ITypes.FundingType);
/// @notice Complete a vesting transfer by accepting it as the new owner
/// @param _vestingId The ID of the vesting to accept
function acceptVestingTransfer(bytes32 _vestingId) external;
/// @notice Allows an admin to claim vested tokens on behalf of a recipient
/// @param _vestingId Unique identifier of the vesting arrangement
function adminClaim(bytes32 _vestingId) external payable;
/// @notice Allows an admin to claim vested tokens for multiple vesting arrangements
/// @param _vestingIds Array of vesting identifiers to claim
function batchAdminClaim(bytes32[] memory _vestingIds) external payable;
/// @notice Revokes multiple vesting arrangements in batch
/// @param _vestingIds Array of vesting identifiers to revoke
function batchRevokeVestings(bytes32[] memory _vestingIds) external;
/// @notice Cancels a pending vesting transfer
/// @param _vestingId The ID of the vesting with a pending transfer
function cancelVestingTransfer(bytes32 _vestingId) external;
/// @notice Allows a recipient to claim their vested tokens
/// @param _vestingId Unique identifier of the recipient's vesting arrangement
function claim(bytes32 _vestingId) external payable;
/// @notice Create a vesting schedule for a recipient
/// @param _recipient Address of the recipient for whom vesting is being created
/// @param _startTimestamp Start time of the vesting period as a timestamp
/// @param _endTimestamp End time of the vesting period as a timestamp
/// @param _timelock Period during which the tokens are locked and cannot be claimed
/// @param _initialUnlock Amount of tokens that are initially unlocked and claimable at the start time
/// @param _cliffReleaseTimestamp Timestamp after which the cliff amount can be released
/// @param _cliffAmount Amount of tokens that are released at once after the cliff period is reached
/// @param _releaseIntervalSecs Interval in seconds between subsequent releases
/// @param _linearVestAmount Total amount of tokens that will be vested linearly after the cliff
/// @param _isRevocable Whether the vesting can be revoked by the admin
function createVesting(
address _recipient,
uint32 _startTimestamp,
uint32 _endTimestamp,
uint32 _timelock,
uint256 _initialUnlock,
uint32 _cliffReleaseTimestamp,
uint256 _cliffAmount,
uint32 _releaseIntervalSecs,
uint256 _linearVestAmount,
bool _isRevocable
) external payable;
/// @notice Create vesting schedules in batch for multiple recipients
/// @param params Struct containing arrays of parameters for each vesting schedule
function createVestingBatch(
CreateVestingBatchParams memory params
) external payable;
/// @notice Direct transfer of vesting ownership
/// @param _vestingId The ID of the vesting to transfer
/// @param _newOwner The address of the new owner
/// @dev This is specifically for compatibility with contracts that cannot call acceptVestingTransfer
function directVestingTransfer(
bytes32 _vestingId,
address _newOwner
) external;
/// @notice Returns the address of the current fee collector
/// @return Address of the fee collector
function feeCollector() external view returns (address);
/// @notice Adds funding to a vesting schedule
/// @param _vestingId The identifier of the vesting to fund
function fundVesting(bytes32 _vestingId) external payable;
/// @notice Adds funding to multiple vesting schedules in batch
/// @param _vestingIds Array of vesting identifiers to fund
/// @param _fundingAmounts Array of funding amounts for each vesting
function fundVestingBatch(
bytes32[] memory _vestingIds,
uint256[] memory _fundingAmounts
) external payable;
/// @notice Get all vesting IDs for a specific recipient
/// @param _recipient Address of the recipient
/// @return Array of vesting IDs belonging to the recipient
function getAllRecipientVestings(
address _recipient
) external view returns (bytes32[] memory);
/// @notice Get the number of vestings for a specific recipient
/// @param _recipient Address of the recipient
/// @return Number of vestings for the recipient
function getAllRecipientVestingsLength(
address _recipient
) external view returns (uint256);
/// @notice Get a slice of vesting IDs for a specific recipient
/// @param _from Start index (inclusive)
/// @param _to End index (exclusive)
/// @param _recipient Address of the recipient
/// @return Array of vesting IDs in the specified range
function getAllRecipientVestingsSliced(
uint256 _from,
uint256 _to,
address _recipient
) external view returns (bytes32[] memory);
/// @notice Fetches a list of all recipient addresses who have at least one vesting schedule
/// @return An array of addresses, each representing a recipient with an active or historical vesting schedule
function getAllRecipients() external view returns (address[] memory);
/// @notice Get the total number of recipients
/// @return Number of recipients
function getAllRecipientsLength() external view returns (uint256);
/// @notice Get a slice of recipient addresses
/// @param _from Start index (inclusive)
/// @param _to End index (exclusive)
/// @return Array of recipient addresses in the specified range
function getAllRecipientsSliced(
uint256 _from,
uint256 _to
) external view returns (address[] memory);
/// @notice Get the amount of tokens that can be claimed from a vesting
/// @param _vestingId The identifier of the vesting
/// @return claimable The amount of tokens that can be claimed
function getClaimableAmount(
bytes32 _vestingId
) external view returns (uint256 claimable);
/// @notice Checks if a vesting has a pending transfer
/// @param _vestingId The ID of the vesting to check
/// @return The address of the pending owner if there is one, or zero address if none
function getPendingVestingTransfer(
bytes32 _vestingId
) external view returns (address);
/// @notice Get the amount of tokens that have vested by a specific timestamp
/// @param _vestingId The identifier of the vesting
/// @param _referenceTimestamp The timestamp to check vesting status at
/// @return The amount of tokens vested at the reference timestamp
function getVestedAmount(
bytes32 _vestingId,
uint32 _referenceTimestamp
) external view returns (uint256);
/// @notice Get funding information for a vesting schedule
/// @param _vestingId The identifier of the vesting
/// @return fundingType The type of funding (Full or Partial)
/// @return totalFunded Total amount of tokens funded so far
/// @return totalRequired Total amount of tokens required for full funding
function getVestingFundingInfo(
bytes32 _vestingId
)
external
view
returns (uint8 fundingType, uint256 totalFunded, uint256 totalRequired);
/// @notice Retrieves information about a specific vesting arrangement
/// @param _vestingId Unique identifier of the vesting
/// @return Details of the specified vesting
function getVestingInfo(
bytes32 _vestingId
) external view returns (TokenVestingLib.Vesting memory);
/// @notice Initiates the transfer of vesting ownership
/// @param _vestingId The ID of the vesting to transfer
/// @param _newOwner The address of the new owner
function initiateVestingTransfer(
bytes32 _vestingId,
address _newOwner
) external;
/// @notice Determine if a vesting is fully funded
/// @param _vestingId The identifier of the vesting
/// @return True if the vesting is fully funded
function isVestingFullyFunded(
bytes32 _vestingId
) external view returns (bool);
/// @notice Returns the total amount of tokens reserved for vesting
/// @return Amount of tokens reserved for vesting
function numTokensReservedForVesting() external view returns (uint256);
/// @notice Returns the pending transfer address for a vesting ID
/// @param vestingId The vesting ID to check
/// @return Address of the pending transfer, if any
function pendingVestingTransfers(
bytes32 vestingId
) external view returns (address);
/// @notice Returns a vesting ID for a specific recipient at a specific index
/// @param recipient The recipient address
/// @param index The index in the recipient's vestings array
/// @return The vesting ID
function recipientVestings(
address recipient,
uint256 index
) external view returns (bytes32);
/// @notice Revokes a vesting arrangement before it has been fully claimed
/// @param _vestingId Unique identifier of the vesting to be revoked
function revokeVesting(bytes32 _vestingId) external;
/// @notice Updates the fee collector address
/// @param newFeeCollector The new fee collector address
function transferFeeCollectorRole(address newFeeCollector) external;
/// @notice Returns the amount of funding for a specific vesting ID
/// @param vestingId The vesting ID to check
/// @return Amount of funding for the vesting
function vestingFunding(bytes32 vestingId) external view returns (uint256);
/// @notice Allows the admin to withdraw tokens not locked in vesting
/// @param _amountRequested Amount of tokens the admin wishes to withdraw
function withdrawAdmin(uint256 _amountRequested) external;
/// @notice Withdraws gas fees (ETH) collected by the contract
/// @param recipient Address to receive the fees
/// @param amount Amount of ETH to withdraw
function withdrawGasFee(address recipient, uint256 amount) external;
/// @notice Withdraws tokens accidentally sent to the contract's address
/// @param _otherTokenAddress Address of the token to be withdrawn
function withdrawOtherToken(address _otherTokenAddress) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/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.
*/
abstract 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() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(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");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the 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 `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, 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 `from` to `to` 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 from, address to, uint256 amount) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
pragma solidity ^0.8.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 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) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}{
"remappings": [
"@ensdomains/=node_modules/@ensdomains/",
"eth-gas-reporter/=node_modules/eth-gas-reporter/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"hardhat/=node_modules/hardhat/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
"openzeppelin/=lib/openzeppelin-contracts/contracts/"
],
"optimizer": {
"enabled": true,
"runs": 2000
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": true,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"feeCollector_","type":"address"},{"internalType":"uint256","name":"defaultGasFee_","type":"uint256"},{"internalType":"uint256","name":"defaultTokenFee_","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CustomFeeNotSet","type":"error"},{"inputs":[],"name":"FeeTooHigh","type":"error"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"enum ITypes.FeeType","name":"newFeeType","type":"uint8"}],"name":"ChangeDefaultFeeType","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"admin","type":"address"},{"indexed":true,"internalType":"address","name":"newFeeCollector","type":"address"}],"name":"FeeCollectorSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract NativeTokenVestingManager","name":"nativeTokenVestingManager","type":"address"},{"indexed":false,"internalType":"enum ITypes.FeeType","name":"feeType","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"},{"indexed":false,"internalType":"address","name":"feeCollector","type":"address"},{"indexed":false,"internalType":"address","name":"creator","type":"address"},{"indexed":false,"internalType":"enum ITypes.FundingType","name":"fundingType","type":"uint8"}],"name":"NativeTokenVestingManagerCreated","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":"admin","type":"address"},{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"},{"indexed":true,"internalType":"address","name":"campaignCreator","type":"address"},{"indexed":false,"internalType":"enum ITypes.FeeType","name":"feeType","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"gasFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenFee","type":"uint256"}],"name":"SetCustomFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldGasFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newGasFee","type":"uint256"}],"name":"SetDefaultGasFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldTokenFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newTokenFee","type":"uint256"}],"name":"SetDefaultTokenFee","type":"event"},{"inputs":[],"name":"BASIS_POINTS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultFeeType","outputs":[{"internalType":"enum ITypes.FeeType","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultGasFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultTokenFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"campaignCreator","type":"address"}],"name":"disableCustomFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeCollector","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"campaignCreator","type":"address"}],"name":"getCustomFee","outputs":[{"components":[{"internalType":"bool","name":"enabled","type":"bool"},{"internalType":"enum ITypes.FeeType","name":"preferredFeeType","type":"uint8"},{"internalType":"uint256","name":"gasFee","type":"uint256"},{"internalType":"uint256","name":"tokenFee","type":"uint256"}],"internalType":"struct FactoryFeeManager.CustomFee","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum ITypes.FundingType","name":"fundingType","type":"uint8"}],"name":"newNativeTokenVestingManager","outputs":[{"internalType":"contract NativeTokenVestingManager","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"resetGasFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"resetTokenFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"campaignCreator","type":"address"},{"internalType":"enum ITypes.FeeType","name":"feeType","type":"uint8"},{"internalType":"uint256","name":"gasFee","type":"uint256"},{"internalType":"uint256","name":"tokenFee","type":"uint256"}],"name":"setCustomFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum ITypes.FeeType","name":"feeType","type":"uint8"}],"name":"setDefaultFeeType","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newGasFee","type":"uint256"}],"name":"setDefaultGasFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newTokenFee","type":"uint256"}],"name":"setDefaultTokenFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newFeeCollector","type":"address"}],"name":"setFeeCollector","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
6080346100f757601f61468838819003918201601f19168301916001600160401b038311848410176100fb578084926060946040528339810103126100f75780516001600160a01b03811691908290036100f75760208101516040918201515f8054336001600160a01b03198216811783559451959294916001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a360035481156100e85761271084116100d9576001600160a81b0319161760035560015560025561457890816101108239f35b63cd4e616760e01b5f5260045ffd5b63e6c4247b60e01b5f5260045ffd5b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6080806040526004361015610012575f80fd5b5f905f3560e01c908162d857c9146109385750806301960495146108df5780630be536fd14610758578063174ad186146106f35780632184c94c146106d55780633a8dda7d1461061b57806366878b92146105ee578063715018a6146105885780638da5cb5b14610562578063a3a2582e14610509578063a42dce801461048c578063a6e34510146103f3578063c10e9f1a146102fe578063c415b95c146102d7578063e1f1c4a7146102ba578063f2fde38b146101b7578063f3ebcae6146101035763f769019c146100e3575f80fd5b346101005780600319360112610100576020600254604051908152f35b80fd5b50346101005760206003193601126101005760043560028110156101b35760407f092f91dc14ff3ff17916a850d885f3528926208958afa8b91d61fa7f169d64539161014d610c5a565b61015681610bc6565b6003547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff74ff00000000000000000000000000000000000000008360a01b169116176003558151903382526101aa81610bc6565b6020820152a180f35b5080fd5b5034610100576020600319360112610100576001600160a01b036101d9610bb0565b6101e1610c5a565b168015610236576001600160a01b0382548273ffffffffffffffffffffffffffffffffffffffff198216178455167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152fd5b503461010057806003193601126101005760206040516127108152f35b503461010057806003193601126101005760206001600160a01b0360035416604051908152f35b5034610100576020600319360112610100576001600160a01b03610320610bb0565b610328610c5a565b1680156103cb57808252600460205260ff604083205416156103a35780825260046020528160026040822082815582600182015501557f3cc9b9eaab9dbc1846c4cd03b8e42d9295dc371837acbf684f4cdbd3d282178560a0604051338152846020820152846040820152846060820152846080820152a280f35b6004827f55e44bb7000000000000000000000000000000000000000000000000000000008152fd5b6004827fe6c4247b000000000000000000000000000000000000000000000000000000008152fd5b503461010057602060031936011261010057600435610410610c5a565b61271081116104645760028054908290556040805133815260208101929092528101919091527f9dc950d0b8f391076ebed01cf78d9407175fc43f986998e47b0e463d88449f469080606081015b0390a180f35b6004827fcd4e6167000000000000000000000000000000000000000000000000000000008152fd5b5034610100576020600319360112610100576001600160a01b036104ae610bb0565b6104b6610c5a565b1680156103cb578073ffffffffffffffffffffffffffffffffffffffff1960035416176003557f089588e3f10370c99a6f74177eacb5361ba90e1b70a123bfeccb6619c21cd7216020604051338152a280f35b5034610100578060031936011261010057610522610c5a565b7f9dc950d0b8f391076ebed01cf78d9407175fc43f986998e47b0e463d88449f46606060025483600255604051903382526020820152836040820152a180f35b50346101005780600319360112610100576001600160a01b036020915416604051908152f35b50346101005780600319360112610100576105a1610c5a565b806001600160a01b03815473ffffffffffffffffffffffffffffffffffffffff1981168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b5034610100578060031936011261010057602060ff60035460a01c166040519061061781610bc6565b8152f35b50346101005760206003193601126101005760406080916001600160a01b03610642610bb0565b826060855161065081610bfd565b828152826020820152828782015201521681526004602052206040519061067682610bfd565b60ff81548181161515845260081c1690602083019161069481610bc6565b825260026001820154916040850192835201549160608401928352604051935115158452516106c281610bc6565b6020840152516040830152516060820152f35b50346101005780600319360112610100576020600154604051908152f35b5034610100576020600319360112610100577fad98fff657b869276115039ca32672748a9d070087367531e2e97d1b0146c3d2600435610731610c5a565b6001805490829055604080513381526020810192909252810191909152806060810161045e565b503461010057608060031936011261010057610772610bb0565b60243560028110156108db57604435916001600160a01b0360643591610796610c5a565b169283156108b357612710821161088b579160a0917f3cc9b9eaab9dbc1846c4cd03b8e42d9295dc371837acbf684f4cdbd3d2821785936040516107d981610bfd565b600181526002602082016107ec86610bc6565b8581526040830184815260608401918683528a8c52600460205260408c2094511515907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000060ff61ff008854935161084281610bc6565b61084b81610bc6565b60081b16931691161717845551600184015551910155604051923384526001602085015261087881610bc6565b604084015260608301526080820152a280f35b6004857fcd4e6167000000000000000000000000000000000000000000000000000000008152fd5b6004857fe6c4247b000000000000000000000000000000000000000000000000000000008152fd5b8280fd5b50346101005780600319360112610100576108f8610c5a565b7fad98fff657b869276115039ca32672748a9d070087367531e2e97d1b0146c3d2606060015483600155604051903382526020820152836040820152a180f35b905034610b5a576020600319360112610b5a57600435906002821015610b5a57335f52600460205260405f209061096e81610bfd565b60ff82548181161515835260081c169061098782610bc6565b816020820152600260018401549360408301948552015460608201525115159081610b95575b5015610b8b5751905b6001600160a01b03600354166040519061387780830183811067ffffffffffffffff821117610b5e576060928492610ccc843986825260208201526109fa85610bc6565b8460408201520301905ff0908115610b4f577fcf27c1d1aa04b24269ba420f805f2d0357e792f0b2798a18fd1f17576b7542fe9160c0916001600160a01b03600354166001600160a01b036040519316958684525f602085015260408401526060830152336080830152610a6d81610bc6565b60a0820152a1803b15610b5a576040517f4b0bddd2000000000000000000000000000000000000000000000000000000008152336004820152600160248201525f8160448183865af18015610b4f57610b3a575b50803b156101b3576040517f4b0bddd2000000000000000000000000000000000000000000000000000000008152306004820152826024820152828160448183865af18015610b2f57610b1a575b602082604051908152f35b610b25838092610c19565b6101b35781610b0f565b6040513d85823e3d90fd5b610b479192505f90610c19565b5f905f610ac1565b6040513d5f823e3d90fd5b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b50600154906109b6565b9050610ba081610bc6565b610ba981610bc6565b155f6109ad565b600435906001600160a01b0382168203610b5a57565b60021115610bd057565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b6080810190811067ffffffffffffffff821117610b5e57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610b5e57604052565b6001600160a01b035f54163303610c6d57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fdfe60e03461015f57601f61387738819003918201601f19168301916001600160401b038311848410176101635780849260609460405283398101031261015f578051602082015190916001600160a01b0382169182900361015f576040015191600283101561015f57335f525f60205260405f20600160ff1982541617905560018055604051600181527fe529461c8529abc0e0fe7c5ee361f74fe22e0b7574df1fc0b7558a282091fb7860203392a281156101505760c052600580546001600160a01b0319169190911790556080524360a0526040516136ff9081610178823960805181818161027b015281816106df01528181610ea40152818161115e015281816114c601528181611652015281816118b801528181612065015281816129ca01528181612a87015261331d015260a05181611be0015260c0518181816105b1015281816113ed015261296f0152f35b63e6c4247b60e01b5f5260045ffd5b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6080806040526004361015610012575f80fd5b5f3560e01c908163137c68fa146125d45750806324d7806c146125985780632b7832b31461257b5780632eb7d434146124c257806343ff9d8d146124a55780634a7be739146124245780634b0bddd2146122b557806354f1c9041461214b5780635650d7d714611fd05780635edc1c8814611f0f578063661b743d14611dd957806367a421b614611daf5780636d63b7ff14611cfa578063775907f714611c035780638635512f14611bc957806389a9c7b414610fcb5780638a85cbba14611b9e5780638c5143ea14611b7b5780639335af1a14611a8e5780639fb40e7b1461192e578063a3ba6c7a14611880578063b6b00a501461184d578063b74513c1146117c4578063b8900aa71461159c578063b9ed715314611574578063ba48042b146114f6578063ba50bba5146114b2578063bb64fd4414611494578063bd66528a14611436578063c415b95c14611410578063c57981b5146113d6578063caaa9a2214611107578063cdc6aa4c14610ffe578063cedc9d1d14610fcb578063cf61a09714610fb1578063d0e8bd4614610e6b578063d1bc76a114610e2a578063d77836ce14610d58578063d955d6eb14610d03578063e50373f914610b05578063e5b3248714610a9f578063e865fbc714610a6c578063e940e5381461063a578063ecb62f6114610564578063f0e1efa0146105475763f5ee64c014610216575f80fd5b60406003193601126105435760043567ffffffffffffffff8111610543576102429036906004016126c6565b60243567ffffffffffffffff8111610543576102629036906004016126c6565b919092335f525f60205260ff60405f2054161561051b577f00000000000000000000000000000000000000000000000000000000000000006102a3816126a8565b156104015781156104f3578282036104cb573415610483575f5f5b8381106104ab57503403610483575f5b8281106102d757005b6102e2818484612b05565b35906102ef818688612b05565b35801561047957825f52600b60205260405f2080546001600160a01b038116156104515760e01c61042957806004610333600261033c9401546003840154906127e6565b910154906127e6565b92805f52600860205260405f2054848110156104015761035c90856128f5565b82116103d9577f7d880d31c520e2aafc7d84cd9b7b8519c22fa45a2682a2b09035af562b3820a4600194825f52600860205260405f2061039d8582546127e6565b90556103ab846002546127e6565b6002555f83815260086020908152604091829020548251968752908601528401523392606090a35b016102ce565b7fb6b88bbc000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fbbc4bdcf000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f48a0eba2000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f3a605dd2000000000000000000000000000000000000000000000000000000005f5260045ffd5b50600191506103d3565b7f57e000b1000000000000000000000000000000000000000000000000000000005f5260045ffd5b906104c46001916104bd84888a612b05565b35906127e6565b91016102be565b7fa24a13a6000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f521299a9000000000000000000000000000000000000000000000000000000005f5260045ffd5b7ffdd63ca2000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f80fd5b34610543575f600319360112610543576020600354604051908152f35b60206003193601126105435760043567ffffffffffffffff8111610543576105909036906004016126c6565b90335f525f60205260ff60405f2054161561051b5781156104f3576105d5827f0000000000000000000000000000000000000000000000000000000000000000612b7a565b3403610612576105e7346003546127e6565b6003555f5b8281106105f557005b8061060c6106066001938686612b05565b356132bf565b016105ec565b7f025dbdd4000000000000000000000000000000000000000000000000000000005f5260045ffd5b60206003193601126105435760043567ffffffffffffffff81116105435780600401610140600319833603011261054357335f525f60205260ff60405f2054161561051b576106898180612b15565b602484019291508061069b8484612b15565b905014801590610a54575b8015610a3c575b8015610a24575b8015610a0c575b80156109f4575b80156109dc575b80156109c3575b80156109aa575b6104cb5760017f0000000000000000000000000000000000000000000000000000000000000000610707816126a8565b0361092157346108f957925b5f9060448101606482016084830160a484019060c485019260e486019461012461010488019701975b8b811061074557005b60045461075181612738565b600455610768826107628d80612b15565b90612b05565b356001600160a01b038116809103610543578a8a8a8f8f8a8a8a8f948f948c80808080808080610799819a8a612b15565b6107a39291612b05565b6107ac90612b69565b996107b79089612b15565b6107c19291612b05565b6107ca90612b69565b996107d59088612b15565b6107df9291612b05565b6107e890612b69565b996107f39087612b15565b6107fd9291612b05565b35996108099086612b15565b6108139291612b05565b61081c90612b69565b996108279085612b15565b6108319291612b05565b359961083d9084612b15565b6108479291612b05565b61085090612b69565b9961085b9083612b15565b6108659291612b05565b359961087091612b15565b61087a9291612b05565b3598891515809a03610543576040519a6108938c6127f3565b8b5260208b015263ffffffff1660408a015263ffffffff16606089015263ffffffff16608088015260a087015263ffffffff1660c086015260e085015263ffffffff166101008401526101208301526101408201526108f190612cf7565b60010161073c565b7fbb21bfd4000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f8060848601610104870160c488015b8686861061095b5750505050809150340361048357610952906002546127e6565b60025592610713565b8394610999826104bd8961076288610993896104bd8561076260019d9e9f8261076261099f9f9261098b93612b15565b35938a612b15565b94612b15565b906127e6565b940193929190610931565b50806109ba610124860184612b15565b905014156106d7565b50806109d3610104860184612b15565b905014156106d0565b50806109eb60e4860184612b15565b905014156106c9565b5080610a0360a4860184612b15565b905014156106c2565b5080610a1b60c4860184612b15565b905014156106bb565b5080610a336084860184612b15565b905014156106b4565b5080610a4b6064860184612b15565b905014156106ad565b5080610a636044860184612b15565b905014156106a6565b34610543575f600319360112610543576020610a97610a8e47600254906128f5565b600354906128f5565b604051908152f35b34610543576020600319360112610543576001600160a01b03610ac06125ee565b168015610add575f526009602052602060405f2054604051908152f35b7fe6c4247b000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461054357602060031936011261054357610b1e6125ee565b335f525f60205260ff60405f2054161561051b576001600160a01b03168015610add57604051907f70a08231000000000000000000000000000000000000000000000000000000008252306004830152602082602481845afa908115610cf8575f91610cc2575b610c1f92505f806040519360208501907fa9059cbb000000000000000000000000000000000000000000000000000000008252336024870152604486015260448552610bd260648661282d565b60405194610be160408761282d565b602086527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020870152519082855af1610c1961292e565b91613616565b8051908115918215610c9f575b505015610c3557005b608460405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b819250906020918101031261054357602001518015158103610543578180610c2c565b90506020823d602011610cf0575b81610cdd6020938361282d565b8101031261054357610c1f915190610b85565b3d9150610cd0565b6040513d5f823e3d90fd5b3461054357604060031936011261054357610d1c6125ee565b6001600160a01b0360243591165f52600960205260405f20805482101561054357602091610d4991612723565b90549060031b1c604051908152f35b3461054357602060031936011261054357600435335f525f60205260ff60405f2054161561051b57610d90610a8e47600254906128f5565b8111610e02575f808080936040518181527fca1cf43de312865665f595e88f569f9d5246690c07df26e86aba01147e6d131460203392a2335af1610dd261292e565b5015610dda57005b7f90b8ec18000000000000000000000000000000000000000000000000000000005f5260045ffd5b7ff4d678b8000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461054357602060031936011261054357600435600654811015610543576001600160a01b03610e5b6020926126f7565b90549060031b1c16604051908152f35b602060031936011261054357600435805f52600b60205260405f205460e01c61042957335f525f60205260ff60405f2054161561051b577f0000000000000000000000000000000000000000000000000000000000000000610ecc816126a8565b156104015734156108f957805f52600b60205260405f206001600160a01b0381541615610451578060046103336002610f0c9401546003840154906127e6565b90805f52600860205260405f20548281101561040157610f2c90836128f5565b34116103d957805f52600860205260405f20610f493482546127e6565b9055610f57346002546127e6565b600255805f5260086020527f7d880d31c520e2aafc7d84cd9b7b8519c22fa45a2682a2b09035af562b3820a4610fac60405f205493604051918291339634846040919493926060820195825260208201520152565b0390a3005b34610543575f6003193601126105435760206040515f8152f35b34610543576020600319360112610543576004355f52600c60205260206001600160a01b0360405f205416604051908152f35b3461054357606060031936011261054357600435602435604435916001600160a01b038316809303610543578215610add578181108015906110f2575b6110ca5761104981836128f5565b92601f1961106f61105986612902565b95611067604051978861282d565b808752612902565b01366020860137815b838110611091576040518061108d878261266f565b0390f35b600190825f5260096020526110a98160405f20612723565b90549060031b1c6110c36110bd86846128f5565b8861291a565b5201611078565b7f561ce9bb000000000000000000000000000000000000000000000000000000005f5260045ffd5b50825f52600960205260405f2054821161103b565b346105435760206003193601126105435760043567ffffffffffffffff8111610543576111389036906004016126c6565b90335f525f60205260ff60405f2054161561051b5781156104f3575f63ffffffff4216927f00000000000000000000000000000000000000000000000000000000000000009060018214925b81811061118d57005b611198818387612b05565b3590815f52600b60205260405f205460e01c61042957815f52600b60205260405f209163ffffffff835460c01c16804210156113ae576006840160ff81541615611386578961122a6101a0928761122f7ffab6a0d43d3a59dd207bc8acbb42f638ace4f051bc53d95dd662fbfa5bc0213a9661121e8661121960019d612850565b61342a565b94859161121985612850565b6128f5565b928c61123a8d6126a8565b611343575b508061129860ff9495600593907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffff0000000000000000000000000000000000000000000000000000000083549260e01b169116179055565b6112a4856002546128f5565b60025560405194855280546001600160a01b038116602087015263ffffffff8160a01c16604087015263ffffffff8160c01c16606087015260e01c608086015263ffffffff8a82015481811660a0880152818160201c1660c088015260401c1660e0860152600281015461010086015260038101546101208601526004810154610140860152015461016084015254161515610180820152a201611184565b5f8881526008602052604090205460ff945060059291908181116113735750506112985f955b959450509061123f565b61129891611380916128f5565b95611369565b7fce21cad1000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f2e8e455d000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610543575f6003193601126105435760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b34610543575f6003193601126105435760206001600160a01b0360055416604051908152f35b602060031936011261054357600435805f52600b6020526001600160a01b0360405f205416330361146c5761146a9061296d565b005b7f75228a9c000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610543576020600319360112610543576020610a97600435612a29565b34610543575f6003193601126105435760207f0000000000000000000000000000000000000000000000000000000000000000604051906114f2816126a8565b8152f35b34610543576020600319360112610543576001600160a01b036115176125ee565b165f52600960205260405f206040519081602082549182815201915f5260205f20905f5b81811061155e5761108d856115528187038261282d565b6040519182918261266f565b825484526020909301926001928301920161153b565b346105435760206003193601126105435760206115926004356129ac565b6040519015158152f35b3461054357602060031936011261054357600435335f525f60205260ff60405f2054161561051b57805f52600b60205260405f205460e01c61042957805f52600b60205260405f2063ffffffff815460c01c16804210156113ae57600682019060ff825416156113865761122a6101a0928461164d61163b7ffab6a0d43d3a59dd207bc8acbb42f638ace4f051bc53d95dd662fbfa5bc0213a97612850565b9461121e63ffffffff4216809761342a565b9260017f000000000000000000000000000000000000000000000000000000000000000061167a816126a8565b14611781575b50806116d960ff9495600593907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffff0000000000000000000000000000000000000000000000000000000083549260e01b169116179055565b6116e5856002546128f5565b60025560405194855280546001600160a01b038116602087015263ffffffff8160a01c16604087015263ffffffff8160c01c16606087015260e01c608086015263ffffffff600182015481811660a0880152818160201c1660c088015260401c1660e0860152600281015461010086015260038101546101208601526004810154610140860152015461016084015254161515610180820152a2005b5f8881526008602052604090205460ff945060059291908181116117b15750506116d95f955b9594505090611680565b6116d9916117be916128f5565b956117a7565b34610543575f6003193601126105435760405180602060065491828152019060065f527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f905f5b81811061182e5761108d856118228187038261282d565b6040519182918261262d565b82546001600160a01b031684526020909301926001928301920161180b565b34610543576040600319360112610543576020610a9761186b61261a565b6004355f52600b835261121960405f20612850565b3461054357602060031936011261054357600435805f52600b60205260405f206001600160a01b0381541615610451576060916118f67f0000000000000000000000000000000000000000000000000000000000000000926118e1846126a8565b600461033360028301546003840154906127e6565b90611900836126a8565b8261191d5750805b60ff6040519316835260208301526040820152f35b5f52600860205260405f2054611908565b3461054357602060031936011261054357600435805f52600b60205260405f205460e01c61042957805f52600c6020526001600160a01b0360405f205416338103611a6657815f52600b60205260405f206001600160a01b03815416906119958483612b8d565b6001600160a01b03831673ffffffffffffffffffffffffffffffffffffffff198254161790556119c482612c91565b15611a48575b815f5260096020526119df8360405f206127a5565b5f82815260096020908152604080832054600a8352818420878552835281842055600c9091528120805473ffffffffffffffffffffffffffffffffffffffff191690557fd26d820c791a6bb2143a3066ee2a8314ff045a3abc3f256346814ad5be7d63469080a4005b611a5182612746565b600654825f52600760205260405f20556119ca565b7fc414c24a000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461054357604060031936011261054357600435611aaa612604565b815f52600b6020526001600160a01b0360405f205416330361146c57815f52600b60205260405f205460e01c610429576001600160a01b03168015610add57815f52600c6020526001600160a01b0360405f205416611b5357815f52600c60205260405f208173ffffffffffffffffffffffffffffffffffffffff19825416179055337f666421a92fd891d9481da847947b32bfd485b5a6ac70ef477f18b9aae576c3715f80a4005b7f8f524afa000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610543576020600319360112610543576020611592611b996125ee565b612c91565b602060031936011261054357335f525f60205260ff60405f2054161561051b5761146a60043561296d565b34610543575f6003193601126105435760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461054357604060031936011261054357611c1c6125ee565b602435906001600160a01b03600554163303611cd2576001600160a01b038116828115610add576003549081156106125715611cca575b808411611ca2575f8481948294611c6b8385966128f5565b6003557fee3a3e8b975ee1a894fd6ed0a36ec6d1db3dc70e575382d21e9aed5a2c72f5146020604051858152a25af1610dd261292e565b7f732f9413000000000000000000000000000000000000000000000000000000005f5260045ffd5b925082611c53565b7f73fcd3fe000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461054357604060031936011261054357600435602435808210801590611da4575b6110ca57611d2a82826128f5565b91601f19611d50611d3a85612902565b94611d48604051968761282d565b808652612902565b01366020850137805b828110611d6e576040518061108d868261262d565b806001600160a01b03611d826001936126f7565b90549060031b1c16611d9d611d9785846128f5565b8761291a565b5201611d59565b506006548111611d1c565b34610543576020600319360112610543576004355f526008602052602060405f2054604051908152f35b34610543576020600319360112610543575f610160604051611dfa81612810565b8281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e082015282610100820152826101208201528261014082015201526004355f52600b60205261108d611e5760405f20612850565b60405191829182919091610160806101808301946001600160a01b03815116845263ffffffff602082015116602085015263ffffffff604082015116604085015263ffffffff606082015116606085015263ffffffff608082015116608085015263ffffffff60a08201511660a085015263ffffffff60c08201511660c085015260e081015160e085015261010081015161010085015261012081015161012085015261014081015161014085015201511515910152565b3461054357602060031936011261054357600435805f52600b6020526001600160a01b0360405f205416330361146c57805f52600c6020526001600160a01b0360405f20541615611fa857805f52600c60205260405f2073ffffffffffffffffffffffffffffffffffffffff198154169055337f155ceda63be6f9c02f83b108c17e45601c8e72e9845fd5a15c4c33b53e8cac1b5f80a3005b7fd5e80fb4000000000000000000000000000000000000000000000000000000005f5260045ffd5b61014060031936011261054357611fe56125ee565b611fed61261a565b60443563ffffffff81168091036105435760643563ffffffff81168091036105435760843560a43563ffffffff81168091036105435760c4359060e4359263ffffffff8416809403610543576101043594610124359687151580980361054357335f525f60205260ff60405f2054161561051b5760017f000000000000000000000000000000000000000000000000000000000000000061208d816126a8565b0361210457346108f95763ffffffff6120c6996001600160a01b0361146a9c5b6004546120b981612738565b6004556040519d8e6127f3565b8d521660208c01521660408a01526060890152608088015260a087015260c086015260e0850152610100840152610120830152610140820152612cf7565b61211a8761211587869d9c9d6127e6565b6127e6565b98893403610e025761146a9a6001600160a01b0363ffffffff926121436120c69d6002546127e6565b6002556120ad565b3461054357604060031936011261054357600435612167612604565b815f52600b6020526001600160a01b0360405f205416330361146c57815f52600b60205260405f205460e01c610429576001600160a01b038116908115610add57825f52600b60205260405f20906001600160a01b03825416916121cb8584612b8d565b8373ffffffffffffffffffffffffffffffffffffffff198254161790556121f181612c91565b15612296575b50815f52600960205261220d8360405f206127a5565b5f82815260096020908152604080832054600a8352818420878552835281842055600c9091529020546001600160a01b031661226a575b7fd26d820c791a6bb2143a3066ee2a8314ff045a3abc3f256346814ad5be7d63465f80a4005b825f52600c60205260405f2073ffffffffffffffffffffffffffffffffffffffff198154169055612244565b61229f90612746565b600654825f52600760205260405f2055836121f7565b34610543576040600319360112610543576122ce6125ee565b602435908115159081830361054357335f525f60205260ff60405f2054161561051b576001600160a01b0316918215610add57825f525f6020528160ff60405f2054161515146123fc57801561237a577fe529461c8529abc0e0fe7c5ee361f74fe22e0b7574df1fc0b7558a282091fb7891612371602092612351600154612738565b6001555b855f525f845260405f209060ff60ff1983541691151516179055565b604051908152a2005b6001549160018311156123d45782156123c0576123716020927fe529461c8529abc0e0fe7c5ee361f74fe22e0b7574df1fc0b7558a282091fb78945f1901600155612355565b634e487b7160e01b5f52601160045260245ffd5b7fc13a62ad000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fd52dfc1c000000000000000000000000000000000000000000000000000000005f5260045ffd5b346105435760206003193601126105435761243d6125ee565b600554906001600160a01b03821690813303611cd2576001600160a01b0316918215610add5773ffffffffffffffffffffffffffffffffffffffff191682176005557f5d16ad41baeb009cd23eb8f6c7cde5c2e0cd5acf4a33926ab488875c37c37f385f80a3005b34610543575f600319360112610543576020600654604051908152f35b34610543576020600319360112610543576004355f52600b60205261018060405f208054906001810154906002810154600382015460048301549163ffffffff60ff600660058701549601541695604051976001600160a01b0381168952828160a01c1660208a0152828160c01c1660408a015260e01c60608901528181166080890152818160201c1660a089015260401c1660c087015260e08601526101008501526101208401526101408301521515610160820152f35b34610543575f600319360112610543576020600154604051908152f35b34610543576020600319360112610543576001600160a01b036125b96125ee565b165f525f602052602060ff60405f2054166040519015158152f35b34610543575f600319360112610543576020906002548152f35b600435906001600160a01b038216820361054357565b602435906001600160a01b038216820361054357565b6024359063ffffffff8216820361054357565b60206040818301928281528451809452019201905f5b8181106126505750505090565b82516001600160a01b0316845260209384019390920191600101612643565b60206040818301928281528451809452019201905f5b8181106126925750505090565b8251845260209384019390920191600101612685565b600211156126b257565b634e487b7160e01b5f52602160045260245ffd5b9181601f840112156105435782359167ffffffffffffffff8311610543576020808501948460051b01011161054357565b60065481101561270f5760065f5260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b805482101561270f575f5260205f2001905f90565b5f1981146123c05760010190565b60065490680100000000000000008210156127915761277082600161278f94016006556006612723565b9091906001600160a01b038084549260031b9316831b921b1916179055565b565b634e487b7160e01b5f52604160045260245ffd5b8054906801000000000000000082101561279157816127cc9160016127e294018155612723565b819391549060031b91821b915f19901b19161790565b9055565b919082018092116123c057565b610160810190811067ffffffffffffffff82111761279157604052565b610180810190811067ffffffffffffffff82111761279157604052565b90601f601f19910116810190811067ffffffffffffffff82111761279157604052565b9060405161285d81612810565b61016060ff6006839580546001600160a01b038116865263ffffffff8160a01c16602087015263ffffffff8160c01c16604087015260e01c606086015263ffffffff60018201548181166080880152818160201c1660a088015260401c1660c0860152600281015460e08601526003810154610100860152600481015461012086015260058101546101408601520154161515910152565b919082039182116123c057565b67ffffffffffffffff81116127915760051b60200190565b805182101561270f5760209160051b010190565b3d15612968573d9067ffffffffffffffff8211612791576040519161295d601f8201601f19166020018461282d565b82523d5f602084013e565b606090565b7f000000000000000000000000000000000000000000000000000000000000000034036106125761278f906129a4346003546127e6565b6003556132bf565b805f52600b60205260405f206001600160a01b0381541615610451577f00000000000000000000000000000000000000000000000000000000000000006129f2816126a8565b15612a22578060046103336002612a109401546003840154906127e6565b905f52600860205260405f2054101590565b5050600190565b805f52600b60205260405f206001600160a01b03815416156104515763ffffffff60018201541663ffffffff4216809111612afe57612a6d60059161121984612850565b91015480821115612af857612a8281836128f5565b9260017f0000000000000000000000000000000000000000000000000000000000000000612aaf816126a8565b14612abb575b50505090565b5f52600860205260405f2054918210612ad5575b80612ab5565b90915080821115612af057612ae9916128f5565b5f80612acf565b50505f612ae9565b5f612a82565b5050505f90565b919081101561270f5760051b0190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610543570180359067ffffffffffffffff821161054357602001918160051b3603831361054357565b3563ffffffff811681036105435790565b818102929181159184041417156123c057565b906001600160a01b03821690815f52600960205260405f2091805f52600a60205260405f20825f5260205260405f20548015612c8a575f1981018181116123c05784545f198101919082116123c057818103612c49575b50505082548015612c35575f1901612bfc8185612723565b8154905f199060031b1b1916905583555f52600a60205260405f20905f526020525f60408120555415612c2c5750565b61278f9061355c565b634e487b7160e01b5f52603160045260245ffd5b612c69612c596127cc9388612723565b90549060031b1c92839288612723565b9055825f52600a60205260405f20905f5260205260405f20555f8080612be4565b5050505050565b6001600160a01b03168015610add575f52600760205260405f2054151590565b9063ffffffff8091169116039063ffffffff82116123c057565b9063ffffffff16908115612ce35763ffffffff160690565b634e487b7160e01b5f52601260045260245ffd5b602081016001600160a01b0381511615610add57610120820191825192612d2460a08301948551906127e6565b612d3460e08401918251906127e6565b1561329757604083019163ffffffff8351161561326f5763ffffffff83511690606085019163ffffffff8351161061323d5763ffffffff84511663ffffffff8351161480613265575b61323d57610100850163ffffffff815116156132155760c08601805163ffffffff168061317d575084516131555763ffffffff612dce612dc482875116838a511690612cb1565b8285511690612ccb565b1661312d575b63ffffffff806001600160a01b038a51169751169451169863ffffffff808060808b015116925193511696519351169351936101408901511515966040519b8c99612e1e8b612810565b8a5260208a0197885260408a0190815260608a01905f825260808b0194855260a08b0193845260c08b0192835260e08b019586526101008b019687526101208b019788526101408b01985f8a526101608c019a8b528d612e876001600160a01b03825116612c91565b156130f9575b508c515f908152600b602052604090209b519051915192517fffffffff0000000000000000000000000000000000000000000000000000000060e09190911b1660c09390931b7bffffffff000000000000000000000000000000000000000000000000166001600160a01b0390911660a09290921b77ffffffff000000000000000000000000000000000000000016919091171717895560018901925163ffffffff168354925160201b67ffffffff0000000016915160401b6bffffffff000000000000000016927fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001617171790555160028601555160038501555160048401555160058301555115159060060190612fb2919060ff60ff1983541691151516179055565b81516001600160a01b03165f52600960205260405f208151612fd3916127a5565b81516001600160a01b03165f52600960205260405f205482516001600160a01b03165f52600a60205260405f2082515f5260205260405f20555190516001600160a01b0316916040516130d3819282919091610160806101808301946001600160a01b03815116845263ffffffff602082015116602085015263ffffffff604082015116604085015263ffffffff606082015116606085015263ffffffff608082015116608085015263ffffffff60a08201511660a085015263ffffffff60c08201511660c085015260e081015160e085015261010081015161010085015261012081015161012085015261014081015161014085015201511515910152565b037f040a2d252643cef2c4dea3c242e5ed4bf32a76631de8342604dd6cba3074047291a3565b6001600160a01b0361310c915116612746565b8d6001600160a01b036006549151165f52600760205260405f20558d612e8d565b7f28df2855000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fea243c32000000000000000000000000000000000000000000000000000000005f5260045ffd5b8063ffffffff88511611908115613203575b506131db5763ffffffff6131ad612dc4828751168385511690612cb1565b1615612dd4577f28df2855000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f9ce30e12000000000000000000000000000000000000000000000000000000005f5260045ffd5b905063ffffffff85511611155f61318f565b7f98e089af000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f417de2db000000000000000000000000000000000000000000000000000000005f5260045ffd5b5080511515612d7d565b7ffebd12a0000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f17329d67000000000000000000000000000000000000000000000000000000005f5260045ffd5b805f52600b60205260405f206001600160a01b03815416156104515763ffffffff60018201541663ffffffff42168091116133f8576133019061121983612850565b9060058101805461331281856128f5565b938415610e025760017f0000000000000000000000000000000000000000000000000000000000000000613345816126a8565b146133b9575b50925f946001600160a01b038695948695946133688588976127e6565b9055613376846002546128f5565b600255541680917f0508a8b4117d9a7b3d8f5895f6413e61b4f9a2df35afbfb41e78d0ecfff1843f6020604051868152a35af16133b161292e565b5015610dda57565b855f52600860205260405f20549081101561334b5790935083808211156133f0576133e3916128f5565b801561048357925f61334b565b50505f6133e3565b7f779ecdd8000000000000000000000000000000000000000000000000000000005f5260045ffd5b8115612ce3570490565b63ffffffff60608201511680613546575b505f91604082019063ffffffff8251168063ffffffff83161161353e575b506020830163ffffffff80825116921691821015613532575b60c0840163ffffffff815116831015613514575b5163ffffffff169081156135055750905b8181116134a6575b5050505090565b610999926101206134ef8463ffffffff6134e76134cb6134fc9b9a986134f7986128f5565b826134de60a08b01928284511690613420565b91511690612b7a565b9451166128f5565b930151612b7a565b613420565b5f80808061349f565b63ffffffff9150511690613497565b9461352a63ffffffff91610100870151906127e6565b959050613486565b60e08401519450613472565b90505f613459565b8063ffffffff8416111561343b5791505f61343b565b6001600160a01b0316805f52600760205260405f20548015613612575f1981018181116123c0576006545f1981019081116123c0578082036135d6575b5050506006548015612c35575f19016135b1816126f7565b6001600160a01b0382549160031b1b191690556006555f5260076020525f6040812055565b612770916001600160a01b036135ee6135fe936126f7565b90549060031b1c169283916126f7565b5f52600760205260405f20555f8080613599565b5050565b91929015613677575081511561362a575090565b3b156136335790565b606460405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b82519091501561368a5750805190602001fd5b6044602091601f19601f60405194859362461bcd60e51b85528160048601528051918291826024880152018686015e5f85828601015201168101030190fdfea26469706673582212208ebb5b01582fd089a65e597c8ee020bc44e837c16366968063e92facabd1ca2364736f6c634300081c0033a2646970667358221220c7ffc9434ffe73932b736169e969b939f5887a035946eb6bc17583ba4d22458864736f6c634300081c003300000000000000000000000038cb1b3bd9779b4afc9144854dd0e71e47d000ca0000000000000000000000000000000000000000000000000001476b081e800000000000000000000000000000000000000000000000000000000000000001f4
Deployed Bytecode
0x6080806040526004361015610012575f80fd5b5f905f3560e01c908162d857c9146109385750806301960495146108df5780630be536fd14610758578063174ad186146106f35780632184c94c146106d55780633a8dda7d1461061b57806366878b92146105ee578063715018a6146105885780638da5cb5b14610562578063a3a2582e14610509578063a42dce801461048c578063a6e34510146103f3578063c10e9f1a146102fe578063c415b95c146102d7578063e1f1c4a7146102ba578063f2fde38b146101b7578063f3ebcae6146101035763f769019c146100e3575f80fd5b346101005780600319360112610100576020600254604051908152f35b80fd5b50346101005760206003193601126101005760043560028110156101b35760407f092f91dc14ff3ff17916a850d885f3528926208958afa8b91d61fa7f169d64539161014d610c5a565b61015681610bc6565b6003547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff74ff00000000000000000000000000000000000000008360a01b169116176003558151903382526101aa81610bc6565b6020820152a180f35b5080fd5b5034610100576020600319360112610100576001600160a01b036101d9610bb0565b6101e1610c5a565b168015610236576001600160a01b0382548273ffffffffffffffffffffffffffffffffffffffff198216178455167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152fd5b503461010057806003193601126101005760206040516127108152f35b503461010057806003193601126101005760206001600160a01b0360035416604051908152f35b5034610100576020600319360112610100576001600160a01b03610320610bb0565b610328610c5a565b1680156103cb57808252600460205260ff604083205416156103a35780825260046020528160026040822082815582600182015501557f3cc9b9eaab9dbc1846c4cd03b8e42d9295dc371837acbf684f4cdbd3d282178560a0604051338152846020820152846040820152846060820152846080820152a280f35b6004827f55e44bb7000000000000000000000000000000000000000000000000000000008152fd5b6004827fe6c4247b000000000000000000000000000000000000000000000000000000008152fd5b503461010057602060031936011261010057600435610410610c5a565b61271081116104645760028054908290556040805133815260208101929092528101919091527f9dc950d0b8f391076ebed01cf78d9407175fc43f986998e47b0e463d88449f469080606081015b0390a180f35b6004827fcd4e6167000000000000000000000000000000000000000000000000000000008152fd5b5034610100576020600319360112610100576001600160a01b036104ae610bb0565b6104b6610c5a565b1680156103cb578073ffffffffffffffffffffffffffffffffffffffff1960035416176003557f089588e3f10370c99a6f74177eacb5361ba90e1b70a123bfeccb6619c21cd7216020604051338152a280f35b5034610100578060031936011261010057610522610c5a565b7f9dc950d0b8f391076ebed01cf78d9407175fc43f986998e47b0e463d88449f46606060025483600255604051903382526020820152836040820152a180f35b50346101005780600319360112610100576001600160a01b036020915416604051908152f35b50346101005780600319360112610100576105a1610c5a565b806001600160a01b03815473ffffffffffffffffffffffffffffffffffffffff1981168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b5034610100578060031936011261010057602060ff60035460a01c166040519061061781610bc6565b8152f35b50346101005760206003193601126101005760406080916001600160a01b03610642610bb0565b826060855161065081610bfd565b828152826020820152828782015201521681526004602052206040519061067682610bfd565b60ff81548181161515845260081c1690602083019161069481610bc6565b825260026001820154916040850192835201549160608401928352604051935115158452516106c281610bc6565b6020840152516040830152516060820152f35b50346101005780600319360112610100576020600154604051908152f35b5034610100576020600319360112610100577fad98fff657b869276115039ca32672748a9d070087367531e2e97d1b0146c3d2600435610731610c5a565b6001805490829055604080513381526020810192909252810191909152806060810161045e565b503461010057608060031936011261010057610772610bb0565b60243560028110156108db57604435916001600160a01b0360643591610796610c5a565b169283156108b357612710821161088b579160a0917f3cc9b9eaab9dbc1846c4cd03b8e42d9295dc371837acbf684f4cdbd3d2821785936040516107d981610bfd565b600181526002602082016107ec86610bc6565b8581526040830184815260608401918683528a8c52600460205260408c2094511515907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000060ff61ff008854935161084281610bc6565b61084b81610bc6565b60081b16931691161717845551600184015551910155604051923384526001602085015261087881610bc6565b604084015260608301526080820152a280f35b6004857fcd4e6167000000000000000000000000000000000000000000000000000000008152fd5b6004857fe6c4247b000000000000000000000000000000000000000000000000000000008152fd5b8280fd5b50346101005780600319360112610100576108f8610c5a565b7fad98fff657b869276115039ca32672748a9d070087367531e2e97d1b0146c3d2606060015483600155604051903382526020820152836040820152a180f35b905034610b5a576020600319360112610b5a57600435906002821015610b5a57335f52600460205260405f209061096e81610bfd565b60ff82548181161515835260081c169061098782610bc6565b816020820152600260018401549360408301948552015460608201525115159081610b95575b5015610b8b5751905b6001600160a01b03600354166040519061387780830183811067ffffffffffffffff821117610b5e576060928492610ccc843986825260208201526109fa85610bc6565b8460408201520301905ff0908115610b4f577fcf27c1d1aa04b24269ba420f805f2d0357e792f0b2798a18fd1f17576b7542fe9160c0916001600160a01b03600354166001600160a01b036040519316958684525f602085015260408401526060830152336080830152610a6d81610bc6565b60a0820152a1803b15610b5a576040517f4b0bddd2000000000000000000000000000000000000000000000000000000008152336004820152600160248201525f8160448183865af18015610b4f57610b3a575b50803b156101b3576040517f4b0bddd2000000000000000000000000000000000000000000000000000000008152306004820152826024820152828160448183865af18015610b2f57610b1a575b602082604051908152f35b610b25838092610c19565b6101b35781610b0f565b6040513d85823e3d90fd5b610b479192505f90610c19565b5f905f610ac1565b6040513d5f823e3d90fd5b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b50600154906109b6565b9050610ba081610bc6565b610ba981610bc6565b155f6109ad565b600435906001600160a01b0382168203610b5a57565b60021115610bd057565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b6080810190811067ffffffffffffffff821117610b5e57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610b5e57604052565b6001600160a01b035f54163303610c6d57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fdfe60e03461015f57601f61387738819003918201601f19168301916001600160401b038311848410176101635780849260609460405283398101031261015f578051602082015190916001600160a01b0382169182900361015f576040015191600283101561015f57335f525f60205260405f20600160ff1982541617905560018055604051600181527fe529461c8529abc0e0fe7c5ee361f74fe22e0b7574df1fc0b7558a282091fb7860203392a281156101505760c052600580546001600160a01b0319169190911790556080524360a0526040516136ff9081610178823960805181818161027b015281816106df01528181610ea40152818161115e015281816114c601528181611652015281816118b801528181612065015281816129ca01528181612a87015261331d015260a05181611be0015260c0518181816105b1015281816113ed015261296f0152f35b63e6c4247b60e01b5f5260045ffd5b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6080806040526004361015610012575f80fd5b5f3560e01c908163137c68fa146125d45750806324d7806c146125985780632b7832b31461257b5780632eb7d434146124c257806343ff9d8d146124a55780634a7be739146124245780634b0bddd2146122b557806354f1c9041461214b5780635650d7d714611fd05780635edc1c8814611f0f578063661b743d14611dd957806367a421b614611daf5780636d63b7ff14611cfa578063775907f714611c035780638635512f14611bc957806389a9c7b414610fcb5780638a85cbba14611b9e5780638c5143ea14611b7b5780639335af1a14611a8e5780639fb40e7b1461192e578063a3ba6c7a14611880578063b6b00a501461184d578063b74513c1146117c4578063b8900aa71461159c578063b9ed715314611574578063ba48042b146114f6578063ba50bba5146114b2578063bb64fd4414611494578063bd66528a14611436578063c415b95c14611410578063c57981b5146113d6578063caaa9a2214611107578063cdc6aa4c14610ffe578063cedc9d1d14610fcb578063cf61a09714610fb1578063d0e8bd4614610e6b578063d1bc76a114610e2a578063d77836ce14610d58578063d955d6eb14610d03578063e50373f914610b05578063e5b3248714610a9f578063e865fbc714610a6c578063e940e5381461063a578063ecb62f6114610564578063f0e1efa0146105475763f5ee64c014610216575f80fd5b60406003193601126105435760043567ffffffffffffffff8111610543576102429036906004016126c6565b60243567ffffffffffffffff8111610543576102629036906004016126c6565b919092335f525f60205260ff60405f2054161561051b577f00000000000000000000000000000000000000000000000000000000000000006102a3816126a8565b156104015781156104f3578282036104cb573415610483575f5f5b8381106104ab57503403610483575f5b8281106102d757005b6102e2818484612b05565b35906102ef818688612b05565b35801561047957825f52600b60205260405f2080546001600160a01b038116156104515760e01c61042957806004610333600261033c9401546003840154906127e6565b910154906127e6565b92805f52600860205260405f2054848110156104015761035c90856128f5565b82116103d9577f7d880d31c520e2aafc7d84cd9b7b8519c22fa45a2682a2b09035af562b3820a4600194825f52600860205260405f2061039d8582546127e6565b90556103ab846002546127e6565b6002555f83815260086020908152604091829020548251968752908601528401523392606090a35b016102ce565b7fb6b88bbc000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fbbc4bdcf000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f48a0eba2000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f3a605dd2000000000000000000000000000000000000000000000000000000005f5260045ffd5b50600191506103d3565b7f57e000b1000000000000000000000000000000000000000000000000000000005f5260045ffd5b906104c46001916104bd84888a612b05565b35906127e6565b91016102be565b7fa24a13a6000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f521299a9000000000000000000000000000000000000000000000000000000005f5260045ffd5b7ffdd63ca2000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f80fd5b34610543575f600319360112610543576020600354604051908152f35b60206003193601126105435760043567ffffffffffffffff8111610543576105909036906004016126c6565b90335f525f60205260ff60405f2054161561051b5781156104f3576105d5827f0000000000000000000000000000000000000000000000000000000000000000612b7a565b3403610612576105e7346003546127e6565b6003555f5b8281106105f557005b8061060c6106066001938686612b05565b356132bf565b016105ec565b7f025dbdd4000000000000000000000000000000000000000000000000000000005f5260045ffd5b60206003193601126105435760043567ffffffffffffffff81116105435780600401610140600319833603011261054357335f525f60205260ff60405f2054161561051b576106898180612b15565b602484019291508061069b8484612b15565b905014801590610a54575b8015610a3c575b8015610a24575b8015610a0c575b80156109f4575b80156109dc575b80156109c3575b80156109aa575b6104cb5760017f0000000000000000000000000000000000000000000000000000000000000000610707816126a8565b0361092157346108f957925b5f9060448101606482016084830160a484019060c485019260e486019461012461010488019701975b8b811061074557005b60045461075181612738565b600455610768826107628d80612b15565b90612b05565b356001600160a01b038116809103610543578a8a8a8f8f8a8a8a8f948f948c80808080808080610799819a8a612b15565b6107a39291612b05565b6107ac90612b69565b996107b79089612b15565b6107c19291612b05565b6107ca90612b69565b996107d59088612b15565b6107df9291612b05565b6107e890612b69565b996107f39087612b15565b6107fd9291612b05565b35996108099086612b15565b6108139291612b05565b61081c90612b69565b996108279085612b15565b6108319291612b05565b359961083d9084612b15565b6108479291612b05565b61085090612b69565b9961085b9083612b15565b6108659291612b05565b359961087091612b15565b61087a9291612b05565b3598891515809a03610543576040519a6108938c6127f3565b8b5260208b015263ffffffff1660408a015263ffffffff16606089015263ffffffff16608088015260a087015263ffffffff1660c086015260e085015263ffffffff166101008401526101208301526101408201526108f190612cf7565b60010161073c565b7fbb21bfd4000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f8060848601610104870160c488015b8686861061095b5750505050809150340361048357610952906002546127e6565b60025592610713565b8394610999826104bd8961076288610993896104bd8561076260019d9e9f8261076261099f9f9261098b93612b15565b35938a612b15565b94612b15565b906127e6565b940193929190610931565b50806109ba610124860184612b15565b905014156106d7565b50806109d3610104860184612b15565b905014156106d0565b50806109eb60e4860184612b15565b905014156106c9565b5080610a0360a4860184612b15565b905014156106c2565b5080610a1b60c4860184612b15565b905014156106bb565b5080610a336084860184612b15565b905014156106b4565b5080610a4b6064860184612b15565b905014156106ad565b5080610a636044860184612b15565b905014156106a6565b34610543575f600319360112610543576020610a97610a8e47600254906128f5565b600354906128f5565b604051908152f35b34610543576020600319360112610543576001600160a01b03610ac06125ee565b168015610add575f526009602052602060405f2054604051908152f35b7fe6c4247b000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461054357602060031936011261054357610b1e6125ee565b335f525f60205260ff60405f2054161561051b576001600160a01b03168015610add57604051907f70a08231000000000000000000000000000000000000000000000000000000008252306004830152602082602481845afa908115610cf8575f91610cc2575b610c1f92505f806040519360208501907fa9059cbb000000000000000000000000000000000000000000000000000000008252336024870152604486015260448552610bd260648661282d565b60405194610be160408761282d565b602086527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020870152519082855af1610c1961292e565b91613616565b8051908115918215610c9f575b505015610c3557005b608460405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b819250906020918101031261054357602001518015158103610543578180610c2c565b90506020823d602011610cf0575b81610cdd6020938361282d565b8101031261054357610c1f915190610b85565b3d9150610cd0565b6040513d5f823e3d90fd5b3461054357604060031936011261054357610d1c6125ee565b6001600160a01b0360243591165f52600960205260405f20805482101561054357602091610d4991612723565b90549060031b1c604051908152f35b3461054357602060031936011261054357600435335f525f60205260ff60405f2054161561051b57610d90610a8e47600254906128f5565b8111610e02575f808080936040518181527fca1cf43de312865665f595e88f569f9d5246690c07df26e86aba01147e6d131460203392a2335af1610dd261292e565b5015610dda57005b7f90b8ec18000000000000000000000000000000000000000000000000000000005f5260045ffd5b7ff4d678b8000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461054357602060031936011261054357600435600654811015610543576001600160a01b03610e5b6020926126f7565b90549060031b1c16604051908152f35b602060031936011261054357600435805f52600b60205260405f205460e01c61042957335f525f60205260ff60405f2054161561051b577f0000000000000000000000000000000000000000000000000000000000000000610ecc816126a8565b156104015734156108f957805f52600b60205260405f206001600160a01b0381541615610451578060046103336002610f0c9401546003840154906127e6565b90805f52600860205260405f20548281101561040157610f2c90836128f5565b34116103d957805f52600860205260405f20610f493482546127e6565b9055610f57346002546127e6565b600255805f5260086020527f7d880d31c520e2aafc7d84cd9b7b8519c22fa45a2682a2b09035af562b3820a4610fac60405f205493604051918291339634846040919493926060820195825260208201520152565b0390a3005b34610543575f6003193601126105435760206040515f8152f35b34610543576020600319360112610543576004355f52600c60205260206001600160a01b0360405f205416604051908152f35b3461054357606060031936011261054357600435602435604435916001600160a01b038316809303610543578215610add578181108015906110f2575b6110ca5761104981836128f5565b92601f1961106f61105986612902565b95611067604051978861282d565b808752612902565b01366020860137815b838110611091576040518061108d878261266f565b0390f35b600190825f5260096020526110a98160405f20612723565b90549060031b1c6110c36110bd86846128f5565b8861291a565b5201611078565b7f561ce9bb000000000000000000000000000000000000000000000000000000005f5260045ffd5b50825f52600960205260405f2054821161103b565b346105435760206003193601126105435760043567ffffffffffffffff8111610543576111389036906004016126c6565b90335f525f60205260ff60405f2054161561051b5781156104f3575f63ffffffff4216927f00000000000000000000000000000000000000000000000000000000000000009060018214925b81811061118d57005b611198818387612b05565b3590815f52600b60205260405f205460e01c61042957815f52600b60205260405f209163ffffffff835460c01c16804210156113ae576006840160ff81541615611386578961122a6101a0928761122f7ffab6a0d43d3a59dd207bc8acbb42f638ace4f051bc53d95dd662fbfa5bc0213a9661121e8661121960019d612850565b61342a565b94859161121985612850565b6128f5565b928c61123a8d6126a8565b611343575b508061129860ff9495600593907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffff0000000000000000000000000000000000000000000000000000000083549260e01b169116179055565b6112a4856002546128f5565b60025560405194855280546001600160a01b038116602087015263ffffffff8160a01c16604087015263ffffffff8160c01c16606087015260e01c608086015263ffffffff8a82015481811660a0880152818160201c1660c088015260401c1660e0860152600281015461010086015260038101546101208601526004810154610140860152015461016084015254161515610180820152a201611184565b5f8881526008602052604090205460ff945060059291908181116113735750506112985f955b959450509061123f565b61129891611380916128f5565b95611369565b7fce21cad1000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f2e8e455d000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610543575f6003193601126105435760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b34610543575f6003193601126105435760206001600160a01b0360055416604051908152f35b602060031936011261054357600435805f52600b6020526001600160a01b0360405f205416330361146c5761146a9061296d565b005b7f75228a9c000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610543576020600319360112610543576020610a97600435612a29565b34610543575f6003193601126105435760207f0000000000000000000000000000000000000000000000000000000000000000604051906114f2816126a8565b8152f35b34610543576020600319360112610543576001600160a01b036115176125ee565b165f52600960205260405f206040519081602082549182815201915f5260205f20905f5b81811061155e5761108d856115528187038261282d565b6040519182918261266f565b825484526020909301926001928301920161153b565b346105435760206003193601126105435760206115926004356129ac565b6040519015158152f35b3461054357602060031936011261054357600435335f525f60205260ff60405f2054161561051b57805f52600b60205260405f205460e01c61042957805f52600b60205260405f2063ffffffff815460c01c16804210156113ae57600682019060ff825416156113865761122a6101a0928461164d61163b7ffab6a0d43d3a59dd207bc8acbb42f638ace4f051bc53d95dd662fbfa5bc0213a97612850565b9461121e63ffffffff4216809761342a565b9260017f000000000000000000000000000000000000000000000000000000000000000061167a816126a8565b14611781575b50806116d960ff9495600593907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffff0000000000000000000000000000000000000000000000000000000083549260e01b169116179055565b6116e5856002546128f5565b60025560405194855280546001600160a01b038116602087015263ffffffff8160a01c16604087015263ffffffff8160c01c16606087015260e01c608086015263ffffffff600182015481811660a0880152818160201c1660c088015260401c1660e0860152600281015461010086015260038101546101208601526004810154610140860152015461016084015254161515610180820152a2005b5f8881526008602052604090205460ff945060059291908181116117b15750506116d95f955b9594505090611680565b6116d9916117be916128f5565b956117a7565b34610543575f6003193601126105435760405180602060065491828152019060065f527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f905f5b81811061182e5761108d856118228187038261282d565b6040519182918261262d565b82546001600160a01b031684526020909301926001928301920161180b565b34610543576040600319360112610543576020610a9761186b61261a565b6004355f52600b835261121960405f20612850565b3461054357602060031936011261054357600435805f52600b60205260405f206001600160a01b0381541615610451576060916118f67f0000000000000000000000000000000000000000000000000000000000000000926118e1846126a8565b600461033360028301546003840154906127e6565b90611900836126a8565b8261191d5750805b60ff6040519316835260208301526040820152f35b5f52600860205260405f2054611908565b3461054357602060031936011261054357600435805f52600b60205260405f205460e01c61042957805f52600c6020526001600160a01b0360405f205416338103611a6657815f52600b60205260405f206001600160a01b03815416906119958483612b8d565b6001600160a01b03831673ffffffffffffffffffffffffffffffffffffffff198254161790556119c482612c91565b15611a48575b815f5260096020526119df8360405f206127a5565b5f82815260096020908152604080832054600a8352818420878552835281842055600c9091528120805473ffffffffffffffffffffffffffffffffffffffff191690557fd26d820c791a6bb2143a3066ee2a8314ff045a3abc3f256346814ad5be7d63469080a4005b611a5182612746565b600654825f52600760205260405f20556119ca565b7fc414c24a000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461054357604060031936011261054357600435611aaa612604565b815f52600b6020526001600160a01b0360405f205416330361146c57815f52600b60205260405f205460e01c610429576001600160a01b03168015610add57815f52600c6020526001600160a01b0360405f205416611b5357815f52600c60205260405f208173ffffffffffffffffffffffffffffffffffffffff19825416179055337f666421a92fd891d9481da847947b32bfd485b5a6ac70ef477f18b9aae576c3715f80a4005b7f8f524afa000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610543576020600319360112610543576020611592611b996125ee565b612c91565b602060031936011261054357335f525f60205260ff60405f2054161561051b5761146a60043561296d565b34610543575f6003193601126105435760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461054357604060031936011261054357611c1c6125ee565b602435906001600160a01b03600554163303611cd2576001600160a01b038116828115610add576003549081156106125715611cca575b808411611ca2575f8481948294611c6b8385966128f5565b6003557fee3a3e8b975ee1a894fd6ed0a36ec6d1db3dc70e575382d21e9aed5a2c72f5146020604051858152a25af1610dd261292e565b7f732f9413000000000000000000000000000000000000000000000000000000005f5260045ffd5b925082611c53565b7f73fcd3fe000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461054357604060031936011261054357600435602435808210801590611da4575b6110ca57611d2a82826128f5565b91601f19611d50611d3a85612902565b94611d48604051968761282d565b808652612902565b01366020850137805b828110611d6e576040518061108d868261262d565b806001600160a01b03611d826001936126f7565b90549060031b1c16611d9d611d9785846128f5565b8761291a565b5201611d59565b506006548111611d1c565b34610543576020600319360112610543576004355f526008602052602060405f2054604051908152f35b34610543576020600319360112610543575f610160604051611dfa81612810565b8281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e082015282610100820152826101208201528261014082015201526004355f52600b60205261108d611e5760405f20612850565b60405191829182919091610160806101808301946001600160a01b03815116845263ffffffff602082015116602085015263ffffffff604082015116604085015263ffffffff606082015116606085015263ffffffff608082015116608085015263ffffffff60a08201511660a085015263ffffffff60c08201511660c085015260e081015160e085015261010081015161010085015261012081015161012085015261014081015161014085015201511515910152565b3461054357602060031936011261054357600435805f52600b6020526001600160a01b0360405f205416330361146c57805f52600c6020526001600160a01b0360405f20541615611fa857805f52600c60205260405f2073ffffffffffffffffffffffffffffffffffffffff198154169055337f155ceda63be6f9c02f83b108c17e45601c8e72e9845fd5a15c4c33b53e8cac1b5f80a3005b7fd5e80fb4000000000000000000000000000000000000000000000000000000005f5260045ffd5b61014060031936011261054357611fe56125ee565b611fed61261a565b60443563ffffffff81168091036105435760643563ffffffff81168091036105435760843560a43563ffffffff81168091036105435760c4359060e4359263ffffffff8416809403610543576101043594610124359687151580980361054357335f525f60205260ff60405f2054161561051b5760017f000000000000000000000000000000000000000000000000000000000000000061208d816126a8565b0361210457346108f95763ffffffff6120c6996001600160a01b0361146a9c5b6004546120b981612738565b6004556040519d8e6127f3565b8d521660208c01521660408a01526060890152608088015260a087015260c086015260e0850152610100840152610120830152610140820152612cf7565b61211a8761211587869d9c9d6127e6565b6127e6565b98893403610e025761146a9a6001600160a01b0363ffffffff926121436120c69d6002546127e6565b6002556120ad565b3461054357604060031936011261054357600435612167612604565b815f52600b6020526001600160a01b0360405f205416330361146c57815f52600b60205260405f205460e01c610429576001600160a01b038116908115610add57825f52600b60205260405f20906001600160a01b03825416916121cb8584612b8d565b8373ffffffffffffffffffffffffffffffffffffffff198254161790556121f181612c91565b15612296575b50815f52600960205261220d8360405f206127a5565b5f82815260096020908152604080832054600a8352818420878552835281842055600c9091529020546001600160a01b031661226a575b7fd26d820c791a6bb2143a3066ee2a8314ff045a3abc3f256346814ad5be7d63465f80a4005b825f52600c60205260405f2073ffffffffffffffffffffffffffffffffffffffff198154169055612244565b61229f90612746565b600654825f52600760205260405f2055836121f7565b34610543576040600319360112610543576122ce6125ee565b602435908115159081830361054357335f525f60205260ff60405f2054161561051b576001600160a01b0316918215610add57825f525f6020528160ff60405f2054161515146123fc57801561237a577fe529461c8529abc0e0fe7c5ee361f74fe22e0b7574df1fc0b7558a282091fb7891612371602092612351600154612738565b6001555b855f525f845260405f209060ff60ff1983541691151516179055565b604051908152a2005b6001549160018311156123d45782156123c0576123716020927fe529461c8529abc0e0fe7c5ee361f74fe22e0b7574df1fc0b7558a282091fb78945f1901600155612355565b634e487b7160e01b5f52601160045260245ffd5b7fc13a62ad000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fd52dfc1c000000000000000000000000000000000000000000000000000000005f5260045ffd5b346105435760206003193601126105435761243d6125ee565b600554906001600160a01b03821690813303611cd2576001600160a01b0316918215610add5773ffffffffffffffffffffffffffffffffffffffff191682176005557f5d16ad41baeb009cd23eb8f6c7cde5c2e0cd5acf4a33926ab488875c37c37f385f80a3005b34610543575f600319360112610543576020600654604051908152f35b34610543576020600319360112610543576004355f52600b60205261018060405f208054906001810154906002810154600382015460048301549163ffffffff60ff600660058701549601541695604051976001600160a01b0381168952828160a01c1660208a0152828160c01c1660408a015260e01c60608901528181166080890152818160201c1660a089015260401c1660c087015260e08601526101008501526101208401526101408301521515610160820152f35b34610543575f600319360112610543576020600154604051908152f35b34610543576020600319360112610543576001600160a01b036125b96125ee565b165f525f602052602060ff60405f2054166040519015158152f35b34610543575f600319360112610543576020906002548152f35b600435906001600160a01b038216820361054357565b602435906001600160a01b038216820361054357565b6024359063ffffffff8216820361054357565b60206040818301928281528451809452019201905f5b8181106126505750505090565b82516001600160a01b0316845260209384019390920191600101612643565b60206040818301928281528451809452019201905f5b8181106126925750505090565b8251845260209384019390920191600101612685565b600211156126b257565b634e487b7160e01b5f52602160045260245ffd5b9181601f840112156105435782359167ffffffffffffffff8311610543576020808501948460051b01011161054357565b60065481101561270f5760065f5260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b805482101561270f575f5260205f2001905f90565b5f1981146123c05760010190565b60065490680100000000000000008210156127915761277082600161278f94016006556006612723565b9091906001600160a01b038084549260031b9316831b921b1916179055565b565b634e487b7160e01b5f52604160045260245ffd5b8054906801000000000000000082101561279157816127cc9160016127e294018155612723565b819391549060031b91821b915f19901b19161790565b9055565b919082018092116123c057565b610160810190811067ffffffffffffffff82111761279157604052565b610180810190811067ffffffffffffffff82111761279157604052565b90601f601f19910116810190811067ffffffffffffffff82111761279157604052565b9060405161285d81612810565b61016060ff6006839580546001600160a01b038116865263ffffffff8160a01c16602087015263ffffffff8160c01c16604087015260e01c606086015263ffffffff60018201548181166080880152818160201c1660a088015260401c1660c0860152600281015460e08601526003810154610100860152600481015461012086015260058101546101408601520154161515910152565b919082039182116123c057565b67ffffffffffffffff81116127915760051b60200190565b805182101561270f5760209160051b010190565b3d15612968573d9067ffffffffffffffff8211612791576040519161295d601f8201601f19166020018461282d565b82523d5f602084013e565b606090565b7f000000000000000000000000000000000000000000000000000000000000000034036106125761278f906129a4346003546127e6565b6003556132bf565b805f52600b60205260405f206001600160a01b0381541615610451577f00000000000000000000000000000000000000000000000000000000000000006129f2816126a8565b15612a22578060046103336002612a109401546003840154906127e6565b905f52600860205260405f2054101590565b5050600190565b805f52600b60205260405f206001600160a01b03815416156104515763ffffffff60018201541663ffffffff4216809111612afe57612a6d60059161121984612850565b91015480821115612af857612a8281836128f5565b9260017f0000000000000000000000000000000000000000000000000000000000000000612aaf816126a8565b14612abb575b50505090565b5f52600860205260405f2054918210612ad5575b80612ab5565b90915080821115612af057612ae9916128f5565b5f80612acf565b50505f612ae9565b5f612a82565b5050505f90565b919081101561270f5760051b0190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610543570180359067ffffffffffffffff821161054357602001918160051b3603831361054357565b3563ffffffff811681036105435790565b818102929181159184041417156123c057565b906001600160a01b03821690815f52600960205260405f2091805f52600a60205260405f20825f5260205260405f20548015612c8a575f1981018181116123c05784545f198101919082116123c057818103612c49575b50505082548015612c35575f1901612bfc8185612723565b8154905f199060031b1b1916905583555f52600a60205260405f20905f526020525f60408120555415612c2c5750565b61278f9061355c565b634e487b7160e01b5f52603160045260245ffd5b612c69612c596127cc9388612723565b90549060031b1c92839288612723565b9055825f52600a60205260405f20905f5260205260405f20555f8080612be4565b5050505050565b6001600160a01b03168015610add575f52600760205260405f2054151590565b9063ffffffff8091169116039063ffffffff82116123c057565b9063ffffffff16908115612ce35763ffffffff160690565b634e487b7160e01b5f52601260045260245ffd5b602081016001600160a01b0381511615610add57610120820191825192612d2460a08301948551906127e6565b612d3460e08401918251906127e6565b1561329757604083019163ffffffff8351161561326f5763ffffffff83511690606085019163ffffffff8351161061323d5763ffffffff84511663ffffffff8351161480613265575b61323d57610100850163ffffffff815116156132155760c08601805163ffffffff168061317d575084516131555763ffffffff612dce612dc482875116838a511690612cb1565b8285511690612ccb565b1661312d575b63ffffffff806001600160a01b038a51169751169451169863ffffffff808060808b015116925193511696519351169351936101408901511515966040519b8c99612e1e8b612810565b8a5260208a0197885260408a0190815260608a01905f825260808b0194855260a08b0193845260c08b0192835260e08b019586526101008b019687526101208b019788526101408b01985f8a526101608c019a8b528d612e876001600160a01b03825116612c91565b156130f9575b508c515f908152600b602052604090209b519051915192517fffffffff0000000000000000000000000000000000000000000000000000000060e09190911b1660c09390931b7bffffffff000000000000000000000000000000000000000000000000166001600160a01b0390911660a09290921b77ffffffff000000000000000000000000000000000000000016919091171717895560018901925163ffffffff168354925160201b67ffffffff0000000016915160401b6bffffffff000000000000000016927fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001617171790555160028601555160038501555160048401555160058301555115159060060190612fb2919060ff60ff1983541691151516179055565b81516001600160a01b03165f52600960205260405f208151612fd3916127a5565b81516001600160a01b03165f52600960205260405f205482516001600160a01b03165f52600a60205260405f2082515f5260205260405f20555190516001600160a01b0316916040516130d3819282919091610160806101808301946001600160a01b03815116845263ffffffff602082015116602085015263ffffffff604082015116604085015263ffffffff606082015116606085015263ffffffff608082015116608085015263ffffffff60a08201511660a085015263ffffffff60c08201511660c085015260e081015160e085015261010081015161010085015261012081015161012085015261014081015161014085015201511515910152565b037f040a2d252643cef2c4dea3c242e5ed4bf32a76631de8342604dd6cba3074047291a3565b6001600160a01b0361310c915116612746565b8d6001600160a01b036006549151165f52600760205260405f20558d612e8d565b7f28df2855000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fea243c32000000000000000000000000000000000000000000000000000000005f5260045ffd5b8063ffffffff88511611908115613203575b506131db5763ffffffff6131ad612dc4828751168385511690612cb1565b1615612dd4577f28df2855000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f9ce30e12000000000000000000000000000000000000000000000000000000005f5260045ffd5b905063ffffffff85511611155f61318f565b7f98e089af000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f417de2db000000000000000000000000000000000000000000000000000000005f5260045ffd5b5080511515612d7d565b7ffebd12a0000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f17329d67000000000000000000000000000000000000000000000000000000005f5260045ffd5b805f52600b60205260405f206001600160a01b03815416156104515763ffffffff60018201541663ffffffff42168091116133f8576133019061121983612850565b9060058101805461331281856128f5565b938415610e025760017f0000000000000000000000000000000000000000000000000000000000000000613345816126a8565b146133b9575b50925f946001600160a01b038695948695946133688588976127e6565b9055613376846002546128f5565b600255541680917f0508a8b4117d9a7b3d8f5895f6413e61b4f9a2df35afbfb41e78d0ecfff1843f6020604051868152a35af16133b161292e565b5015610dda57565b855f52600860205260405f20549081101561334b5790935083808211156133f0576133e3916128f5565b801561048357925f61334b565b50505f6133e3565b7f779ecdd8000000000000000000000000000000000000000000000000000000005f5260045ffd5b8115612ce3570490565b63ffffffff60608201511680613546575b505f91604082019063ffffffff8251168063ffffffff83161161353e575b506020830163ffffffff80825116921691821015613532575b60c0840163ffffffff815116831015613514575b5163ffffffff169081156135055750905b8181116134a6575b5050505090565b610999926101206134ef8463ffffffff6134e76134cb6134fc9b9a986134f7986128f5565b826134de60a08b01928284511690613420565b91511690612b7a565b9451166128f5565b930151612b7a565b613420565b5f80808061349f565b63ffffffff9150511690613497565b9461352a63ffffffff91610100870151906127e6565b959050613486565b60e08401519450613472565b90505f613459565b8063ffffffff8416111561343b5791505f61343b565b6001600160a01b0316805f52600760205260405f20548015613612575f1981018181116123c0576006545f1981019081116123c0578082036135d6575b5050506006548015612c35575f19016135b1816126f7565b6001600160a01b0382549160031b1b191690556006555f5260076020525f6040812055565b612770916001600160a01b036135ee6135fe936126f7565b90549060031b1c169283916126f7565b5f52600760205260405f20555f8080613599565b5050565b91929015613677575081511561362a575090565b3b156136335790565b606460405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b82519091501561368a5750805190602001fd5b6044602091601f19601f60405194859362461bcd60e51b85528160048601528051918291826024880152018686015e5f85828601015201168101030190fdfea26469706673582212208ebb5b01582fd089a65e597c8ee020bc44e837c16366968063e92facabd1ca2364736f6c634300081c0033a2646970667358221220c7ffc9434ffe73932b736169e969b939f5887a035946eb6bc17583ba4d22458864736f6c634300081c0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000038cb1b3bd9779b4afc9144854dd0e71e47d000ca0000000000000000000000000000000000000000000000000001476b081e800000000000000000000000000000000000000000000000000000000000000001f4
-----Decoded View---------------
Arg [0] : feeCollector_ (address): 0x38Cb1b3BD9779b4AFc9144854Dd0e71E47D000CA
Arg [1] : defaultGasFee_ (uint256): 360000000000000
Arg [2] : defaultTokenFee_ (uint256): 500
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 00000000000000000000000038cb1b3bd9779b4afc9144854dd0e71e47d000ca
Arg [1] : 0000000000000000000000000000000000000000000000000001476b081e8000
Arg [2] : 00000000000000000000000000000000000000000000000000000000000001f4
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.