ETH Price: $1,978.04 (-5.08%)

Contract

0xE8B1a71fe1AdDFbcA59A532371D510553de92116
 

Overview

ETH Balance

0.013967997765120324 ETH

Eth Value

$27.63 (@ $1,978.04/ETH)

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Claim243658972026-02-02 1:29:2333 days ago1769995763IN
0xE8B1a71f...53de92116
0.00038799 ETH0.000031560.29602619
Claim242244332026-01-13 7:53:5952 days ago1768290839IN
0xE8B1a71f...53de92116
0.00038799 ETH0.000004990.04684207
Claim241852022026-01-07 20:30:2358 days ago1767817823IN
0xE8B1a71f...53de92116
0.00038799 ETH0.000011090.10402525
Claim241554762026-01-03 17:00:2362 days ago1767459623IN
0xE8B1a71f...53de92116
0.00038799 ETH0.000006290.07030784
Claim241316802025-12-31 9:21:5965 days ago1767172919IN
0xE8B1a71f...53de92116
0.00038799 ETH0.000061460.57642339
Claim241129622025-12-28 18:35:5968 days ago1766946959IN
0xE8B1a71f...53de92116
0.00038799 ETH0.000217122.03624474
Claim241063642025-12-27 20:30:2369 days ago1766867423IN
0xE8B1a71f...53de92116
0.00038799 ETH0.000002730.03053841
Claim241027172025-12-27 8:17:4769 days ago1766823467IN
0xE8B1a71f...53de92116
0.00038799 ETH0.000003370.03773288
Claim240983352025-12-26 17:36:2370 days ago1766770583IN
0xE8B1a71f...53de92116
0.00038799 ETH0.00000530.04972385
Claim240982092025-12-26 17:10:5970 days ago1766769059IN
0xE8B1a71f...53de92116
0.00038799 ETH0.000056250.52756475
Claim240977362025-12-26 15:35:4770 days ago1766763347IN
0xE8B1a71f...53de92116
0.00038799 ETH0.000264542.48092558
Claim240959202025-12-26 9:30:2370 days ago1766741423IN
0xE8B1a71f...53de92116
0.00038799 ETH0.00000280.02627367
Claim240953582025-12-26 7:37:4770 days ago1766734667IN
0xE8B1a71f...53de92116
0.00038799 ETH0.000008660.08125452
Claim240869642025-12-25 3:30:5971 days ago1766633459IN
0xE8B1a71f...53de92116
0.00038799 ETH0.000216022.02595147
Claim240844452025-12-24 19:03:1172 days ago1766602991IN
0xE8B1a71f...53de92116
0.00038799 ETH0.000003660.04095148
Claim240843002025-12-24 18:34:1172 days ago1766601251IN
0xE8B1a71f...53de92116
0.00038799 ETH0.00000410.03851002
Claim240839042025-12-24 17:14:2372 days ago1766596463IN
0xE8B1a71f...53de92116
0.00038799 ETH0.000006210.05824442
Claim240835432025-12-24 16:02:1172 days ago1766592131IN
0xE8B1a71f...53de92116
0.00038799 ETH0.000006640.06234619
Claim240827862025-12-24 13:29:5972 days ago1766582999IN
0xE8B1a71f...53de92116
0.00038799 ETH0.000004010.03767265
Claim240827522025-12-24 13:23:1172 days ago1766582591IN
0xE8B1a71f...53de92116
0.00038799 ETH0.000216242.02801184
Claim240827012025-12-24 13:12:5972 days ago1766581979IN
0xE8B1a71f...53de92116
0.00038799 ETH0.000002970.0279177
Claim240825892025-12-24 12:50:3572 days ago1766580635IN
0xE8B1a71f...53de92116
0.00038799 ETH0.000003270.03068322
Claim240825182025-12-24 12:36:2372 days ago1766579783IN
0xE8B1a71f...53de92116
0.00038799 ETH0.000215922.02500584
Claim240823022025-12-24 11:52:5972 days ago1766577179IN
0xE8B1a71f...53de92116
0.00038799 ETH0.000181542.02776307
Claim240822102025-12-24 11:34:3572 days ago1766576075IN
0xE8B1a71f...53de92116
0.00038799 ETH0.00005730.5373977
View all transactions

Latest 2 internal transactions

Advanced mode:
Parent Transaction Hash Method Block
From
To
Claim241045922025-12-27 14:33:4769 days ago1766846027
0xE8B1a71f...53de92116
0.00038799 ETH
0x61012034240455602025-12-19 8:51:1177 days ago1766134271  Contract Creation0 ETH
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

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0x40CB26d0...943d782Ba
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
TokenVestingManager

Compiler Version
v0.8.28+commit.7893614a

Optimization Enabled:
Yes with 2000 runs

Other Settings:
cancun EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 12 : TokenVestingManager.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;

import { SafeERC20, IERC20 } 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 { ITokenVestingManager } from "contracts/interfaces/ITokenVestingManager.sol";
import { ITypes } from "contracts/interfaces/ITypes.sol";

/**
 * @title Token Vesting Manager
 * @notice Contract to manage token vesting
 */
contract TokenVestingManager is AccessProtected, ITokenVestingManager {
	using TokenVestingLib for TokenVestingLib.Vesting;
	using SafeERC20 for IERC20;

	/// Basis points for fee calculation
	uint256 public constant BASIS_POINTS = 10000;

	/// Address of the token to be vested
	address public immutable TOKEN_ADDRESS;
	/// Fee type for this contract
	ITypes.FeeType public immutable FEE_TYPE;
	/// 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 tokens reserved for vesting
	uint256 public numTokensReservedForVesting;
	/// Number of tokens reserved for fees
	uint256 public numTokensReservedForFee;
	/// 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
	/// Holds the vesting ids for each recipient
	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
	/// Holds the vesting information for each vesting id
	mapping(bytes32 => TokenVestingLib.Vesting) public vestingById;
	/// Mapping: vestingId => newOwner
	/// Tracks pending ownership transfers for each vesting ID
	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 Sets the token address to be vested
	 * @param tokenAddress_ - Address of the token to be vested
	 * @param feeType_ - Fee type for this contract
	 * @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(
		address tokenAddress_,
		ITypes.FeeType feeType_,
		uint256 fee_,
		address feeCollector_,
		ITypes.FundingType fundingType_
	) {
		if (tokenAddress_ == address(0) || feeCollector_ == address(0))
			revert Errors.InvalidAddress();
		if (feeType_ == ITypes.FeeType.DistributionToken && fee_ > BASIS_POINTS)
			revert Errors.FeeTooHigh();

		TOKEN_ADDRESS = tokenAddress_;
		FEE_TYPE = feeType_;
		FEE = fee_;
		feeCollector = feeCollector_;
		FUNDING_TYPE = fundingType_;
		DEPLOYMENT_BLOCK_NUMBER = block.number;
	}

	// EXTERNAL WRITE FUNCTIONS

	/**
	 * @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 onlyAdmin {
		uint256 totalExpectedAmount = _initialUnlock +
			_cliffAmount +
			_linearVestAmount;

		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
			})
		);

		// For partial funding, the vesting is created first then funded separately
		if (FUNDING_TYPE == ITypes.FundingType.Full) {
			numTokensReservedForVesting += totalExpectedAmount;

			IERC20(TOKEN_ADDRESS).safeTransferFrom(
				msg.sender,
				address(this),
				totalExpectedAmount
			);
		}
	}

	/**
	 * @notice Create a batch of vestings
	 * @param params - Parameters for creating the vesting batch
	 */
	function createVestingBatch(
		CreateVestingBatchParams calldata params
	) external onlyAdmin {
		uint256 length = params._recipients.length;

		uint256 totalExpectedAmount;
		bytes32[] memory newVestingIds = new bytes32[](length);

		// Check array lengths match
		_checkArrayLengthMismatch(params, length);

		for (uint256 i; i < length; ++i) {
			bytes32 vestingId = bytes32(vestingIdNonce++);
			newVestingIds[i] = vestingId;
			_createVesting(
				TokenVestingLib.VestingParams({
					_vestingId: newVestingIds[i],
					_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]
				})
			);

			uint256 vestingAmount = params._initialUnlocks[i] +
				params._cliffAmounts[i] +
				params._linearVestAmounts[i];

			totalExpectedAmount += vestingAmount;
		}

		// In full funding mode, we require the full amount to be transferred
		// Avoid using the vestingFunding mapping for full funding mode
		if (FUNDING_TYPE == ITypes.FundingType.Full) {
			numTokensReservedForVesting += totalExpectedAmount;

			IERC20(TOKEN_ADDRESS).safeTransferFrom(
				msg.sender,
				address(this),
				totalExpectedAmount
			);
		}
	}

	/**
	 * @notice Fund an existing vesting schedule
	 * @param _vestingId - Identifier of the vesting to fund
	 * @param _fundingAmount - Amount of tokens to add as funding
	 */
	function fundVesting(
		bytes32 _vestingId,
		uint256 _fundingAmount
	) external isVestingActive(_vestingId) onlyAdmin {
		if (FUNDING_TYPE == ITypes.FundingType.Full)
			revert Errors.VestingFullyFunded();
		if (_fundingAmount == 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];

		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 VestingFunded(
			_vestingId,
			msg.sender,
			_fundingAmount,
			vestingFunding[_vestingId],
			totalRequired
		);

		// Transfer tokens from sender to contract
		IERC20(TOKEN_ADDRESS).safeTransferFrom(
			msg.sender,
			address(this),
			_fundingAmount
		);
	}

	/**
	 * @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 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();

		uint256 totalFundingAmount;

		// 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;

			// Track total for the transfer
			totalFundingAmount += fundingAmount;

			// Emit event for this funding
			emit VestingFunded(
				vestingId,
				msg.sender,
				fundingAmount,
				vestingFunding[vestingId],
				totalRequired
			);
		}

		// If no actual funding happened, revert to avoid empty transactions
		if (totalFundingAmount == 0) {
			revert Errors.InvalidFundingAmount();
		}

		// Transfer total tokens from sender to contract after all state updates
		IERC20(TOKEN_ADDRESS).safeTransferFrom(
			msg.sender,
			address(this),
			totalFundingAmount
		);
	}

	/**
	 * @notice Claim vested tokens
	 * @param _vestingId - Identifier of the vesting
	 */
	function claim(
		bytes32 _vestingId
	) external payable onlyVestingRecipient(_vestingId) {
		if (FEE_TYPE == ITypes.FeeType.Gas && msg.value != FEE)
			revert Errors.InsufficientFee();
		_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 (FEE_TYPE == ITypes.FeeType.Gas && msg.value != FEE)
			revert Errors.InsufficientFee();

		_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();

		// Validate the total fee for all claims upfront
		if (FEE_TYPE == ITypes.FeeType.Gas && msg.value != FEE * length)
			revert Errors.InsufficientFee();

		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 (amountRemaining < _amountRequested)
			revert Errors.InsufficientBalance();

		emit AdminWithdrawn(msg.sender, _amountRequested);
		IERC20(TOKEN_ADDRESS).safeTransfer(msg.sender, _amountRequested);
	}

	/**
	 * @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();
		if (_otherTokenAddress == TOKEN_ADDRESS) revert Errors.InvalidToken();

		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; // Store index+1 to distinguish from 0 (not in array)
		}

		// 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 token 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 withdrawTokenFee(
		address recipient,
		uint256 amount
	) external onlyFeeCollector {
		if (recipient == address(0)) revert Errors.InvalidAddress();
		if (numTokensReservedForFee == 0) revert Errors.InsufficientBalance();

		uint256 withdrawAmount = amount;
		// If amount is 0, withdraw all fees
		if (withdrawAmount == 0 || withdrawAmount > numTokensReservedForFee) {
			withdrawAmount = numTokensReservedForFee;
		}

		// Update the reserved fee amount
		numTokensReservedForFee -= withdrawAmount;

		emit TokenFeeWithdrawn(recipient, TOKEN_ADDRESS, withdrawAmount);

		IERC20(TOKEN_ADDRESS).safeTransfer(recipient, withdrawAmount);
	}

	/**
	 * @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();

		uint256 balance = address(this).balance;
		if (balance == 0) revert Errors.InsufficientBalance();

		uint256 withdrawAmount = amount;
		// If amount is 0, withdraw all fees
		if (withdrawAmount == 0) withdrawAmount = balance;
		if (withdrawAmount > balance) revert Errors.FeeTooLow();

		emit GasFeeWithdrawn(recipient, withdrawAmount);

		// Using low-level call with re-entrancy protection
		(bool success, ) = recipient.call{ value: withdrawAmount }("");
		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);
	}

	// EXTERNAL VIEW FUNCTIONS

	/**
	 * @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 _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];
	}

	// PUBLIC VIEW/PURE FUNCTIONS

	/**
	 * @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
			IERC20(TOKEN_ADDRESS).balanceOf(address(this)) -
			numTokensReservedForVesting -
			numTokensReservedForFee;
	}

	/**
	 * @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);
	}

	// INTERNAL WRITE FUNCTIONS

	/**
	 * @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) {
				claimable = currentFunding > vesting.claimedAmount
					? currentFunding - vesting.claimedAmount
					: 0;

				if (claimable == 0) revert Errors.InsufficientFunding();
			}
		}

		vesting.claimedAmount += claimable;
		numTokensReservedForVesting -= claimable;

		uint256 transferAmount = claimable;

		if (FEE_TYPE == ITypes.FeeType.DistributionToken && FEE > 0) {
			uint256 feeAmount = (claimable * FEE) / BASIS_POINTS;
			if (feeAmount > claimable) revert Errors.FeeTooHigh();
			transferAmount = claimable - feeAmount;
			numTokensReservedForFee += feeAmount;
		}

		emit Claimed(_vestingId, vesting.recipient, transferAmount);

		IERC20(TOKEN_ADDRESS).safeTransfer(vesting.recipient, transferAmount);
	}

	/**
	 * @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;

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

	// INTERNAL VIEW/PURE FUNCTIONS

	/// @dev Internal function to check if an address is a recipient
	/// @param recipient The address to check
	/// @return True if the address is a recipient, false otherwise
	function _isRecipient(address recipient) internal view returns (bool) {
		if (recipient == address(0)) revert Errors.InvalidAddress();
		return recipientToIndex[recipient] != 0;
	}

	/**
	 * @dev Internal function to check if all array lengths in batch params match the expected length
	 * @param params - Batch parameters to validate
	 * @param expectedLength - The expected length all arrays should match
	 */
	function _checkArrayLengthMismatch(
		CreateVestingBatchParams calldata params,
		uint256 expectedLength
	) internal pure {
		if (
			params._startTimestamps.length != expectedLength ||
			params._endTimestamps.length != expectedLength ||
			params._timelocks.length != expectedLength ||
			params._initialUnlocks.length != expectedLength ||
			params._cliffAmounts.length != expectedLength ||
			params._cliffReleaseTimestamps.length != expectedLength ||
			params._releaseIntervalSecs.length != expectedLength ||
			params._linearVestAmounts.length != expectedLength ||
			params._isRevocables.length != expectedLength
		) {
			revert Errors.ArrayLengthMismatch();
		}
	}
}

// SPDX-License-Identifier: 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 5 of 12 : 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 6 of 12 : ITokenVestingManager.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;

import "contracts/libraries/TokenVestingLib.sol";
import "contracts/interfaces/ITypes.sol";

/// @title Interface for the TokenVestingManager contract
/// @notice Provides the external functions of the TokenVestingManager contract for managing token vesting schedules
interface ITokenVestingManager {
	/**
	 * @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 token fees are withdrawn by the fee collector
	/// @param recipient Address receiving the withdrawn fees
	/// @param token Address of the token being withdrawn
	/// @param amount Amount of tokens withdrawn
	event TokenFeeWithdrawn(
		address indexed recipient,
		address indexed token,
		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 basis points denominator for percentage calculations
	function BASIS_POINTS() external view returns (uint256);

	/// @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 The address of the token being vested
	function TOKEN_ADDRESS() external view returns (address);

	/// @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;

	/// @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;

	/// @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 additional funding to an existing vesting schedule
	/// @param _vestingId The identifier of the vesting to fund
	/// @param _fundingAmount Amount of tokens to add as funding
	function fundVesting(bytes32 _vestingId, uint256 _fundingAmount) external;

	/// @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;

	/// @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 fees
	/// @return Amount of tokens reserved for fees
	function numTokensReservedForFee() external view returns (uint256);

	/// @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 ERC20 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 ERC20 tokens accidentally sent to the contract's address
	/// @param _otherTokenAddress Address of the token to be withdrawn
	function withdrawOtherToken(address _otherTokenAddress) external;

	/// @notice Withdraws token fees collected by the contract
	/// @param recipient Address to receive the fees
	/// @param amount Amount of tokens to withdraw
	function withdrawTokenFee(address recipient, uint256 amount) external;
}

File 7 of 12 : 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: 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;
    }
}

// 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);
    }
}

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":"tokenAddress_","type":"address"},{"internalType":"enum ITypes.FeeType","name":"feeType_","type":"uint8"},{"internalType":"uint256","name":"fee_","type":"uint256"},{"internalType":"address","name":"feeCollector_","type":"address"},{"internalType":"enum ITypes.FundingType","name":"fundingType_","type":"uint8"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AdminAccessRequired","type":"error"},{"inputs":[],"name":"AdminStatusAlreadyActive","type":"error"},{"inputs":[],"name":"ArrayLengthMismatch","type":"error"},{"inputs":[],"name":"CannotRemoveLastAdmin","type":"error"},{"inputs":[],"name":"EmptyArray","type":"error"},{"inputs":[],"name":"EmptyVesting","type":"error"},{"inputs":[],"name":"FeeTooHigh","type":"error"},{"inputs":[],"name":"FeeTooLow","type":"error"},{"inputs":[],"name":"FullyVested","type":"error"},{"inputs":[],"name":"FundingLimitExceeded","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InsufficientFee","type":"error"},{"inputs":[],"name":"InsufficientFunding","type":"error"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"InvalidCliffAmount","type":"error"},{"inputs":[],"name":"InvalidCliffRelease","type":"error"},{"inputs":[],"name":"InvalidEndTimestamp","type":"error"},{"inputs":[],"name":"InvalidFundingAmount","type":"error"},{"inputs":[],"name":"InvalidIntervalLength","type":"error"},{"inputs":[],"name":"InvalidRange","type":"error"},{"inputs":[],"name":"InvalidReleaseInterval","type":"error"},{"inputs":[],"name":"InvalidStartTimestamp","type":"error"},{"inputs":[],"name":"InvalidToken","type":"error"},{"inputs":[],"name":"InvalidVestedAmount","type":"error"},{"inputs":[],"name":"NoPendingTransfer","type":"error"},{"inputs":[],"name":"NotAuthorizedForTransfer","type":"error"},{"inputs":[],"name":"NotFeeCollector","type":"error"},{"inputs":[],"name":"NotVestingOwner","type":"error"},{"inputs":[],"name":"PendingTransferExists","type":"error"},{"inputs":[],"name":"TimelockEnabled","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"inputs":[],"name":"VestingFullyFunded","type":"error"},{"inputs":[],"name":"VestingNotActive","type":"error"},{"inputs":[],"name":"VestingNotRevocable","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_admin","type":"address"},{"indexed":false,"internalType":"bool","name":"_enabled","type":"bool"}],"name":"AdminAccessSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountRequested","type":"uint256"}],"name":"AdminWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"vestingId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"withdrawalAmount","type":"uint256"}],"name":"Claimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldFeeCollector","type":"address"},{"indexed":true,"internalType":"address","name":"newFeeCollector","type":"address"}],"name":"FeeCollectorUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"GasFeeWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokenFeeWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"vestingId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint32","name":"startTimestamp","type":"uint32"},{"internalType":"uint32","name":"endTimestamp","type":"uint32"},{"internalType":"uint32","name":"deactivationTimestamp","type":"uint32"},{"internalType":"uint32","name":"timelock","type":"uint32"},{"internalType":"uint32","name":"releaseIntervalSecs","type":"uint32"},{"internalType":"uint32","name":"cliffReleaseTimestamp","type":"uint32"},{"internalType":"uint256","name":"initialUnlock","type":"uint256"},{"internalType":"uint256","name":"cliffAmount","type":"uint256"},{"internalType":"uint256","name":"linearVestAmount","type":"uint256"},{"internalType":"uint256","name":"claimedAmount","type":"uint256"},{"internalType":"bool","name":"isRevocable","type":"bool"}],"indexed":false,"internalType":"struct TokenVestingLib.Vesting","name":"vesting","type":"tuple"}],"name":"VestingCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"vestingId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"funder","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalFunded","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalRequired","type":"uint256"}],"name":"VestingFunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"vestingId","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"numTokensWithheld","type":"uint256"},{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint32","name":"startTimestamp","type":"uint32"},{"internalType":"uint32","name":"endTimestamp","type":"uint32"},{"internalType":"uint32","name":"deactivationTimestamp","type":"uint32"},{"internalType":"uint32","name":"timelock","type":"uint32"},{"internalType":"uint32","name":"releaseIntervalSecs","type":"uint32"},{"internalType":"uint32","name":"cliffReleaseTimestamp","type":"uint32"},{"internalType":"uint256","name":"initialUnlock","type":"uint256"},{"internalType":"uint256","name":"cliffAmount","type":"uint256"},{"internalType":"uint256","name":"linearVestAmount","type":"uint256"},{"internalType":"uint256","name":"claimedAmount","type":"uint256"},{"internalType":"bool","name":"isRevocable","type":"bool"}],"indexed":false,"internalType":"struct TokenVestingLib.Vesting","name":"vesting","type":"tuple"}],"name":"VestingRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"currentOwner","type":"address"},{"indexed":true,"internalType":"bytes32","name":"vestingId","type":"bytes32"}],"name":"VestingTransferCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"currentOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"},{"indexed":true,"internalType":"bytes32","name":"vestingId","type":"bytes32"}],"name":"VestingTransferInitiated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"},{"indexed":true,"internalType":"bytes32","name":"vestingId","type":"bytes32"}],"name":"VestingTransferred","type":"event"},{"inputs":[],"name":"BASIS_POINTS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEPLOYMENT_BLOCK_NUMBER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEE_TYPE","outputs":[{"internalType":"enum ITypes.FeeType","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FUNDING_TYPE","outputs":[{"internalType":"enum ITypes.FundingType","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TOKEN_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_vestingId","type":"bytes32"}],"name":"acceptVestingTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_vestingId","type":"bytes32"}],"name":"adminClaim","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"adminCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"amountAvailableToWithdrawByAdmin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"_vestingIds","type":"bytes32[]"}],"name":"batchAdminClaim","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"_vestingIds","type":"bytes32[]"}],"name":"batchRevokeVestings","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_vestingId","type":"bytes32"}],"name":"cancelVestingTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_vestingId","type":"bytes32"}],"name":"claim","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint32","name":"_startTimestamp","type":"uint32"},{"internalType":"uint32","name":"_endTimestamp","type":"uint32"},{"internalType":"uint32","name":"_timelock","type":"uint32"},{"internalType":"uint256","name":"_initialUnlock","type":"uint256"},{"internalType":"uint32","name":"_cliffReleaseTimestamp","type":"uint32"},{"internalType":"uint256","name":"_cliffAmount","type":"uint256"},{"internalType":"uint32","name":"_releaseIntervalSecs","type":"uint32"},{"internalType":"uint256","name":"_linearVestAmount","type":"uint256"},{"internalType":"bool","name":"_isRevocable","type":"bool"}],"name":"createVesting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"_recipients","type":"address[]"},{"internalType":"uint32[]","name":"_startTimestamps","type":"uint32[]"},{"internalType":"uint32[]","name":"_endTimestamps","type":"uint32[]"},{"internalType":"uint32[]","name":"_timelocks","type":"uint32[]"},{"internalType":"uint256[]","name":"_initialUnlocks","type":"uint256[]"},{"internalType":"uint32[]","name":"_cliffReleaseTimestamps","type":"uint32[]"},{"internalType":"uint256[]","name":"_cliffAmounts","type":"uint256[]"},{"internalType":"uint32[]","name":"_releaseIntervalSecs","type":"uint32[]"},{"internalType":"uint256[]","name":"_linearVestAmounts","type":"uint256[]"},{"internalType":"bool[]","name":"_isRevocables","type":"bool[]"}],"internalType":"struct ITokenVestingManager.CreateVestingBatchParams","name":"params","type":"tuple"}],"name":"createVestingBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_vestingId","type":"bytes32"},{"internalType":"address","name":"_newOwner","type":"address"}],"name":"directVestingTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeCollector","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_vestingId","type":"bytes32"},{"internalType":"uint256","name":"_fundingAmount","type":"uint256"}],"name":"fundVesting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"_vestingIds","type":"bytes32[]"},{"internalType":"uint256[]","name":"_fundingAmounts","type":"uint256[]"}],"name":"fundVestingBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"}],"name":"getAllRecipientVestings","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"}],"name":"getAllRecipientVestingsLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_from","type":"uint256"},{"internalType":"uint256","name":"_to","type":"uint256"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"getAllRecipientVestingsSliced","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllRecipients","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllRecipientsLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_from","type":"uint256"},{"internalType":"uint256","name":"_to","type":"uint256"}],"name":"getAllRecipientsSliced","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_vestingId","type":"bytes32"}],"name":"getClaimableAmount","outputs":[{"internalType":"uint256","name":"claimable","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_vestingId","type":"bytes32"}],"name":"getPendingVestingTransfer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_vestingId","type":"bytes32"},{"internalType":"uint32","name":"_referenceTimestamp","type":"uint32"}],"name":"getVestedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_vestingId","type":"bytes32"}],"name":"getVestingFundingInfo","outputs":[{"internalType":"uint8","name":"fundingType","type":"uint8"},{"internalType":"uint256","name":"totalFunded","type":"uint256"},{"internalType":"uint256","name":"totalRequired","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_vestingId","type":"bytes32"}],"name":"getVestingInfo","outputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint32","name":"startTimestamp","type":"uint32"},{"internalType":"uint32","name":"endTimestamp","type":"uint32"},{"internalType":"uint32","name":"deactivationTimestamp","type":"uint32"},{"internalType":"uint32","name":"timelock","type":"uint32"},{"internalType":"uint32","name":"releaseIntervalSecs","type":"uint32"},{"internalType":"uint32","name":"cliffReleaseTimestamp","type":"uint32"},{"internalType":"uint256","name":"initialUnlock","type":"uint256"},{"internalType":"uint256","name":"cliffAmount","type":"uint256"},{"internalType":"uint256","name":"linearVestAmount","type":"uint256"},{"internalType":"uint256","name":"claimedAmount","type":"uint256"},{"internalType":"bool","name":"isRevocable","type":"bool"}],"internalType":"struct TokenVestingLib.Vesting","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_vestingId","type":"bytes32"},{"internalType":"address","name":"_newOwner","type":"address"}],"name":"initiateVestingTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_addressToCheck","type":"address"}],"name":"isAdmin","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"}],"name":"isRecipient","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_vestingId","type":"bytes32"}],"name":"isVestingFullyFunded","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numTokensReservedForFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numTokensReservedForVesting","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"pendingVestingTransfers","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"recipientVestings","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"recipients","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_vestingId","type":"bytes32"}],"name":"revokeVesting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"admin","type":"address"},{"internalType":"bool","name":"isEnabled","type":"bool"}],"name":"setAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newFeeCollector","type":"address"}],"name":"transferFeeCollectorRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"vestingById","outputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint32","name":"startTimestamp","type":"uint32"},{"internalType":"uint32","name":"endTimestamp","type":"uint32"},{"internalType":"uint32","name":"deactivationTimestamp","type":"uint32"},{"internalType":"uint32","name":"timelock","type":"uint32"},{"internalType":"uint32","name":"releaseIntervalSecs","type":"uint32"},{"internalType":"uint32","name":"cliffReleaseTimestamp","type":"uint32"},{"internalType":"uint256","name":"initialUnlock","type":"uint256"},{"internalType":"uint256","name":"cliffAmount","type":"uint256"},{"internalType":"uint256","name":"linearVestAmount","type":"uint256"},{"internalType":"uint256","name":"claimedAmount","type":"uint256"},{"internalType":"bool","name":"isRevocable","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"vestingFunding","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountRequested","type":"uint256"}],"name":"withdrawAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawGasFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_otherTokenAddress","type":"address"}],"name":"withdrawOtherToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawTokenFee","outputs":[],"stateMutability":"nonpayable","type":"function"}]

0x6101203461023a57601f613e7c38819003918201601f19168301916001600160401b0383118484101761023e5780849260a09460405283398101031261023a5761004881610252565b906020810151600281101561023a57604082015190608061006b60608501610252565b93015193600285101561023a57335f525f60205260405f20600160ff1982541617905560018055604051600181527fe529461c8529abc0e0fe7c5ee361f74fe22e0b7574df1fc0b7558a282091fb7860203392a26001600160a01b038116158015610229575b61021a57600182148061020f575b6102005760805260a05261010052600580546001600160a01b0319166001600160a01b039290921691909117905560c0524360e052604051613c15908161026782396080518181816102fc015281816107cc01528181610bcd01528181610d8e01528181611846015281816125fc0152818161269601528181612cb601526136c2015260a0518181816105de01528181610e3001528181612a7a0152613649015260c05181818161029c0152818161077e01528181610fcf01528181611335015281816114bd0152818161172c015281816118a3015281816120f201528181612afe01528181612bbb01526135fb015260e05181611bcb0152610100518181816106680152818161125e01528181612ab9015281816136f101526137760152f35b63cd4e616760e01b5f5260045ffd5b5061271083116100df565b63e6c4247b60e01b5f5260045ffd5b506001600160a01b038416156100d1565b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b038216820361023a5756fe60806040526004361015610011575f80fd5b5f3560e01c8063092c9340146126205780630bdf5300146125dd578063137c68fa146125c057806324d7806c146125845780632b7832b3146125675780632c0e5d1c1461254a5780632eb7d4341461249157806343ff9d8d146124745780634a7be739146123f35780634b0bddd21461228457806354f1c9041461211a5780635650d7d714611fda5780635edc1c8814611f19578063661b743d14611de357806367a421b614611db95780636d63b7ff14611d04578063775907f714611bee5780638635512f14611bb457806389a9c7b414610e605780638a85cbba14611b895780638c5143ea14611b665780639335af1a14611a795780639fb40e7b14611919578063a3ba6c7a1461186b578063afe6975e146116eb578063b6b00a50146116b8578063b74513c11461162f578063b8900aa714611407578063b9ed7153146113df578063ba48042b14611361578063ba50bba514611321578063bb64fd4414611303578063bd66528a146112a7578063c415b95c14611281578063c57981b514611247578063caaa9a2214610f78578063cdc6aa4c14610e93578063cedc9d1d14610e60578063cf61a09714610e1c578063d1bc76a114610ddb578063d77836ce14610d1d578063d955d6eb14610cc8578063e1f1c4a714610cac578063e50373f914610b87578063e5b3248714610b21578063e865fbc714610aff578063e940e53814610694578063ecb62f61146105955763f5ee64c014610234575f80fd5b346105915760406003193601126105915760043567ffffffffffffffff8111610591576102659036906004016127d1565b60243567ffffffffffffffff8111610591576102859036906004016127d1565b91335f525f60205260ff60405f20541615610569577f00000000000000000000000000000000000000000000000000000000000000006102c4816127b3565b1561049657801561054157828103610519579291905f935f935b81851061034b57858015610323576103219030336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016613536565b005b7fbb21bfd4000000000000000000000000000000000000000000000000000000005f5260045ffd5b909192939461035b868484612c39565b3595610368818787612c39565b35801561050e57875f52600b60205260405f2080546001600160a01b038116156104e65760e01c6104be578060046103ac60026103b59401546003840154906128fe565b910154906128fe565b97805f52600860205260405f205489811015610496576103d5908a612843565b821161046e577f7d880d31c520e2aafc7d84cd9b7b8519c22fa45a2682a2b09035af562b3820a461043083600196845f52600860205260405f2061041a8382546128fe565b9055610428826002546128fe565b6002556128fe565b99825f52600860205260405f20546104606040519283923397846040919493926060820195825260208201520152565b0390a35b01939291906102de565b7fb6b88bbc000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fbbc4bdcf000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f48a0eba2000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f3a605dd2000000000000000000000000000000000000000000000000000000005f5260045ffd5b506001919650610464565b7fa24a13a6000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f521299a9000000000000000000000000000000000000000000000000000000005f5260045ffd5b7ffdd63ca2000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f80fd5b60206003193601126105915760043567ffffffffffffffff8111610591576105c19036906004016127d1565b90335f525f60205260ff60405f20541615610569578115610541577f0000000000000000000000000000000000000000000000000000000000000000610606816127b3565b1580610661575b610639575f5b82811061061c57005b8061063361062d6001938686612c39565b3561359a565b01610613565b7f025dbdd4000000000000000000000000000000000000000000000000000000005f5260045ffd5b5061068c827f0000000000000000000000000000000000000000000000000000000000000000612d9c565b34141561060d565b346105915760206003193601126105915760043567ffffffffffffffff8111610591578060040190610140600319823603011261059157335f525f60205260ff60405f20541615610569576106e98280612d37565b5f9291506106f681612c49565b936024830191806107078484612d37565b905014801590610ae7575b8015610acf575b8015610ab7575b8015610a9f575b8015610a87575b8015610a6f575b8015610a56575b8015610a3d575b61051957929085905f936044840160648501936084860160a4870160c488019460e48901936101246101048b019a01905b8c8c106107f1578d7f00000000000000000000000000000000000000000000000000000000000000006107a6816127b3565b156107ad57005b806107bd610321926002546128fe565b60025530336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016613536565b909192939495969798999a9b9c8c6108186004549161080f83612850565b60045584612a25565b528c6108396108278285612a25565b51916108338d80612d37565b90612c39565b356001600160a01b03811680910361059157848f8f908f8f8f918f908f94918f928f948f8280808080808061086f81988a612d37565b6108799291612c39565b61088290612d8b565b9961088d9089612d37565b6108979291612c39565b6108a090612d8b565b996108ab9088612d37565b6108b59291612c39565b6108be90612d8b565b996108c99087612d37565b6108d39291612c39565b35996108df9086612d37565b6108e99291612c39565b6108f290612d8b565b996108fd9085612d37565b6109079291612c39565b35996109139084612d37565b61091d9291612c39565b61092690612d8b565b996109319083612d37565b61093b9291612c39565b359961094691612d37565b6109509291612c39565b3598891515809a03610591576040519a6109698c61290b565b8b5260208b015263ffffffff1660408a015263ffffffff16606089015263ffffffff16608088015260a087015263ffffffff1660c086015260e085015263ffffffff166101008401526101208301526101408201526109c790612f6e565b8b8d8a818d816109d78c83612d37565b6109e19291612c39565b35926109ec91612d37565b6109f69291612c39565b35610a00916128fe565b91610a0b908d612d37565b610a159291612c39565b35610a1f916128fe565b610a28916128fe565b9c9b6001019a99989796959493929190610774565b5080610a4d610124860184612d37565b90501415610743565b5080610a66610104860184612d37565b9050141561073c565b5080610a7e60e4860184612d37565b90501415610735565b5080610a9660a4860184612d37565b9050141561072e565b5080610aae60c4860184612d37565b90501415610727565b5080610ac66084860184612d37565b90501415610720565b5080610ade6064860184612d37565b90501415610719565b5080610af66044860184612d37565b90501415610712565b34610591575f600319360112610591576020610b19612c7a565b604051908152f35b34610591576020600319360112610591576001600160a01b03610b426126f9565b168015610b5f575f526009602052602060405f2054604051908152f35b7fe6c4247b000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461059157602060031936011261059157610ba06126f9565b335f525f60205260ff60405f20541615610569576001600160a01b03168015610b5f576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168114610c8457604051907f70a08231000000000000000000000000000000000000000000000000000000008252306004830152602082602481845afa908115610c79575f91610c43575b61032192503390612daf565b90506020823d602011610c71575b81610c5e60209383612945565b8101031261059157610321915190610c37565b3d9150610c51565b6040513d5f823e3d90fd5b7fc1ab6dc1000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610591575f6003193601126105915760206040516127108152f35b3461059157604060031936011261059157610ce16126f9565b6001600160a01b0360243591165f52600960205260405f20805482101561059157602091610d0e9161282e565b90549060031b1c604051908152f35b3461059157602060031936011261059157600435335f525f60205260ff60405f205416156105695780610d4e612c7a565b10610db357610321906040518181527fca1cf43de312865665f595e88f569f9d5246690c07df26e86aba01147e6d131460203392a2336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016612daf565b7ff4d678b8000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461059157602060031936011261059157600435600654811015610591576001600160a01b03610e0c602092612802565b90549060031b1c16604051908152f35b34610591575f6003193601126105915760207f000000000000000000000000000000000000000000000000000000000000000060405190610e5c816127b3565b8152f35b34610591576020600319360112610591576004355f52600c60205260206001600160a01b0360405f205416604051908152f35b3461059157606060031936011261059157600435602435604435916001600160a01b038316809303610591578215610b5f57818110801590610f63575b610f3b57610ee6610ee18284612843565b612c49565b92815b838110610f025760405180610efe878261277a565b0390f35b600190825f526009602052610f1a8160405f2061282e565b90549060031b1c610f34610f2e8684612843565b88612a25565b5201610ee9565b7f561ce9bb000000000000000000000000000000000000000000000000000000005f5260045ffd5b50825f52600960205260405f20548211610ed0565b346105915760206003193601126105915760043567ffffffffffffffff811161059157610fa99036906004016127d1565b90335f525f60205260ff60405f20541615610569578115610541575f63ffffffff4216927f00000000000000000000000000000000000000000000000000000000000000009060018214925b818110610ffe57005b611009818387612c39565b3590815f52600b60205260405f205460e01c6104be57815f52600b60205260405f209163ffffffff835460c01c168042101561121f576006840160ff815416156111f7578961109b6101a092876110a07ffab6a0d43d3a59dd207bc8acbb42f638ace4f051bc53d95dd662fbfa5bc0213a9661108f8661108a60019d612968565b613836565b94859161108a85612968565b612843565b928c6110ab8d6127b3565b6111b4575b508061110960ff9495600593907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffff0000000000000000000000000000000000000000000000000000000083549260e01b169116179055565b61111585600254612843565b60025560405194855280546001600160a01b038116602087015263ffffffff8160a01c16604087015263ffffffff8160c01c16606087015260e01c608086015263ffffffff8a82015481811660a0880152818160201c1660c088015260401c1660e0860152600281015461010086015260038101546101208601526004810154610140860152015461016084015254161515610180820152a201610ff5565b5f8881526008602052604090205460ff945060059291908181116111e45750506111095f955b95945050906110b0565b611109916111f191612843565b956111da565b7fce21cad1000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f2e8e455d000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610591575f6003193601126105915760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b34610591575f6003193601126105915760206001600160a01b0360055416604051908152f35b602060031936011261059157600435805f52600b6020526001600160a01b0360405f20541633036112db5761032190612a78565b7f75228a9c000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610591576020600319360112610591576020610b19600435612b5d565b34610591575f6003193601126105915760207f000000000000000000000000000000000000000000000000000000000000000060405190610e5c816127b3565b34610591576020600319360112610591576001600160a01b036113826126f9565b165f52600960205260405f206040519081602082549182815201915f5260205f20905f5b8181106113c957610efe856113bd81870382612945565b6040519182918261277a565b82548452602090930192600192830192016113a6565b346105915760206003193601126105915760206113fd600435612ae0565b6040519015158152f35b3461059157602060031936011261059157600435335f525f60205260ff60405f2054161561056957805f52600b60205260405f205460e01c6104be57805f52600b60205260405f2063ffffffff815460c01c168042101561121f57600682019060ff825416156111f75761109b6101a092846114b86114a67ffab6a0d43d3a59dd207bc8acbb42f638ace4f051bc53d95dd662fbfa5bc0213a97612968565b9461108f63ffffffff42168097613836565b9260017f00000000000000000000000000000000000000000000000000000000000000006114e5816127b3565b146115ec575b508061154460ff9495600593907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffff0000000000000000000000000000000000000000000000000000000083549260e01b169116179055565b61155085600254612843565b60025560405194855280546001600160a01b038116602087015263ffffffff8160a01c16604087015263ffffffff8160c01c16606087015260e01c608086015263ffffffff600182015481811660a0880152818160201c1660c088015260401c1660e0860152600281015461010086015260038101546101208601526004810154610140860152015461016084015254161515610180820152a2005b5f8881526008602052604090205460ff9450600592919081811161161c5750506115445f955b95945050906114eb565b6115449161162991612843565b95611612565b34610591575f6003193601126105915760405180602060065491828152019060065f527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f905f5b81811061169957610efe8561168d81870382612945565b60405191829182612738565b82546001600160a01b0316845260209093019260019283019201611676565b34610591576040600319360112610591576020610b196116d6612725565b6004355f52600b835261108a60405f20612968565b3461059157604060031936011261059157600435602435815f52600b60205260405f205460e01c6104be57335f525f60205260ff60405f20541615610569577f0000000000000000000000000000000000000000000000000000000000000000611754816127b3565b1561049657801561032357815f52600b60205260405f206001600160a01b03815416156104e6578060046103ac60026117949401546003840154906128fe565b825f52600860205260405f205481811015610496576117b39082612843565b821161046e5782610321935f52600860205260405f206117d48482546128fe565b90556117e2836002546128fe565b600255805f5260086020527f7d880d31c520e2aafc7d84cd9b7b8519c22fa45a2682a2b09035af562b3820a461183760405f205493604051918291339688846040919493926060820195825260208201520152565b0390a330336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016613536565b3461059157602060031936011261059157600435805f52600b60205260405f206001600160a01b03815416156104e6576060916118e17f0000000000000000000000000000000000000000000000000000000000000000926118cc846127b3565b60046103ac60028301546003840154906128fe565b906118eb836127b3565b826119085750805b60ff6040519316835260208301526040820152f35b5f52600860205260405f20546118f3565b3461059157602060031936011261059157600435805f52600b60205260405f205460e01c6104be57805f52600c6020526001600160a01b0360405f205416338103611a5157815f52600b60205260405f206001600160a01b03815416906119808483612e04565b6001600160a01b03831673ffffffffffffffffffffffffffffffffffffffff198254161790556119af82612f08565b15611a33575b815f5260096020526119ca8360405f206128bd565b5f82815260096020908152604080832054600a8352818420878552835281842055600c9091528120805473ffffffffffffffffffffffffffffffffffffffff191690557fd26d820c791a6bb2143a3066ee2a8314ff045a3abc3f256346814ad5be7d63469080a4005b611a3c8261285e565b600654825f52600760205260405f20556119b5565b7fc414c24a000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461059157604060031936011261059157600435611a9561270f565b815f52600b6020526001600160a01b0360405f20541633036112db57815f52600b60205260405f205460e01c6104be576001600160a01b03168015610b5f57815f52600c6020526001600160a01b0360405f205416611b3e57815f52600c60205260405f208173ffffffffffffffffffffffffffffffffffffffff19825416179055337f666421a92fd891d9481da847947b32bfd485b5a6ac70ef477f18b9aae576c3715f80a4005b7f8f524afa000000000000000000000000000000000000000000000000000000005f5260045ffd5b346105915760206003193601126105915760206113fd611b846126f9565b612f08565b602060031936011261059157335f525f60205260ff60405f2054161561056957610321600435612a78565b34610591575f6003193601126105915760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461059157604060031936011261059157611c076126f9565b6024356001600160a01b03600554163303611cdc576001600160a01b038216918215610b5f57478015610db3578215611cd4575b8211611cac575f8080938193867fee3a3e8b975ee1a894fd6ed0a36ec6d1db3dc70e575382d21e9aed5a2c72f5146020604051858152a25af1611c7c612a39565b5015611c8457005b7f90b8ec18000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f732f9413000000000000000000000000000000000000000000000000000000005f5260045ffd5b915081611c3b565b7f73fcd3fe000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461059157604060031936011261059157600435602435808210801590611dae575b610f3b57611d348282612843565b91601f19611d5a611d4485612a0d565b94611d526040519687612945565b808652612a0d565b01366020850137805b828110611d785760405180610efe8682612738565b806001600160a01b03611d8c600193612802565b90549060031b1c16611da7611da18584612843565b87612a25565b5201611d63565b506006548111611d26565b34610591576020600319360112610591576004355f526008602052602060405f2054604051908152f35b34610591576020600319360112610591575f610160604051611e0481612928565b8281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e082015282610100820152826101208201528261014082015201526004355f52600b602052610efe611e6160405f20612968565b60405191829182919091610160806101808301946001600160a01b03815116845263ffffffff602082015116602085015263ffffffff604082015116604085015263ffffffff606082015116606085015263ffffffff608082015116608085015263ffffffff60a08201511660a085015263ffffffff60c08201511660c085015260e081015160e085015261010081015161010085015261012081015161012085015261014081015161014085015201511515910152565b3461059157602060031936011261059157600435805f52600b6020526001600160a01b0360405f20541633036112db57805f52600c6020526001600160a01b0360405f20541615611fb257805f52600c60205260405f2073ffffffffffffffffffffffffffffffffffffffff198154169055337f155ceda63be6f9c02f83b108c17e45601c8e72e9845fd5a15c4c33b53e8cac1b5f80a3005b7fd5e80fb4000000000000000000000000000000000000000000000000000000005f5260045ffd5b346105915761014060031936011261059157611ff46126f9565b611ffc612725565b6044359063ffffffff82168092036105915760643563ffffffff81168091036105915760843560a43563ffffffff81168091036105915760c43560e4359163ffffffff8316809303610591576101043593610124359586151580970361059157335f525f60205260ff60405f20541615610569576120f09863ffffffff6120b2996001600160a01b036120988a6120938a896128fe565b6128fe565b9d6004546120a581612850565b6004556040519d8e61290b565b8d521660208c01521660408a01526060890152608088015260a087015260c086015260e0850152610100840152610120830152610140820152612f6e565b7f00000000000000000000000000000000000000000000000000000000000000006107a6816127b3565b346105915760406003193601126105915760043561213661270f565b815f52600b6020526001600160a01b0360405f20541633036112db57815f52600b60205260405f205460e01c6104be576001600160a01b038116908115610b5f57825f52600b60205260405f20906001600160a01b038254169161219a8584612e04565b8373ffffffffffffffffffffffffffffffffffffffff198254161790556121c081612f08565b15612265575b50815f5260096020526121dc8360405f206128bd565b5f82815260096020908152604080832054600a8352818420878552835281842055600c9091529020546001600160a01b0316612239575b7fd26d820c791a6bb2143a3066ee2a8314ff045a3abc3f256346814ad5be7d63465f80a4005b825f52600c60205260405f2073ffffffffffffffffffffffffffffffffffffffff198154169055612213565b61226e9061285e565b600654825f52600760205260405f2055836121c6565b346105915760406003193601126105915761229d6126f9565b602435908115159081830361059157335f525f60205260ff60405f20541615610569576001600160a01b0316918215610b5f57825f525f6020528160ff60405f2054161515146123cb578015612349577fe529461c8529abc0e0fe7c5ee361f74fe22e0b7574df1fc0b7558a282091fb7891612340602092612320600154612850565b6001555b855f525f845260405f209060ff60ff1983541691151516179055565b604051908152a2005b6001549160018311156123a357821561238f576123406020927fe529461c8529abc0e0fe7c5ee361f74fe22e0b7574df1fc0b7558a282091fb78945f1901600155612324565b634e487b7160e01b5f52601160045260245ffd5b7fc13a62ad000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fd52dfc1c000000000000000000000000000000000000000000000000000000005f5260045ffd5b346105915760206003193601126105915761240c6126f9565b600554906001600160a01b03821690813303611cdc576001600160a01b0316918215610b5f5773ffffffffffffffffffffffffffffffffffffffff191682176005557f5d16ad41baeb009cd23eb8f6c7cde5c2e0cd5acf4a33926ab488875c37c37f385f80a3005b34610591575f600319360112610591576020600654604051908152f35b34610591576020600319360112610591576004355f52600b60205261018060405f208054906001810154906002810154600382015460048301549163ffffffff60ff600660058701549601541695604051976001600160a01b0381168952828160a01c1660208a0152828160c01c1660408a015260e01c60608901528181166080890152818160201c1660a089015260401c1660c087015260e08601526101008501526101208401526101408301521515610160820152f35b34610591575f600319360112610591576020600354604051908152f35b34610591575f600319360112610591576020600154604051908152f35b34610591576020600319360112610591576001600160a01b036125a56126f9565b165f525f602052602060ff60405f2054166040519015158152f35b34610591575f600319360112610591576020600254604051908152f35b34610591575f6003193601126105915760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b34610591576040600319360112610591576126396126f9565b6024356001600160a01b03600554163303611cdc576001600160a01b038216918215610b5f57600354928315610db357610321938315818582156126ef575b50506126e7575b8361268991612843565b6003556001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001680917f14f7c82e513e1c45523692386ca72ff853393b9b4228a9f6cc27fd6238d929d86020604051878152a3612daf565b92508261267f565b1190508187612678565b600435906001600160a01b038216820361059157565b602435906001600160a01b038216820361059157565b6024359063ffffffff8216820361059157565b60206040818301928281528451809452019201905f5b81811061275b5750505090565b82516001600160a01b031684526020938401939092019160010161274e565b60206040818301928281528451809452019201905f5b81811061279d5750505090565b8251845260209384019390920191600101612790565b600211156127bd57565b634e487b7160e01b5f52602160045260245ffd5b9181601f840112156105915782359167ffffffffffffffff8311610591576020808501948460051b01011161059157565b60065481101561281a5760065f5260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b805482101561281a575f5260205f2001905f90565b9190820391821161238f57565b5f19811461238f5760010190565b60065490680100000000000000008210156128a9576128888260016128a79401600655600661282e565b9091906001600160a01b038084549260031b9316831b921b1916179055565b565b634e487b7160e01b5f52604160045260245ffd5b805490680100000000000000008210156128a957816128e49160016128fa9401815561282e565b819391549060031b91821b915f19901b19161790565b9055565b9190820180921161238f57565b610160810190811067ffffffffffffffff8211176128a957604052565b610180810190811067ffffffffffffffff8211176128a957604052565b90601f601f19910116810190811067ffffffffffffffff8211176128a957604052565b9060405161297581612928565b61016060ff6006839580546001600160a01b038116865263ffffffff8160a01c16602087015263ffffffff8160c01c16604087015260e01c606086015263ffffffff60018201548181166080880152818160201c1660a088015260401c1660c0860152600281015460e08601526003810154610100860152600481015461012086015260058101546101408601520154161515910152565b67ffffffffffffffff81116128a95760051b60200190565b805182101561281a5760209160051b010190565b3d15612a73573d9067ffffffffffffffff82116128a95760405191612a68601f8201601f191660200184612945565b82523d5f602084013e565b606090565b7f0000000000000000000000000000000000000000000000000000000000000000612aa2816127b3565b1580612ab6575b610639576128a79061359a565b507f0000000000000000000000000000000000000000000000000000000000000000341415612aa9565b805f52600b60205260405f206001600160a01b03815416156104e6577f0000000000000000000000000000000000000000000000000000000000000000612b26816127b3565b15612b56578060046103ac6002612b449401546003840154906128fe565b905f52600860205260405f2054101590565b5050600190565b805f52600b60205260405f206001600160a01b03815416156104e65763ffffffff60018201541663ffffffff4216809111612c3257612ba160059161108a84612968565b91015480821115612c2c57612bb68183612843565b9260017f0000000000000000000000000000000000000000000000000000000000000000612be3816127b3565b14612bef575b50505090565b5f52600860205260405f2054918210612c09575b80612be9565b90915080821115612c2457612c1d91612843565b5f80612c03565b50505f612c1d565b5f612bb6565b5050505f90565b919081101561281a5760051b0190565b90612c5382612a0d565b612c606040519182612945565b828152601f19612c708294612a0d565b0190602036910137565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526020816024816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa908115610c79575f91612d03575b50612cf7612d009160025490612843565b60035490612843565b90565b90506020813d602011612d2f575b81612d1e60209383612945565b810103126105915751612cf7612ce6565b3d9150612d11565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610591570180359067ffffffffffffffff821161059157602001918160051b3603831361059157565b3563ffffffff811681036105915790565b8181029291811591840414171561238f57565b6128a7926001600160a01b03604051937fa9059cbb000000000000000000000000000000000000000000000000000000006020860152166024840152604483015260448252612dff606483612945565b61396e565b906001600160a01b03821690815f52600960205260405f2091805f52600a60205260405f20825f5260205260405f20548015612f01575f19810181811161238f5784545f1981019190821161238f57818103612ec0575b50505082548015612eac575f1901612e73818561282e565b8154905f199060031b1b1916905583555f52600a60205260405f20905f526020525f60408120555415612ea35750565b6128a790613a72565b634e487b7160e01b5f52603160045260245ffd5b612ee0612ed06128e4938861282e565b90549060031b1c9283928861282e565b9055825f52600a60205260405f20905f5260205260405f20555f8080612e5b565b5050505050565b6001600160a01b03168015610b5f575f52600760205260405f2054151590565b9063ffffffff8091169116039063ffffffff821161238f57565b9063ffffffff16908115612f5a5763ffffffff160690565b634e487b7160e01b5f52601260045260245ffd5b602081016001600160a01b0381511615610b5f57610120820191825192612f9b60a08301948551906128fe565b612fab60e08401918251906128fe565b1561350e57604083019163ffffffff835116156134e65763ffffffff83511690606085019163ffffffff835116106134b45763ffffffff84511663ffffffff83511614806134dc575b6134b457610100850163ffffffff8151161561348c5760c08601805163ffffffff16806133f4575084516133cc5763ffffffff61304561303b82875116838a511690612f28565b8285511690612f42565b166133a4575b63ffffffff806001600160a01b038a51169751169451169863ffffffff808060808b015116925193511696519351169351936101408901511515966040519b8c996130958b612928565b8a5260208a0197885260408a0190815260608a01905f825260808b0194855260a08b0193845260c08b0192835260e08b019586526101008b019687526101208b019788526101408b01985f8a526101608c019a8b528d6130fe6001600160a01b03825116612f08565b15613370575b508c515f908152600b602052604090209b519051915192517fffffffff0000000000000000000000000000000000000000000000000000000060e09190911b1660c09390931b7bffffffff000000000000000000000000000000000000000000000000166001600160a01b0390911660a09290921b77ffffffff000000000000000000000000000000000000000016919091171717895560018901925163ffffffff168354925160201b67ffffffff0000000016915160401b6bffffffff000000000000000016927fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001617171790555160028601555160038501555160048401555160058301555115159060060190613229919060ff60ff1983541691151516179055565b81516001600160a01b03165f52600960205260405f20815161324a916128bd565b81516001600160a01b03165f52600960205260405f205482516001600160a01b03165f52600a60205260405f2082515f5260205260405f20555190516001600160a01b03169160405161334a819282919091610160806101808301946001600160a01b03815116845263ffffffff602082015116602085015263ffffffff604082015116604085015263ffffffff606082015116606085015263ffffffff608082015116608085015263ffffffff60a08201511660a085015263ffffffff60c08201511660c085015260e081015160e085015261010081015161010085015261012081015161012085015261014081015161014085015201511515910152565b037f040a2d252643cef2c4dea3c242e5ed4bf32a76631de8342604dd6cba3074047291a3565b6001600160a01b0361338391511661285e565b8d6001600160a01b036006549151165f52600760205260405f20558d613104565b7f28df2855000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fea243c32000000000000000000000000000000000000000000000000000000005f5260045ffd5b8063ffffffff8851161190811561347a575b506134525763ffffffff61342461303b828751168385511690612f28565b161561304b577f28df2855000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f9ce30e12000000000000000000000000000000000000000000000000000000005f5260045ffd5b905063ffffffff85511611155f613406565b7f98e089af000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f417de2db000000000000000000000000000000000000000000000000000000005f5260045ffd5b5080511515612ff4565b7ffebd12a0000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f17329d67000000000000000000000000000000000000000000000000000000005f5260045ffd5b9091926001600160a01b036128a79481604051957f23b872dd000000000000000000000000000000000000000000000000000000006020880152166024860152166044840152606483015260648252612dff608483612945565b8115612f5a570490565b805f52600b60205260405f20906001600160a01b03825416156104e65763ffffffff60018301541663ffffffff421680911161380e576135dd9061108a84612968565b906005830180546135ee8185612843565b938415610db357849060017f0000000000000000000000000000000000000000000000000000000000000000613623816127b3565b1461379c575b50613633916128fe565b905561364182600254612843565b6002558160017f0000000000000000000000000000000000000000000000000000000000000000613671816127b3565b1480613773575b6136e7575b506001600160a01b036128a793541680917f0508a8b4117d9a7b3d8f5895f6413e61b4f9a2df35afbfb41e78d0ecfff1843f6020604051868152a36001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016612daf565b91506127106137167f000000000000000000000000000000000000000000000000000000000000000084612d9c565b049180831161374b576128a793613741613738856001600160a01b0394612843565b946003546128fe565b600355935061367d565b7fcd4e6167000000000000000000000000000000000000000000000000000000005f5260045ffd5b507f00000000000000000000000000000000000000000000000000000000000000001515613678565b9050835f52600860205260405f20549081106137ba575b8490613629565b9093508380821115613805576137cf91612843565b925b83156137dd575f6137b3565b7f57e000b1000000000000000000000000000000000000000000000000000000005f5260045ffd5b50505f926137d1565b7f779ecdd8000000000000000000000000000000000000000000000000000000005f5260045ffd5b63ffffffff60608201511680613958575b505f91604082019063ffffffff8251168063ffffffff831611613950575b506020830163ffffffff80825116921691821015613944575b60c0840163ffffffff815116831015613926575b5163ffffffff169081156139175750905b8181116138b2575b5050505090565b613908926101206138fb8463ffffffff6138f36138d761390e9b9a9861390398612843565b826138ea60a08b01928284511690613590565b91511690612d9c565b945116612843565b930151612d9c565b613590565b906128fe565b5f8080806138ab565b63ffffffff91505116906138a3565b9461393c63ffffffff91610100870151906128fe565b959050613892565b60e0840151945061387e565b90505f613865565b8063ffffffff841611156138475791505f613847565b906001600160a01b036139cf92165f806040519361398d604086612945565b602085527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564602086015260208151910182855af16139c9612a39565b91613b2c565b8051908115918215613a4f575b5050156139e557565b608460405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b819250906020918101031261059157602001518015158103610591575f806139dc565b6001600160a01b0316805f52600760205260405f20548015613b28575f19810181811161238f576006545f19810190811161238f57808203613aec575b5050506006548015612eac575f1901613ac781612802565b6001600160a01b0382549160031b1b191690556006555f5260076020525f6040812055565b612888916001600160a01b03613b04613b1493612802565b90549060031b1c16928391612802565b5f52600760205260405f20555f8080613aaf565b5050565b91929015613b8d5750815115613b40575090565b3b15613b495790565b606460405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b825190915015613ba05750805190602001fd5b6044602091601f19601f60405194859362461bcd60e51b85528160048601528051918291826024880152018686015e5f85828601015201168101030190fdfea26469706673582212209ae3ec446b76d238b28286b295cca1ef5c44e7cefb51b824f919d7cdc48e474264736f6c634300081c0033000000000000000000000000d48067f122afc3a58f0f79611f5f1afae0d7f25b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000160e24678fc09000000000000000000000000622d334081cbd7ceebe9a2e240da68bcc26542780000000000000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x60806040526004361015610011575f80fd5b5f3560e01c8063092c9340146126205780630bdf5300146125dd578063137c68fa146125c057806324d7806c146125845780632b7832b3146125675780632c0e5d1c1461254a5780632eb7d4341461249157806343ff9d8d146124745780634a7be739146123f35780634b0bddd21461228457806354f1c9041461211a5780635650d7d714611fda5780635edc1c8814611f19578063661b743d14611de357806367a421b614611db95780636d63b7ff14611d04578063775907f714611bee5780638635512f14611bb457806389a9c7b414610e605780638a85cbba14611b895780638c5143ea14611b665780639335af1a14611a795780639fb40e7b14611919578063a3ba6c7a1461186b578063afe6975e146116eb578063b6b00a50146116b8578063b74513c11461162f578063b8900aa714611407578063b9ed7153146113df578063ba48042b14611361578063ba50bba514611321578063bb64fd4414611303578063bd66528a146112a7578063c415b95c14611281578063c57981b514611247578063caaa9a2214610f78578063cdc6aa4c14610e93578063cedc9d1d14610e60578063cf61a09714610e1c578063d1bc76a114610ddb578063d77836ce14610d1d578063d955d6eb14610cc8578063e1f1c4a714610cac578063e50373f914610b87578063e5b3248714610b21578063e865fbc714610aff578063e940e53814610694578063ecb62f61146105955763f5ee64c014610234575f80fd5b346105915760406003193601126105915760043567ffffffffffffffff8111610591576102659036906004016127d1565b60243567ffffffffffffffff8111610591576102859036906004016127d1565b91335f525f60205260ff60405f20541615610569577f00000000000000000000000000000000000000000000000000000000000000006102c4816127b3565b1561049657801561054157828103610519579291905f935f935b81851061034b57858015610323576103219030336001600160a01b037f000000000000000000000000d48067f122afc3a58f0f79611f5f1afae0d7f25b16613536565b005b7fbb21bfd4000000000000000000000000000000000000000000000000000000005f5260045ffd5b909192939461035b868484612c39565b3595610368818787612c39565b35801561050e57875f52600b60205260405f2080546001600160a01b038116156104e65760e01c6104be578060046103ac60026103b59401546003840154906128fe565b910154906128fe565b97805f52600860205260405f205489811015610496576103d5908a612843565b821161046e577f7d880d31c520e2aafc7d84cd9b7b8519c22fa45a2682a2b09035af562b3820a461043083600196845f52600860205260405f2061041a8382546128fe565b9055610428826002546128fe565b6002556128fe565b99825f52600860205260405f20546104606040519283923397846040919493926060820195825260208201520152565b0390a35b01939291906102de565b7fb6b88bbc000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fbbc4bdcf000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f48a0eba2000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f3a605dd2000000000000000000000000000000000000000000000000000000005f5260045ffd5b506001919650610464565b7fa24a13a6000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f521299a9000000000000000000000000000000000000000000000000000000005f5260045ffd5b7ffdd63ca2000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f80fd5b60206003193601126105915760043567ffffffffffffffff8111610591576105c19036906004016127d1565b90335f525f60205260ff60405f20541615610569578115610541577f0000000000000000000000000000000000000000000000000000000000000000610606816127b3565b1580610661575b610639575f5b82811061061c57005b8061063361062d6001938686612c39565b3561359a565b01610613565b7f025dbdd4000000000000000000000000000000000000000000000000000000005f5260045ffd5b5061068c827f000000000000000000000000000000000000000000000000000160e24678fc09612d9c565b34141561060d565b346105915760206003193601126105915760043567ffffffffffffffff8111610591578060040190610140600319823603011261059157335f525f60205260ff60405f20541615610569576106e98280612d37565b5f9291506106f681612c49565b936024830191806107078484612d37565b905014801590610ae7575b8015610acf575b8015610ab7575b8015610a9f575b8015610a87575b8015610a6f575b8015610a56575b8015610a3d575b61051957929085905f936044840160648501936084860160a4870160c488019460e48901936101246101048b019a01905b8c8c106107f1578d7f00000000000000000000000000000000000000000000000000000000000000006107a6816127b3565b156107ad57005b806107bd610321926002546128fe565b60025530336001600160a01b037f000000000000000000000000d48067f122afc3a58f0f79611f5f1afae0d7f25b16613536565b909192939495969798999a9b9c8c6108186004549161080f83612850565b60045584612a25565b528c6108396108278285612a25565b51916108338d80612d37565b90612c39565b356001600160a01b03811680910361059157848f8f908f8f8f918f908f94918f928f948f8280808080808061086f81988a612d37565b6108799291612c39565b61088290612d8b565b9961088d9089612d37565b6108979291612c39565b6108a090612d8b565b996108ab9088612d37565b6108b59291612c39565b6108be90612d8b565b996108c99087612d37565b6108d39291612c39565b35996108df9086612d37565b6108e99291612c39565b6108f290612d8b565b996108fd9085612d37565b6109079291612c39565b35996109139084612d37565b61091d9291612c39565b61092690612d8b565b996109319083612d37565b61093b9291612c39565b359961094691612d37565b6109509291612c39565b3598891515809a03610591576040519a6109698c61290b565b8b5260208b015263ffffffff1660408a015263ffffffff16606089015263ffffffff16608088015260a087015263ffffffff1660c086015260e085015263ffffffff166101008401526101208301526101408201526109c790612f6e565b8b8d8a818d816109d78c83612d37565b6109e19291612c39565b35926109ec91612d37565b6109f69291612c39565b35610a00916128fe565b91610a0b908d612d37565b610a159291612c39565b35610a1f916128fe565b610a28916128fe565b9c9b6001019a99989796959493929190610774565b5080610a4d610124860184612d37565b90501415610743565b5080610a66610104860184612d37565b9050141561073c565b5080610a7e60e4860184612d37565b90501415610735565b5080610a9660a4860184612d37565b9050141561072e565b5080610aae60c4860184612d37565b90501415610727565b5080610ac66084860184612d37565b90501415610720565b5080610ade6064860184612d37565b90501415610719565b5080610af66044860184612d37565b90501415610712565b34610591575f600319360112610591576020610b19612c7a565b604051908152f35b34610591576020600319360112610591576001600160a01b03610b426126f9565b168015610b5f575f526009602052602060405f2054604051908152f35b7fe6c4247b000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461059157602060031936011261059157610ba06126f9565b335f525f60205260ff60405f20541615610569576001600160a01b03168015610b5f576001600160a01b037f000000000000000000000000d48067f122afc3a58f0f79611f5f1afae0d7f25b168114610c8457604051907f70a08231000000000000000000000000000000000000000000000000000000008252306004830152602082602481845afa908115610c79575f91610c43575b61032192503390612daf565b90506020823d602011610c71575b81610c5e60209383612945565b8101031261059157610321915190610c37565b3d9150610c51565b6040513d5f823e3d90fd5b7fc1ab6dc1000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610591575f6003193601126105915760206040516127108152f35b3461059157604060031936011261059157610ce16126f9565b6001600160a01b0360243591165f52600960205260405f20805482101561059157602091610d0e9161282e565b90549060031b1c604051908152f35b3461059157602060031936011261059157600435335f525f60205260ff60405f205416156105695780610d4e612c7a565b10610db357610321906040518181527fca1cf43de312865665f595e88f569f9d5246690c07df26e86aba01147e6d131460203392a2336001600160a01b037f000000000000000000000000d48067f122afc3a58f0f79611f5f1afae0d7f25b16612daf565b7ff4d678b8000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461059157602060031936011261059157600435600654811015610591576001600160a01b03610e0c602092612802565b90549060031b1c16604051908152f35b34610591575f6003193601126105915760207f000000000000000000000000000000000000000000000000000000000000000060405190610e5c816127b3565b8152f35b34610591576020600319360112610591576004355f52600c60205260206001600160a01b0360405f205416604051908152f35b3461059157606060031936011261059157600435602435604435916001600160a01b038316809303610591578215610b5f57818110801590610f63575b610f3b57610ee6610ee18284612843565b612c49565b92815b838110610f025760405180610efe878261277a565b0390f35b600190825f526009602052610f1a8160405f2061282e565b90549060031b1c610f34610f2e8684612843565b88612a25565b5201610ee9565b7f561ce9bb000000000000000000000000000000000000000000000000000000005f5260045ffd5b50825f52600960205260405f20548211610ed0565b346105915760206003193601126105915760043567ffffffffffffffff811161059157610fa99036906004016127d1565b90335f525f60205260ff60405f20541615610569578115610541575f63ffffffff4216927f00000000000000000000000000000000000000000000000000000000000000009060018214925b818110610ffe57005b611009818387612c39565b3590815f52600b60205260405f205460e01c6104be57815f52600b60205260405f209163ffffffff835460c01c168042101561121f576006840160ff815416156111f7578961109b6101a092876110a07ffab6a0d43d3a59dd207bc8acbb42f638ace4f051bc53d95dd662fbfa5bc0213a9661108f8661108a60019d612968565b613836565b94859161108a85612968565b612843565b928c6110ab8d6127b3565b6111b4575b508061110960ff9495600593907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffff0000000000000000000000000000000000000000000000000000000083549260e01b169116179055565b61111585600254612843565b60025560405194855280546001600160a01b038116602087015263ffffffff8160a01c16604087015263ffffffff8160c01c16606087015260e01c608086015263ffffffff8a82015481811660a0880152818160201c1660c088015260401c1660e0860152600281015461010086015260038101546101208601526004810154610140860152015461016084015254161515610180820152a201610ff5565b5f8881526008602052604090205460ff945060059291908181116111e45750506111095f955b95945050906110b0565b611109916111f191612843565b956111da565b7fce21cad1000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f2e8e455d000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610591575f6003193601126105915760206040517f000000000000000000000000000000000000000000000000000160e24678fc098152f35b34610591575f6003193601126105915760206001600160a01b0360055416604051908152f35b602060031936011261059157600435805f52600b6020526001600160a01b0360405f20541633036112db5761032190612a78565b7f75228a9c000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610591576020600319360112610591576020610b19600435612b5d565b34610591575f6003193601126105915760207f000000000000000000000000000000000000000000000000000000000000000060405190610e5c816127b3565b34610591576020600319360112610591576001600160a01b036113826126f9565b165f52600960205260405f206040519081602082549182815201915f5260205f20905f5b8181106113c957610efe856113bd81870382612945565b6040519182918261277a565b82548452602090930192600192830192016113a6565b346105915760206003193601126105915760206113fd600435612ae0565b6040519015158152f35b3461059157602060031936011261059157600435335f525f60205260ff60405f2054161561056957805f52600b60205260405f205460e01c6104be57805f52600b60205260405f2063ffffffff815460c01c168042101561121f57600682019060ff825416156111f75761109b6101a092846114b86114a67ffab6a0d43d3a59dd207bc8acbb42f638ace4f051bc53d95dd662fbfa5bc0213a97612968565b9461108f63ffffffff42168097613836565b9260017f00000000000000000000000000000000000000000000000000000000000000006114e5816127b3565b146115ec575b508061154460ff9495600593907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffff0000000000000000000000000000000000000000000000000000000083549260e01b169116179055565b61155085600254612843565b60025560405194855280546001600160a01b038116602087015263ffffffff8160a01c16604087015263ffffffff8160c01c16606087015260e01c608086015263ffffffff600182015481811660a0880152818160201c1660c088015260401c1660e0860152600281015461010086015260038101546101208601526004810154610140860152015461016084015254161515610180820152a2005b5f8881526008602052604090205460ff9450600592919081811161161c5750506115445f955b95945050906114eb565b6115449161162991612843565b95611612565b34610591575f6003193601126105915760405180602060065491828152019060065f527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f905f5b81811061169957610efe8561168d81870382612945565b60405191829182612738565b82546001600160a01b0316845260209093019260019283019201611676565b34610591576040600319360112610591576020610b196116d6612725565b6004355f52600b835261108a60405f20612968565b3461059157604060031936011261059157600435602435815f52600b60205260405f205460e01c6104be57335f525f60205260ff60405f20541615610569577f0000000000000000000000000000000000000000000000000000000000000000611754816127b3565b1561049657801561032357815f52600b60205260405f206001600160a01b03815416156104e6578060046103ac60026117949401546003840154906128fe565b825f52600860205260405f205481811015610496576117b39082612843565b821161046e5782610321935f52600860205260405f206117d48482546128fe565b90556117e2836002546128fe565b600255805f5260086020527f7d880d31c520e2aafc7d84cd9b7b8519c22fa45a2682a2b09035af562b3820a461183760405f205493604051918291339688846040919493926060820195825260208201520152565b0390a330336001600160a01b037f000000000000000000000000d48067f122afc3a58f0f79611f5f1afae0d7f25b16613536565b3461059157602060031936011261059157600435805f52600b60205260405f206001600160a01b03815416156104e6576060916118e17f0000000000000000000000000000000000000000000000000000000000000000926118cc846127b3565b60046103ac60028301546003840154906128fe565b906118eb836127b3565b826119085750805b60ff6040519316835260208301526040820152f35b5f52600860205260405f20546118f3565b3461059157602060031936011261059157600435805f52600b60205260405f205460e01c6104be57805f52600c6020526001600160a01b0360405f205416338103611a5157815f52600b60205260405f206001600160a01b03815416906119808483612e04565b6001600160a01b03831673ffffffffffffffffffffffffffffffffffffffff198254161790556119af82612f08565b15611a33575b815f5260096020526119ca8360405f206128bd565b5f82815260096020908152604080832054600a8352818420878552835281842055600c9091528120805473ffffffffffffffffffffffffffffffffffffffff191690557fd26d820c791a6bb2143a3066ee2a8314ff045a3abc3f256346814ad5be7d63469080a4005b611a3c8261285e565b600654825f52600760205260405f20556119b5565b7fc414c24a000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461059157604060031936011261059157600435611a9561270f565b815f52600b6020526001600160a01b0360405f20541633036112db57815f52600b60205260405f205460e01c6104be576001600160a01b03168015610b5f57815f52600c6020526001600160a01b0360405f205416611b3e57815f52600c60205260405f208173ffffffffffffffffffffffffffffffffffffffff19825416179055337f666421a92fd891d9481da847947b32bfd485b5a6ac70ef477f18b9aae576c3715f80a4005b7f8f524afa000000000000000000000000000000000000000000000000000000005f5260045ffd5b346105915760206003193601126105915760206113fd611b846126f9565b612f08565b602060031936011261059157335f525f60205260ff60405f2054161561056957610321600435612a78565b34610591575f6003193601126105915760206040517f00000000000000000000000000000000000000000000000000000000016ee7f88152f35b3461059157604060031936011261059157611c076126f9565b6024356001600160a01b03600554163303611cdc576001600160a01b038216918215610b5f57478015610db3578215611cd4575b8211611cac575f8080938193867fee3a3e8b975ee1a894fd6ed0a36ec6d1db3dc70e575382d21e9aed5a2c72f5146020604051858152a25af1611c7c612a39565b5015611c8457005b7f90b8ec18000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f732f9413000000000000000000000000000000000000000000000000000000005f5260045ffd5b915081611c3b565b7f73fcd3fe000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461059157604060031936011261059157600435602435808210801590611dae575b610f3b57611d348282612843565b91601f19611d5a611d4485612a0d565b94611d526040519687612945565b808652612a0d565b01366020850137805b828110611d785760405180610efe8682612738565b806001600160a01b03611d8c600193612802565b90549060031b1c16611da7611da18584612843565b87612a25565b5201611d63565b506006548111611d26565b34610591576020600319360112610591576004355f526008602052602060405f2054604051908152f35b34610591576020600319360112610591575f610160604051611e0481612928565b8281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e082015282610100820152826101208201528261014082015201526004355f52600b602052610efe611e6160405f20612968565b60405191829182919091610160806101808301946001600160a01b03815116845263ffffffff602082015116602085015263ffffffff604082015116604085015263ffffffff606082015116606085015263ffffffff608082015116608085015263ffffffff60a08201511660a085015263ffffffff60c08201511660c085015260e081015160e085015261010081015161010085015261012081015161012085015261014081015161014085015201511515910152565b3461059157602060031936011261059157600435805f52600b6020526001600160a01b0360405f20541633036112db57805f52600c6020526001600160a01b0360405f20541615611fb257805f52600c60205260405f2073ffffffffffffffffffffffffffffffffffffffff198154169055337f155ceda63be6f9c02f83b108c17e45601c8e72e9845fd5a15c4c33b53e8cac1b5f80a3005b7fd5e80fb4000000000000000000000000000000000000000000000000000000005f5260045ffd5b346105915761014060031936011261059157611ff46126f9565b611ffc612725565b6044359063ffffffff82168092036105915760643563ffffffff81168091036105915760843560a43563ffffffff81168091036105915760c43560e4359163ffffffff8316809303610591576101043593610124359586151580970361059157335f525f60205260ff60405f20541615610569576120f09863ffffffff6120b2996001600160a01b036120988a6120938a896128fe565b6128fe565b9d6004546120a581612850565b6004556040519d8e61290b565b8d521660208c01521660408a01526060890152608088015260a087015260c086015260e0850152610100840152610120830152610140820152612f6e565b7f00000000000000000000000000000000000000000000000000000000000000006107a6816127b3565b346105915760406003193601126105915760043561213661270f565b815f52600b6020526001600160a01b0360405f20541633036112db57815f52600b60205260405f205460e01c6104be576001600160a01b038116908115610b5f57825f52600b60205260405f20906001600160a01b038254169161219a8584612e04565b8373ffffffffffffffffffffffffffffffffffffffff198254161790556121c081612f08565b15612265575b50815f5260096020526121dc8360405f206128bd565b5f82815260096020908152604080832054600a8352818420878552835281842055600c9091529020546001600160a01b0316612239575b7fd26d820c791a6bb2143a3066ee2a8314ff045a3abc3f256346814ad5be7d63465f80a4005b825f52600c60205260405f2073ffffffffffffffffffffffffffffffffffffffff198154169055612213565b61226e9061285e565b600654825f52600760205260405f2055836121c6565b346105915760406003193601126105915761229d6126f9565b602435908115159081830361059157335f525f60205260ff60405f20541615610569576001600160a01b0316918215610b5f57825f525f6020528160ff60405f2054161515146123cb578015612349577fe529461c8529abc0e0fe7c5ee361f74fe22e0b7574df1fc0b7558a282091fb7891612340602092612320600154612850565b6001555b855f525f845260405f209060ff60ff1983541691151516179055565b604051908152a2005b6001549160018311156123a357821561238f576123406020927fe529461c8529abc0e0fe7c5ee361f74fe22e0b7574df1fc0b7558a282091fb78945f1901600155612324565b634e487b7160e01b5f52601160045260245ffd5b7fc13a62ad000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fd52dfc1c000000000000000000000000000000000000000000000000000000005f5260045ffd5b346105915760206003193601126105915761240c6126f9565b600554906001600160a01b03821690813303611cdc576001600160a01b0316918215610b5f5773ffffffffffffffffffffffffffffffffffffffff191682176005557f5d16ad41baeb009cd23eb8f6c7cde5c2e0cd5acf4a33926ab488875c37c37f385f80a3005b34610591575f600319360112610591576020600654604051908152f35b34610591576020600319360112610591576004355f52600b60205261018060405f208054906001810154906002810154600382015460048301549163ffffffff60ff600660058701549601541695604051976001600160a01b0381168952828160a01c1660208a0152828160c01c1660408a015260e01c60608901528181166080890152818160201c1660a089015260401c1660c087015260e08601526101008501526101208401526101408301521515610160820152f35b34610591575f600319360112610591576020600354604051908152f35b34610591575f600319360112610591576020600154604051908152f35b34610591576020600319360112610591576001600160a01b036125a56126f9565b165f525f602052602060ff60405f2054166040519015158152f35b34610591575f600319360112610591576020600254604051908152f35b34610591575f6003193601126105915760206040516001600160a01b037f000000000000000000000000d48067f122afc3a58f0f79611f5f1afae0d7f25b168152f35b34610591576040600319360112610591576126396126f9565b6024356001600160a01b03600554163303611cdc576001600160a01b038216918215610b5f57600354928315610db357610321938315818582156126ef575b50506126e7575b8361268991612843565b6003556001600160a01b037f000000000000000000000000d48067f122afc3a58f0f79611f5f1afae0d7f25b1680917f14f7c82e513e1c45523692386ca72ff853393b9b4228a9f6cc27fd6238d929d86020604051878152a3612daf565b92508261267f565b1190508187612678565b600435906001600160a01b038216820361059157565b602435906001600160a01b038216820361059157565b6024359063ffffffff8216820361059157565b60206040818301928281528451809452019201905f5b81811061275b5750505090565b82516001600160a01b031684526020938401939092019160010161274e565b60206040818301928281528451809452019201905f5b81811061279d5750505090565b8251845260209384019390920191600101612790565b600211156127bd57565b634e487b7160e01b5f52602160045260245ffd5b9181601f840112156105915782359167ffffffffffffffff8311610591576020808501948460051b01011161059157565b60065481101561281a5760065f5260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b805482101561281a575f5260205f2001905f90565b9190820391821161238f57565b5f19811461238f5760010190565b60065490680100000000000000008210156128a9576128888260016128a79401600655600661282e565b9091906001600160a01b038084549260031b9316831b921b1916179055565b565b634e487b7160e01b5f52604160045260245ffd5b805490680100000000000000008210156128a957816128e49160016128fa9401815561282e565b819391549060031b91821b915f19901b19161790565b9055565b9190820180921161238f57565b610160810190811067ffffffffffffffff8211176128a957604052565b610180810190811067ffffffffffffffff8211176128a957604052565b90601f601f19910116810190811067ffffffffffffffff8211176128a957604052565b9060405161297581612928565b61016060ff6006839580546001600160a01b038116865263ffffffff8160a01c16602087015263ffffffff8160c01c16604087015260e01c606086015263ffffffff60018201548181166080880152818160201c1660a088015260401c1660c0860152600281015460e08601526003810154610100860152600481015461012086015260058101546101408601520154161515910152565b67ffffffffffffffff81116128a95760051b60200190565b805182101561281a5760209160051b010190565b3d15612a73573d9067ffffffffffffffff82116128a95760405191612a68601f8201601f191660200184612945565b82523d5f602084013e565b606090565b7f0000000000000000000000000000000000000000000000000000000000000000612aa2816127b3565b1580612ab6575b610639576128a79061359a565b507f000000000000000000000000000000000000000000000000000160e24678fc09341415612aa9565b805f52600b60205260405f206001600160a01b03815416156104e6577f0000000000000000000000000000000000000000000000000000000000000000612b26816127b3565b15612b56578060046103ac6002612b449401546003840154906128fe565b905f52600860205260405f2054101590565b5050600190565b805f52600b60205260405f206001600160a01b03815416156104e65763ffffffff60018201541663ffffffff4216809111612c3257612ba160059161108a84612968565b91015480821115612c2c57612bb68183612843565b9260017f0000000000000000000000000000000000000000000000000000000000000000612be3816127b3565b14612bef575b50505090565b5f52600860205260405f2054918210612c09575b80612be9565b90915080821115612c2457612c1d91612843565b5f80612c03565b50505f612c1d565b5f612bb6565b5050505f90565b919081101561281a5760051b0190565b90612c5382612a0d565b612c606040519182612945565b828152601f19612c708294612a0d565b0190602036910137565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526020816024816001600160a01b037f000000000000000000000000d48067f122afc3a58f0f79611f5f1afae0d7f25b165afa908115610c79575f91612d03575b50612cf7612d009160025490612843565b60035490612843565b90565b90506020813d602011612d2f575b81612d1e60209383612945565b810103126105915751612cf7612ce6565b3d9150612d11565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610591570180359067ffffffffffffffff821161059157602001918160051b3603831361059157565b3563ffffffff811681036105915790565b8181029291811591840414171561238f57565b6128a7926001600160a01b03604051937fa9059cbb000000000000000000000000000000000000000000000000000000006020860152166024840152604483015260448252612dff606483612945565b61396e565b906001600160a01b03821690815f52600960205260405f2091805f52600a60205260405f20825f5260205260405f20548015612f01575f19810181811161238f5784545f1981019190821161238f57818103612ec0575b50505082548015612eac575f1901612e73818561282e565b8154905f199060031b1b1916905583555f52600a60205260405f20905f526020525f60408120555415612ea35750565b6128a790613a72565b634e487b7160e01b5f52603160045260245ffd5b612ee0612ed06128e4938861282e565b90549060031b1c9283928861282e565b9055825f52600a60205260405f20905f5260205260405f20555f8080612e5b565b5050505050565b6001600160a01b03168015610b5f575f52600760205260405f2054151590565b9063ffffffff8091169116039063ffffffff821161238f57565b9063ffffffff16908115612f5a5763ffffffff160690565b634e487b7160e01b5f52601260045260245ffd5b602081016001600160a01b0381511615610b5f57610120820191825192612f9b60a08301948551906128fe565b612fab60e08401918251906128fe565b1561350e57604083019163ffffffff835116156134e65763ffffffff83511690606085019163ffffffff835116106134b45763ffffffff84511663ffffffff83511614806134dc575b6134b457610100850163ffffffff8151161561348c5760c08601805163ffffffff16806133f4575084516133cc5763ffffffff61304561303b82875116838a511690612f28565b8285511690612f42565b166133a4575b63ffffffff806001600160a01b038a51169751169451169863ffffffff808060808b015116925193511696519351169351936101408901511515966040519b8c996130958b612928565b8a5260208a0197885260408a0190815260608a01905f825260808b0194855260a08b0193845260c08b0192835260e08b019586526101008b019687526101208b019788526101408b01985f8a526101608c019a8b528d6130fe6001600160a01b03825116612f08565b15613370575b508c515f908152600b602052604090209b519051915192517fffffffff0000000000000000000000000000000000000000000000000000000060e09190911b1660c09390931b7bffffffff000000000000000000000000000000000000000000000000166001600160a01b0390911660a09290921b77ffffffff000000000000000000000000000000000000000016919091171717895560018901925163ffffffff168354925160201b67ffffffff0000000016915160401b6bffffffff000000000000000016927fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001617171790555160028601555160038501555160048401555160058301555115159060060190613229919060ff60ff1983541691151516179055565b81516001600160a01b03165f52600960205260405f20815161324a916128bd565b81516001600160a01b03165f52600960205260405f205482516001600160a01b03165f52600a60205260405f2082515f5260205260405f20555190516001600160a01b03169160405161334a819282919091610160806101808301946001600160a01b03815116845263ffffffff602082015116602085015263ffffffff604082015116604085015263ffffffff606082015116606085015263ffffffff608082015116608085015263ffffffff60a08201511660a085015263ffffffff60c08201511660c085015260e081015160e085015261010081015161010085015261012081015161012085015261014081015161014085015201511515910152565b037f040a2d252643cef2c4dea3c242e5ed4bf32a76631de8342604dd6cba3074047291a3565b6001600160a01b0361338391511661285e565b8d6001600160a01b036006549151165f52600760205260405f20558d613104565b7f28df2855000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fea243c32000000000000000000000000000000000000000000000000000000005f5260045ffd5b8063ffffffff8851161190811561347a575b506134525763ffffffff61342461303b828751168385511690612f28565b161561304b577f28df2855000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f9ce30e12000000000000000000000000000000000000000000000000000000005f5260045ffd5b905063ffffffff85511611155f613406565b7f98e089af000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f417de2db000000000000000000000000000000000000000000000000000000005f5260045ffd5b5080511515612ff4565b7ffebd12a0000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f17329d67000000000000000000000000000000000000000000000000000000005f5260045ffd5b9091926001600160a01b036128a79481604051957f23b872dd000000000000000000000000000000000000000000000000000000006020880152166024860152166044840152606483015260648252612dff608483612945565b8115612f5a570490565b805f52600b60205260405f20906001600160a01b03825416156104e65763ffffffff60018301541663ffffffff421680911161380e576135dd9061108a84612968565b906005830180546135ee8185612843565b938415610db357849060017f0000000000000000000000000000000000000000000000000000000000000000613623816127b3565b1461379c575b50613633916128fe565b905561364182600254612843565b6002558160017f0000000000000000000000000000000000000000000000000000000000000000613671816127b3565b1480613773575b6136e7575b506001600160a01b036128a793541680917f0508a8b4117d9a7b3d8f5895f6413e61b4f9a2df35afbfb41e78d0ecfff1843f6020604051868152a36001600160a01b037f000000000000000000000000d48067f122afc3a58f0f79611f5f1afae0d7f25b16612daf565b91506127106137167f000000000000000000000000000000000000000000000000000160e24678fc0984612d9c565b049180831161374b576128a793613741613738856001600160a01b0394612843565b946003546128fe565b600355935061367d565b7fcd4e6167000000000000000000000000000000000000000000000000000000005f5260045ffd5b507f000000000000000000000000000000000000000000000000000160e24678fc091515613678565b9050835f52600860205260405f20549081106137ba575b8490613629565b9093508380821115613805576137cf91612843565b925b83156137dd575f6137b3565b7f57e000b1000000000000000000000000000000000000000000000000000000005f5260045ffd5b50505f926137d1565b7f779ecdd8000000000000000000000000000000000000000000000000000000005f5260045ffd5b63ffffffff60608201511680613958575b505f91604082019063ffffffff8251168063ffffffff831611613950575b506020830163ffffffff80825116921691821015613944575b60c0840163ffffffff815116831015613926575b5163ffffffff169081156139175750905b8181116138b2575b5050505090565b613908926101206138fb8463ffffffff6138f36138d761390e9b9a9861390398612843565b826138ea60a08b01928284511690613590565b91511690612d9c565b945116612843565b930151612d9c565b613590565b906128fe565b5f8080806138ab565b63ffffffff91505116906138a3565b9461393c63ffffffff91610100870151906128fe565b959050613892565b60e0840151945061387e565b90505f613865565b8063ffffffff841611156138475791505f613847565b906001600160a01b036139cf92165f806040519361398d604086612945565b602085527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564602086015260208151910182855af16139c9612a39565b91613b2c565b8051908115918215613a4f575b5050156139e557565b608460405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b819250906020918101031261059157602001518015158103610591575f806139dc565b6001600160a01b0316805f52600760205260405f20548015613b28575f19810181811161238f576006545f19810190811161238f57808203613aec575b5050506006548015612eac575f1901613ac781612802565b6001600160a01b0382549160031b1b191690556006555f5260076020525f6040812055565b612888916001600160a01b03613b04613b1493612802565b90549060031b1c16928391612802565b5f52600760205260405f20555f8080613aaf565b5050565b91929015613b8d5750815115613b40575090565b3b15613b495790565b606460405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b825190915015613ba05750805190602001fd5b6044602091601f19601f60405194859362461bcd60e51b85528160048601528051918291826024880152018686015e5f85828601015201168101030190fdfea26469706673582212209ae3ec446b76d238b28286b295cca1ef5c44e7cefb51b824f919d7cdc48e474264736f6c634300081c0033

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ 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.