ETH Price: $1,993.18 (+2.90%)

Contract

0x7A4F91e5d6ac7901E1E1fCFd2a7D0F61395f1df3
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Transfer Ownersh...236463352025-10-24 9:09:11136 days ago1761296951IN
0x7A4F91e5...1395f1df3
0 ETH0.000061682.14743239
Set Default Gas ...236463252025-10-24 9:07:11136 days ago1761296831IN
0x7A4F91e5...1395f1df3
0 ETH0.000064822.14058156
Set Fee Collecto...236462042025-10-24 8:42:35136 days ago1761295355IN
0x7A4F91e5...1395f1df3
0 ETH0.000004230.13897876

Advanced mode:
Parent Transaction Hash Method Block
From
To
View All Internal Transactions
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
NativeTokenVestingManagerFactory

Compiler Version
v0.8.28+commit.7893614a

Optimization Enabled:
Yes with 2000 runs

Other Settings:
cancun EvmVersion
// 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);
}

File 3 of 15 : NativeTokenVestingManager.sol
// 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();
		}
	}
}

File 4 of 15 : ITypes.sol
// 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;
	}
}

File 9 of 15 : Errors.sol
// 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();
}

File 10 of 15 : INativeTokenVestingManager.sol
// 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;
    }
}

Settings
{
  "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

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"}]

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


Block Uncle Number Difficulty Gas Used Reward
View All Uncles
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.