ETH Price: $3,225.41 (+1.70%)

Contract

0x6A6553e4d4732Cbb10e33069480A8f24Ad678CCE
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Create Split193287492024-02-28 21:36:11321 days ago1709156171IN
0x6A6553e4...4Ad678CCE
0 ETH0.0079864265.31582297

Latest 2 internal transactions

Advanced mode:
Parent Transaction Hash Block
From
To
193287492024-02-28 21:36:11321 days ago1709156171
0x6A6553e4...4Ad678CCE
 Contract Creation0 ETH
185824912023-11-16 5:55:23425 days ago1700114123
0x6A6553e4...4Ad678CCE
 Contract Creation0 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
SplitMain

Compiler Version
v0.8.18+commit.87f61d96

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion
File 1 of 10 : SplitMain.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.16;

import { ISplitMain } from "./interfaces/ISplitMain.sol";
import { SplitWallet } from "./SplitWallet.sol";
import { Clones } from "../utils/Clones.sol";
import { IERC20Upgradeable as IERC20 } from "openzeppelin-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol";
import { IWETH } from "../interfaces/IWETH.sol";
import { ISonaSwap } from "lib/common/ISonaSwap.sol";
import { Ownable } from "solady/auth/Ownable.sol";

/// @title SplitMain
/// @author @SonaEngineering; forked from 0xSplits <[email protected]>
/// @notice A composable and gas-efficient protocol for deploying splitter contracts.
/// @dev Split recipients, ownerships, and keeper fees are stored onchain as calldata & re-passed as args / validated
/// via hashing when needed. Each split gets its own address & proxy for maximum composability with other contracts onchain.
/// For these proxies, we extended EIP-1167 Minimal Proxy Contract to avoid `DELEGATECALL` inside `receive()` to accept
/// hard gas-capped `sends` & `transfers`.
contract SplitMain is ISplitMain, Ownable {
	using SafeTransferLib for address;
	using SafeTransferLib for IERC20;

	/// ERRORS

	/// @notice Unauthorized sender `sender`
	/// @param sender Transaction sender
	error UnauthorizedController(address sender);
	/// @notice Invalid number of accounts `accountsLength`, must have at least 2
	/// @param accountsLength Length of accounts array
	error InvalidSplit__TooFewAccounts(uint256 accountsLength);
	/// @notice Array lengths of accounts & percentAllocations don't match (`accountsLength` != `allocationsLength`)
	/// @param accountsLength Length of accounts array
	/// @param allocationsLength Length of percentAllocations array
	error InvalidSplit__AccountsAndAllocationsMismatch(
		uint256 accountsLength,
		uint256 allocationsLength
	);
	/// @notice Invalid percentAllocations sum `allocationsSum` must equal `PERCENTAGE_SCALE`
	/// @param allocationsSum Sum of percentAllocations array
	error InvalidSplit__InvalidAllocationsSum(uint32 allocationsSum);
	/// @notice Invalid accounts ordering at `index`
	/// @param index Index of out-of-order account
	error InvalidSplit__AccountsOutOfOrder(uint256 index);
	/// @notice Invalid percentAllocation of zero at `index`
	/// @param index Index of zero percentAllocation
	error InvalidSplit__AllocationMustBePositive(uint256 index);
	/// @notice Invalid distributorFee `distributorFee` cannot be greater than 10% (1e5)
	/// @param distributorFee Invalid distributorFee amount
	error InvalidSplit__InvalidDistributorFee(uint32 distributorFee);
	/// @notice Invalid hash `hash` from split data (accounts, percentAllocations, distributorFee)
	/// @param hash Invalid hash
	error InvalidSplit__InvalidHash(bytes32 hash);
	/// @notice Invalid new controlling address `newController` for mutable split
	/// @param newController Invalid new controller
	error InvalidNewController(address newController);

	//
	// STRUCTS
	//

	/// @notice holds Split metadata
	struct Split {
		bytes32 hash;
		address controller;
		address newPotentialController;
	}

	//
	// Storage
	//

	//
	// STORAGE - CONSTANTS & IMMUTABLES
	//

	/// @notice constant to scale uints into percentages (1e6 == 100%)
	uint256 public constant PERCENTAGE_SCALE = 1e6;
	/// @notice maximum distributor fee; 1e5 = 10%/// PERCENTAGE_SCALE
	uint256 internal constant _MAX_DISTRIBUTOR_FEE = 1e5;
	/// @notice address of wallet implementation for split proxies
	address public immutable override walletImplementation;
	/// @notice address of the WETH token
	IWETH public immutable WETH9;
	/// @notice address of the USDC token
	IERC20 public immutable USDC;
	/// @notice address of the swapper contract that converts (W)ETH to USDC
	ISonaSwap public swap;

	//
	// STORAGE - VARIABLES - PRIVATE & INTERNAL
	//

	/// @notice mapping to account ETH balances
	mapping(address => uint256) internal _ethBalances;
	/// @notice mapping to account ERC20 balances
	mapping(IERC20 => mapping(address => uint256)) internal _erc20Balances;
	/// @notice mapping to Split metadata
	mapping(address => Split) internal _splits;

	//
	// MODIFIERS
	//

	/// @notice Reverts if the sender doesn't own the split `split`
	/// @param split Address to check for control
	modifier onlySplitController(address split) {
		if (msg.sender != _splits[split].controller)
			revert UnauthorizedController(msg.sender);
		_;
	}

	/// @notice Reverts if the sender isn't the new potential controller of split `split`
	/// @param split Address to check for new potential control
	modifier onlySplitNewPotentialController(address split) {
		if (msg.sender != _splits[split].newPotentialController)
			revert UnauthorizedController(msg.sender);
		_;
	}

	/// @notice Reverts if the split with recipients represented by `accounts` and `percentAllocations` is malformed
	/// @param accounts Ordered, unique list of addresses with ownership in the split
	/// @param percentAllocations Percent allocations associated with each address
	modifier validSplit(
		address[] memory accounts,
		uint32[] memory percentAllocations
	) {
		_validSplit(accounts, percentAllocations);
		_;
	}

	/// @notice Reverts if `newController` is the zero address
	/// @param newController Proposed new controlling address
	modifier validNewController(address newController) {
		if (newController == address(0)) revert InvalidNewController(newController);
		_;
	}

	// CONSTRUCTOR

	constructor(IWETH _weth, IERC20 _usdc, ISonaSwap _swap) {
		walletImplementation = address(new SplitWallet());
		WETH9 = _weth;
		USDC = _usdc;
		swap = _swap;

		_initializeOwner(msg.sender);
	}

	// FUNCTIONS

	// FUNCTIONS - PUBLIC & EXTERNAL

	/// @notice Receive ETH
	/// @dev Used by split proxies in `distributeETH` to transfer ETH to `SplitMain`
	///		Funds sent outside of `distributeETH` will be unrecoverable
	receive() external payable {} // solhint-disable-line no-empty-blocks

	/// @notice Creates a new split with recipients `accounts` with ownerships `percentAllocations`, a keeper fee for splitting of `distributorFee` and the controlling address `controller`
	/// @param accounts Ordered, unique list of addresses with ownership in the split
	/// @param percentAllocations Percent allocations associated with each address
	/// @return split Address of newly created split
	function createSplit(
		address[] calldata accounts,
		uint32[] calldata percentAllocations
	)
		public
		override
		validSplit(accounts, percentAllocations)
		returns (address split)
	{
		bytes32 splitHash = _hashSplit(accounts, percentAllocations);
		// create mutable split
		split = Clones.clone(walletImplementation);
		_splits[split].controller = msg.sender;
		// store split's hash in storage for future verification
		_splits[split].hash = splitHash;
		emit CreateSplit(split);
	}

	/// @notice create multiple splits in a one call
	/// @dev the same config can be created multiple times
	/// @param splits the array structured objects representing the split configs
	function createSplits(
		SplitInput[] calldata splits
	) external override returns (address[] memory splitAddresses) {
		uint256 splitsLength = splits.length;
		splitAddresses = new address[](splitsLength);
		for (uint256 idx; idx < splitsLength; idx++) {
			splitAddresses[idx] = createSplit(
				splits[idx].accounts,
				splits[idx].percentAllocations
			);
		}
	}

	/// @notice Updates an existing split with recipients `accounts` with ownerships `percentAllocations` and a keeper fee for splitting of `distributorFee`
	/// @param split Address of mutable split to update
	/// @param accounts Ordered, unique list of addresses with ownership in the split
	/// @param percentAllocations Percent allocations associated with each address
	function updateSplit(
		address split,
		address[] calldata accounts,
		uint32[] calldata percentAllocations
	)
		external
		override
		onlySplitController(split)
		validSplit(accounts, percentAllocations)
	{
		_updateSplit(split, accounts, percentAllocations);
	}

	/// @notice Begins transfer of the controlling address of mutable split `split` to `newController`
	///  @dev Two-step control transfer inspired by [dharma](https://github.com/dharma-eng/dharma-smart-wallet/blob/master/contracts/helpers/TwoStepOwnable.sol)
	///  @param split Address of mutable split to transfer control for
	///  @param newController Address to begin transferring control to
	function transferControl(
		address split,
		address newController
	)
		external
		override
		onlySplitController(split)
		validNewController(newController)
	{
		_splits[split].newPotentialController = newController;
		emit InitiateControlTransfer(split, newController);
	}

	/// @notice Cancels transfer of the controlling address of mutable split `split`
	/// @param split Address of mutable split to cancel control transfer for
	function cancelControlTransfer(
		address split
	) external override onlySplitController(split) {
		delete _splits[split].newPotentialController;
		emit CancelControlTransfer(split);
	}

	/// @notice Accepts transfer of the controlling address of mutable split `split`
	/// @param split Address of mutable split to accept control transfer for
	function acceptControl(
		address split
	) external override onlySplitNewPotentialController(split) {
		delete _splits[split].newPotentialController;
		emit ControlTransfer(split, _splits[split].controller, msg.sender);
		_splits[split].controller = msg.sender;
	}

	/// @notice Distributes the ETH balance for split `split`
	/// @dev `accounts`, `percentAllocations`, and `distributorFee` are verified by hashing
	/// & comparing to the hash in storage associated with split `split`
	/// @param split Address of split to distribute balance for
	/// @param accounts Ordered, unique list of addresses with ownership in the split
	/// @param percentAllocations Percent allocations associated with each address
	function distributeETH(
		address split,
		address[] calldata accounts,
		uint32[] calldata percentAllocations
	) external override validSplit(accounts, percentAllocations) {
		// use internal fn instead of modifier to avoid stack depth compiler errors
		_validSplitHash(split, accounts, percentAllocations);
		_convertWETHToUSDCAndDistribute(split, accounts, percentAllocations);
	}

	/// @notice Updates & distributes the ETH balance for split `split`
	/// @dev only callable by SplitController
	/// @param split Address of split to distribute balance for
	/// @param accounts Ordered, unique list of addresses with ownership in the split
	/// @param percentAllocations Percent allocations associated with each address
	function updateAndDistributeETH(
		address split,
		address[] calldata accounts,
		uint32[] calldata percentAllocations
	)
		external
		override
		onlySplitController(split)
		validSplit(accounts, percentAllocations)
	{
		_updateSplit(split, accounts, percentAllocations);
		// know splitHash is valid immediately after updating; only accessible via controller
		_convertWETHToUSDCAndDistribute(split, accounts, percentAllocations);
	}

	/// @notice Distributes the ERC20 `token` balance for split `split`
	/// @dev `accounts`, `percentAllocations`, and `distributorFee` are verified by hashing
	/// & comparing to the hash in storage associated with split `split`
	/// @dev pernicious ERC20s may cause overflow in this function inside
	/// _scaleAmountByPercentage, but results do not affect ETH & other ERC20 balances
	/// @param split Address of split to distribute balance for
	/// @param token Address of ERC20 to distribute balance for
	/// @param accounts Ordered, unique list of addresses with ownership in the split
	/// @param percentAllocations Percent allocations associated with each address
	function distributeERC20(
		address split,
		IERC20 token,
		address[] calldata accounts,
		uint32[] calldata percentAllocations
	) external override validSplit(accounts, percentAllocations) {
		// use internal fn instead of modifier to avoid stack depth compiler errors
		_validSplitHash(split, accounts, percentAllocations);

		if (address(token) == address(USDC)) {
			_distributeERC20(split, token, accounts, percentAllocations);
		} else if (address(token) == address(WETH9)) {
			_convertWETHToUSDCAndDistribute(split, accounts, percentAllocations);
		} else {
			uint256 proxyBalance = token.balanceOf(split);
			SplitWallet(split).sendERC20ToMain(token, proxyBalance);
			_erc20Balances[token][_splits[split].controller] += proxyBalance;
		}
	}

	/// @notice Updates & distributes the ERC20 `token` balance for split `split`
	/// @dev only callable by SplitController
	/// @dev pernicious ERC20s may cause overflow in this function inside
	/// _scaleAmountByPercentage, but results do not affect ETH & other ERC20 balances
	/// @param split Address of split to distribute balance for
	/// @param token Address of ERC20 to distribute balance for
	/// @param accounts Ordered, unique list of addresses with ownership in the split
	/// @param percentAllocations Percent allocations associated with each address
	function updateAndDistributeERC20(
		address split,
		IERC20 token,
		address[] calldata accounts,
		uint32[] calldata percentAllocations
	)
		external
		override
		onlySplitController(split)
		validSplit(accounts, percentAllocations)
	{
		_updateSplit(split, accounts, percentAllocations);
		// know splitHash is valid immediately after updating; only accessible via controller
		_distributeERC20(split, token, accounts, percentAllocations);
	}

	/// @notice Withdraw ETH &/ ERC20 balances for account `account`
	/// @param account Address to withdraw on behalf of
	/// @param withdrawETH Withdraw all ETH if nonzero
	/// @param tokens Addresses of ERC20s to withdraw
	function withdraw(
		address account,
		uint256 withdrawETH,
		IERC20[] calldata tokens
	) external override {
		uint256[] memory tokenAmounts = new uint256[](tokens.length);
		uint256 ethAmount;
		if (withdrawETH != 0) {
			ethAmount = _withdraw(account);
		}
		unchecked {
			// overflow should be impossible in for-loop index
			for (uint256 i = 0; i < tokens.length; ++i) {
				// overflow should be impossible in array length math
				tokenAmounts[i] = _withdrawERC20(account, tokens[i]);
			}
			emit Withdrawal(account, ethAmount, tokens, tokenAmounts);
		}
	}

	/// @notice the owner updates the SonaSwap implementation to `_newSwap`
	/// @param _newSwap the new ISonaSwapImplementation to be utilized
	function updateSwap(ISonaSwap _newSwap) external onlyOwner {
		swap = _newSwap;
	}

	/// FUNCTIONS - VIEWS

	/// @notice Returns the current hash of split `split`
	/// @param split Split to return hash for
	/// @return Split's hash
	function getHash(address split) external view returns (bytes32) {
		return _splits[split].hash;
	}

	/// @notice Returns the current controller of split `split`
	/// @param split Split to return controller for
	/// @return Split's controller
	function getController(address split) external view returns (address) {
		return _splits[split].controller;
	}

	/// @notice Returns the current ETH balance of account `account`
	/// @param account Account to return ETH balance for
	/// @return Account's balance of ETH
	function getETHBalance(address account) external view returns (uint256) {
		return
			_ethBalances[account] +
			(_splits[account].hash != 0 ? account.balance : 0);
	}

	/// @notice Returns the ERC20 balance of token `token` for account `account`
	/// @param account Account to return ERC20 `token` balance for
	/// @param token Token to return balance for
	/// @return Account's balance of `token`
	function getERC20Balance(
		address account,
		IERC20 token
	) external view returns (uint256) {
		return
			_erc20Balances[token][account] +
			(_splits[account].hash != 0 ? token.balanceOf(account) : 0);
	}

	/// FUNCTIONS - PRIVATE & INTERNAL

	/// @notice Sums array of uint32s
	/// @param numbers Array of uint32s to sum
	/// @return sum Sum of `numbers`.
	function _getSum(uint32[] memory numbers) internal pure returns (uint32 sum) {
		// overflow should be impossible in for-loop index
		uint256 numbersLength = numbers.length;
		for (uint256 i = 0; i < numbersLength; ) {
			sum += numbers[i];
			unchecked {
				// overflow should be impossible in for-loop index
				++i;
			}
		}
	}

	/// @notice ensure a split configuration is valid in both contents and organization
	/// @dev reverts if there are fewer than 2 accounts, the arrays are different length,
	/// the allocations don't add up to 1e6, or accounts aren't sorted in the array
	/// @param accounts Ordered, unique list of addresses with ownership in the split
	/// @param percentAllocations Percent allocations associated with each address
	function _validSplit(
		address[] memory accounts,
		uint32[] memory percentAllocations
	) internal pure {
		if (accounts.length < 2)
			revert InvalidSplit__TooFewAccounts(accounts.length);
		if (accounts.length != percentAllocations.length)
			revert InvalidSplit__AccountsAndAllocationsMismatch(
				accounts.length,
				percentAllocations.length
			);
		// _getSum should overflow if any percentAllocation[i] < 0
		if (_getSum(percentAllocations) != PERCENTAGE_SCALE)
			revert InvalidSplit__InvalidAllocationsSum(_getSum(percentAllocations));
		unchecked {
			// overflow should be impossible in for-loop index
			// cache accounts length to save gas
			uint256 loopLength = accounts.length - 1;
			for (uint256 i = 0; i < loopLength; ++i) {
				// overflow should be impossible in array access math
				if (accounts[i] >= accounts[i + 1])
					revert InvalidSplit__AccountsOutOfOrder(i);
				if (percentAllocations[i] == uint32(0))
					revert InvalidSplit__AllocationMustBePositive(i);
			}
			// overflow should be impossible in array access math with validated equal array lengths
			if (percentAllocations[loopLength] == uint32(0))
				revert InvalidSplit__AllocationMustBePositive(loopLength);
		}
	}

	/// @notice Hashes a split
	/// @param accounts Ordered, unique list of addresses with ownership in the split
	/// @param percentAllocations Percent allocations associated with each address
	/// @return computedHash Hash of the split.
	function _hashSplit(
		address[] memory accounts,
		uint32[] memory percentAllocations
	) internal pure returns (bytes32) {
		return keccak256(abi.encodePacked(accounts, percentAllocations));
	}

	/// @notice Updates an existing split with recipients `accounts` with ownerships `percentAllocations` and a keeper fee for splitting of `distributorFee`
	/// @param split Address of mutable split to update
	/// @param accounts Ordered, unique list of addresses with ownership in the split
	/// @param percentAllocations Percent allocations associated with each address
	function _updateSplit(
		address split,
		address[] calldata accounts,
		uint32[] calldata percentAllocations
	) internal {
		bytes32 splitHash = _hashSplit(accounts, percentAllocations);
		// store new hash in storage for future verification
		_splits[split].hash = splitHash;
		emit UpdateSplit(split);
	}

	/// @notice Checks hash from `accounts`, `percentAllocations` against the hash stored for `split`
	/// @param split Address of hash to check
	/// @param accounts Ordered, unique list of addresses with ownership in the split
	/// @param percentAllocations Percent allocations associated with each address
	function _validSplitHash(
		address split,
		address[] memory accounts,
		uint32[] memory percentAllocations
	) internal view {
		bytes32 hash = _hashSplit(accounts, percentAllocations);
		if (_splits[split].hash != hash) revert InvalidSplit__InvalidHash(hash);
	}

	/// @notice Distributes the ETH balance for split `split`
	/// @dev `accounts`, `percentAllocations`, and `distributorFee` must be verified before calling
	/// @param split Address of split to distribute balance for
	/// @param accounts Ordered, unique list of addresses with ownership in the split
	/// @param percentAllocations Percent allocations associated with each address
	function _distributeETH(
		address split,
		address[] memory accounts,
		uint32[] memory percentAllocations
	) internal {
		uint256 mainBalance = _ethBalances[split];
		uint256 proxyBalance = split.balance;
		// if mainBalance is positive, leave 1 in SplitMain for gas efficiency
		uint256 amountToSplit;
		unchecked {
			// underflow should be impossible
			if (mainBalance > 0) mainBalance -= 1;
			// overflow should be impossible
			amountToSplit = mainBalance + proxyBalance;
		}
		if (mainBalance > 0) _ethBalances[split] = 1;
		// emit event with gross amountToSplit
		emit DistributeETH(split, amountToSplit);

		// flush proxy ETH balance to SplitMain
		// split proxy should be guaranteed to exist at this address after validating splitHash
		// (attacker can't deploy own contract to address with high balance & empty sendETHToMain
		// to drain ETH from SplitMain)
		// could technically check if (change in proxy balance == change in SplitMain balance)
		// before/after external call, but seems like extra gas for no practical benefit

		if (proxyBalance > 0) SplitWallet(split).sendETHToMain(proxyBalance);
		unchecked {
			// distribute remaining balance
			// overflow should be impossible in for-loop index
			// cache accounts length to save gas
			uint256 accountsLength = accounts.length;
			for (uint256 i = 0; i < accountsLength; ++i) {
				uint256 balance = _scaleAmountByPercentage(
					amountToSplit,
					percentAllocations[i]
				);

				if (!_trySendingETH(accounts[i], balance)) {
					// overflow should be impossible with validated allocations
					_ethBalances[accounts[i]] += balance;
				}
			}
		}
	}

	function _convertWETHToUSDCAndDistribute(
		address split,
		address[] memory accounts,
		uint32[] memory percentAllocations
	) internal returns (uint256 usdcToSplit) {
		uint256 wethToSwap;
		uint256 mainBalance = _erc20Balances[WETH9][split];
		uint256 mainETHBalance = _ethBalances[split];
		uint256 proxyBalance = WETH9.balanceOf(split);
		uint256 proxyETHBalance = split.balance;
		unchecked {
			// if mainBalance &/ proxyBalance are positive, leave 1 for gas efficiency
			// underflow should be impossible
			if (proxyBalance > 0) proxyBalance -= 1;
			// underflow should be impossible
			if (mainBalance > 0) {
				mainBalance -= 1;
			}
			if (mainETHBalance > 0) {
				mainETHBalance -= 1;
			}
			// overflow should be impossible
			wethToSwap =
				mainBalance +
				mainETHBalance +
				proxyBalance +
				proxyETHBalance;
			// split proxy should be guaranteed to exist at this address after validating splitHash
			// (attacker can't deploy own contract to address with high ERC20 balance & empty
			// sendERC20ToMain to drain ERC20 from SplitMain)
			// doesn't support rebasing or fee-on-transfer tokens
			// flush extra proxy ERC20 balance to SplitMain
			if (proxyBalance > 0)
				SplitWallet(split).sendERC20ToMain(WETH9, proxyBalance);

			if (proxyETHBalance > 0) {
				SplitWallet(split).sendETHToMain(proxyETHBalance);
				WETH9.deposit{ value: proxyETHBalance }();
			}

			if (mainETHBalance > 0) {
				WETH9.deposit{ value: mainETHBalance }();
			}
		}

		WETH9.approve(address(swap), wethToSwap);
		usdcToSplit = swap.swapWEthForUSDC(wethToSwap);
		unchecked {
			// cache accounts length to save gas
			uint256 accountsLength = accounts.length;
			for (uint256 i = 0; i < accountsLength; ++i) {
				uint256 balance = _scaleAmountByPercentage(
					usdcToSplit,
					percentAllocations[i]
				);

				if (!_trySendingERC20(USDC, accounts[i], balance))
					_erc20Balances[USDC][accounts[i]] += balance;
			}
		}
	}

	function _trySendingETH(
		address account,
		uint256 amount
	) internal returns (bool success) {
		// solhint-disable-next-line check-send-result
		return payable(account).send(amount);
	}

	/// @notice Distributes the ERC20 `token` balance for split `split`
	/// @dev `accounts`, `percentAllocations`, and `distributorFee` must be verified before calling
	/// @dev pernicious ERC20s may cause overflow in this function inside
	/// _scaleAmountByPercentage, but results do not affect ETH & other ERC20 balances
	/// @param split Address of split to distribute balance for
	/// @param token Address of ERC20 to distribute balance for
	/// @param accounts Ordered, unique list of addresses with ownership in the split
	/// @param percentAllocations Percent allocations associated with each address
	function _distributeERC20(
		address split,
		IERC20 token,
		address[] memory accounts,
		uint32[] memory percentAllocations
	) internal {
		uint256 amountToSplit;
		uint256 mainBalance = _erc20Balances[token][split];
		uint256 proxyBalance = token.balanceOf(split);
		unchecked {
			// if mainBalance &/ proxyBalance are positive, leave 1 for gas efficiency
			// underflow should be impossible
			if (proxyBalance > 0) proxyBalance -= 1;
			// underflow should be impossible
			if (mainBalance > 0) {
				mainBalance -= 1;
			}
			// overflow should be impossible
			amountToSplit = mainBalance + proxyBalance;
		}
		if (mainBalance > 0) _erc20Balances[token][split] = 1;
		// emit event with gross amountToSplit (before deducting distributorFee)
		emit DistributeERC20(split, token, amountToSplit);

		// split proxy should be guaranteed to exist at this address after validating splitHash
		// (attacker can't deploy own contract to address with high ERC20 balance & empty
		// sendERC20ToMain to drain ERC20 from SplitMain)
		// doesn't support rebasing or fee-on-transfer tokens
		// flush extra proxy ERC20 balance to SplitMain
		if (proxyBalance > 0)
			SplitWallet(split).sendERC20ToMain(token, proxyBalance);

		// distribute remaining balance
		// overflows should be impossible in for-loop with validated allocations
		unchecked {
			// cache accounts length to save gas
			uint256 accountsLength = accounts.length;
			for (uint256 i = 0; i < accountsLength; ++i) {
				uint256 balance = _scaleAmountByPercentage(
					amountToSplit,
					percentAllocations[i]
				);

				if (!_trySendingERC20(token, accounts[i], balance))
					_erc20Balances[token][accounts[i]] += balance;
			}
		}
	}

	function _trySendingERC20(
		IERC20 token,
		address account,
		uint256 amount
	) internal returns (bool success) {
		// TODO for and modify the solady `safeTransfer` function to return false
		// when the receiver contract doesn't have the right interface
		return token.transfer(account, amount);
	}

	/// @notice Multiplies an amount by a scaled percentage
	/// @param amount Amount to get `scaledPercentage` of
	/// @param scaledPercent Percent scaled by PERCENTAGE_SCALE
	/// @return scaledAmount Percent of `amount`.
	function _scaleAmountByPercentage(
		uint256 amount,
		uint256 scaledPercent
	) internal pure returns (uint256 scaledAmount) {
		// use assembly to bypass checking for overflow & division by 0
		// scaledPercent has been validated to be < PERCENTAGE_SCALE)
		// & PERCENTAGE_SCALE will never be 0
		// pernicious ERC20s may cause overflow, but results do not affect ETH & other ERC20 balances

		// solhint-disable-next-line no-inline-assembly
		assembly ("memory-safe") {
			/* eg (100/// 2*1e4) / (1e6) */
			scaledAmount := div(mul(amount, scaledPercent), PERCENTAGE_SCALE)
		}
	}

	/// @notice Withdraw ETH for account `account`
	/// @param account Account to withdrawn ETH for
	/// @return withdrawn Amount of ETH withdrawn
	function _withdraw(address account) internal returns (uint256 withdrawn) {
		// leave balance of 1 for gas efficiency
		// underflow if ethBalance is 0
		withdrawn = _ethBalances[account] - 1;
		_ethBalances[account] = 1;
		account.safeTransferETH(withdrawn);
	}

	/// @notice Withdraw ERC20 `token` for account `account`
	/// @param account Account to withdrawn ERC20 `token` for
	/// @return withdrawn Amount of ERC20 `token` withdrawn
	function _withdrawERC20(
		address account,
		IERC20 token
	) internal returns (uint256 withdrawn) {
		// leave balance of 1 for gas efficiency
		// underflow if erc20Balance is 0
		withdrawn = _erc20Balances[token][account] - 1;
		_erc20Balances[token][account] = 1;
		address(token).safeTransfer(account, withdrawn);
	}
}

File 2 of 10 : ISplitMain.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.16;

import { IERC20Upgradeable as IERC20 } from "openzeppelin-upgradeable/token/ERC20/IERC20Upgradeable.sol";

/// @title ISplitMain
/// @author 0xSplits <[email protected]>
interface ISplitMain {
	/// Structs
	struct SplitInput {
		address[] accounts;
		uint32[] percentAllocations;
	}

	/// FUNCTIONS

	function walletImplementation() external returns (address);

	function createSplit(
		address[] calldata accounts,
		uint32[] calldata percentAllocations
	) external returns (address);

	function updateSplit(
		address split,
		address[] calldata accounts,
		uint32[] calldata percentAllocations
	) external;

	function distributeETH(
		address split,
		address[] calldata accounts,
		uint32[] calldata percentAllocations
	) external;

	function updateAndDistributeETH(
		address split,
		address[] calldata accounts,
		uint32[] calldata percentAllocations
	) external;

	function distributeERC20(
		address split,
		IERC20 token,
		address[] calldata accounts,
		uint32[] calldata percentAllocations
	) external;

	function updateAndDistributeERC20(
		address split,
		IERC20 token,
		address[] calldata accounts,
		uint32[] calldata percentAllocations
	) external;

	function withdraw(
		address account,
		uint256 withdrawETH,
		IERC20[] calldata tokens
	) external;

	function createSplits(
		SplitInput[] calldata splits
	) external returns (address[] memory split);

	function transferControl(address split, address newController) external;

	function cancelControlTransfer(address split) external;

	function acceptControl(address split) external;

	/// EVENTS

	/// @notice emitted after each successful split creation
	/// @param split Address of the created split
	event CreateSplit(address indexed split);

	/// @notice emitted after each successful split update
	/// @param split Address of the updated split
	event UpdateSplit(address indexed split);

	/// @notice emitted after each initiated split control transfer
	/// @param split Address of the split control transfer was initiated for
	/// @param newPotentialController Address of the split's new potential controller
	event InitiateControlTransfer(
		address indexed split,
		address indexed newPotentialController
	);

	/// @notice emitted after each canceled split control transfer
	/// @param split Address of the split control transfer was canceled for
	event CancelControlTransfer(address indexed split);

	/// @notice emitted after each successful split control transfer
	/// @param split Address of the split control was transferred for
	/// @param previousController Address of the split's previous controller
	/// @param newController Address of the split's new controller
	event ControlTransfer(
		address indexed split,
		address indexed previousController,
		address indexed newController
	);

	/// @notice emitted after each successful ETH balance split
	/// @param split Address of the split that distributed its balance
	/// @param amount Amount of ETH distributed
	event DistributeETH(address indexed split, uint256 amount);

	/// @notice emitted after each successful ERC20 balance split
	/// @param split Address of the split that distributed its balance
	/// @param token Address of ERC20 distributed
	/// @param amount Amount of ERC20 distributed
	event DistributeERC20(
		address indexed split,
		IERC20 indexed token,
		uint256 amount
	);

	/// @notice emitted after each successful withdrawal
	/// @param account Address that funds were withdrawn to
	/// @param ethAmount Amount of ETH withdrawn
	/// @param tokens Addresses of ERC20s withdrawn
	/// @param tokenAmounts Amounts of corresponding ERC20s withdrawn
	event Withdrawal(
		address indexed account,
		uint256 ethAmount,
		IERC20[] tokens,
		uint256[] tokenAmounts
	);
}

File 3 of 10 : SplitWallet.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.16;

import { ISplitMain } from "./interfaces/ISplitMain.sol";
import { IERC20Upgradeable as IERC20 } from "openzeppelin-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol";

///ERRORS

/// @notice Unauthorized sender
error Unauthorized();

///
///@title SplitWallet
///@author 0xSplits <[email protected]>
///@notice The implementation logic for `SplitProxy`.
///@dev `SplitProxy` handles `receive()` itself to avoid the gas cost with `DELEGATECALL`.
contract SplitWallet {
	using SafeTransferLib for address;

	///EVENTS

	/// @notice emitted after each successful ETH transfer to proxy
	/// @param split Address of the split that received ETH
	/// @param amount Amount of ETH received
	event ReceiveETH(address indexed split, uint256 amount);

	///STORAGE

	///STORAGE - CONSTANTS & IMMUTABLES

	/// @notice address of SplitMain for split distributions & EOA/SC withdrawals
	ISplitMain public immutable splitMain;

	///MODIFIERS

	/// @notice Reverts if the sender isn't SplitMain
	modifier onlySplitMain() {
		if (msg.sender != address(splitMain)) revert Unauthorized();
		_;
	}

	///CONSTRUCTOR

	constructor() {
		splitMain = ISplitMain(msg.sender);
	}

	///FUNCTIONS - PUBLIC & EXTERNAL

	/// @notice Sends amount `amount` of ETH in proxy to SplitMain
	/// @dev payable reduces gas cost; no vulnerability to accidentally lock
	/// ETH introduced since fn call is restricted to SplitMain
	/// @param amount Amount to send
	function sendETHToMain(uint256 amount) external payable onlySplitMain {
		address(splitMain).safeTransferETH(amount);
	}

	/// @notice Sends amount `amount` of ERC20 `token` in proxy to SplitMain
	/// @dev payable reduces gas cost; no vulnerability to accidentally lock
	/// ETH introduced since fn call is restricted to SplitMain
	/// @param token Token to send
	/// @param amount Amount to send
	function sendERC20ToMain(
		IERC20 token,
		uint256 amount
	) external payable onlySplitMain {
		address(token).safeTransfer(address(splitMain), amount);
	}
}

File 4 of 10 : Clones.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.16;

/// @notice create opcode failed
error CreateError();
/// @notice create2 opcode failed
error Create2Error();

// solhint-disable no-inline-assembly

/// @author 0xSplits <[email protected]>
library Clones {
	/**
	 * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`
	 * except when someone calls `receive()` and then it emits an event matching
	 * `SplitWallet.ReceiveETH(indexed address, amount)`
	 * Inspired by OZ & 0age's minimal clone implementations based on eip 1167 found at
	 * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.3.0/contracts/proxy/Clones.sol
	 * and https://medium.com/coinmonks/the-more-minimal-proxy-5756ae08ee48
	 *
	 * This function uses the create2 opcode and a `salt` to deterministically deploy
	 * the clone. Using the same `implementation` and `salt` multiple time will revert, since
	 * the clones cannot be deployed twice at the same address.
	 *
	 * init: 0x3d605d80600a3d3981f3
	 * 3d   returndatasize  0
	 * 605d push1 0x5d      0x5d 0
	 * 80   dup1            0x5d 0x5d 0
	 * 600a push1 0x0a      0x0a 0x5d 0x5d 0
	 * 3d   returndatasize  0 0x0a 0x5d 0x5d 0
	 * 39   codecopy        0x5d 0                    destOffset offset length     memory[destOffset:destOffset+length] = address(this).code[offset:offset+length]       copy executing contracts bytecode
	 * 81   dup2            0 0x5d 0
	 * f3   return          0                         offset length                return memory[offset:offset+length]                                                   returns from this contract call
	 *
	 * code: 0x36603057343d52307f830d2d700a97af574b186c80d40429385d24241565b08a7c559ba283a964d9b160203da23d3df35b3d3d3d3d363d3d37363d73bebebebebebebebebebebebebebebebebebebebe5af43d3d93803e605b57fd5bf3
	 *     0x000     36       calldatasize      cds
	 *     0x001     6030     push1 0x30        0x30 cds
	 * ,=< 0x003     57       jumpi
	 * |   0x004     34       callvalue         cv
	 * |   0x005     3d       returndatasize    0 cv
	 * |   0x006     52       mstore
	 * |   0x007     30       address           addr
	 * |   0x008     7f830d.. push32 0x830d..   id addr
	 * |   0x029     6020     push1 0x20        0x20 id addr
	 * |   0x02b     3d       returndatasize    0 0x20 id addr
	 * |   0x02c     a2       log2
	 * |   0x02d     3d       returndatasize    0
	 * |   0x02e     3d       returndatasize    0 0
	 * |   0x02f     f3       return
	 * `-> 0x030     5b       jumpdest
	 *     0x031     3d       returndatasize    0
	 *     0x032     3d       returndatasize    0 0
	 *     0x033     3d       returndatasize    0 0 0
	 *     0x034     3d       returndatasize    0 0 0 0
	 *     0x035     36       calldatasize      cds 0 0 0 0
	 *     0x036     3d       returndatasize    0 cds 0 0 0 0
	 *     0x037     3d       returndatasize    0 0 cds 0 0 0 0
	 *     0x038     37       calldatacopy      0 0 0 0
	 *     0x039     36       calldatasize      cds 0 0 0 0
	 *     0x03a     3d       returndatasize    0 cds 0 0 0 0
	 *     0x03b     73bebe.. push20 0xbebe..   0xbebe 0 cds 0 0 0 0
	 *     0x050     5a       gas               gas 0xbebe 0 cds 0 0 0 0
	 *     0x051     f4       delegatecall      suc 0 0
	 *     0x052     3d       returndatasize    rds suc 0 0
	 *     0x053     3d       returndatasize    rds rds suc 0 0
	 *     0x054     93       swap4             0 rds suc 0 rds
	 *     0x055     80       dup1              0 0 rds suc 0 rds
	 *     0x056     3e       returndatacopy    suc 0 rds
	 *     0x057     605b     push1 0x5b        0x5b suc 0 rds
	 * ,=< 0x059     57       jumpi             0 rds
	 * |   0x05a     fd       revert
	 * `-> 0x05b     5b       jumpdest          0 rds
	 *     0x05c     f3       return
	 *
	 */
	function clone(address implementation) internal returns (address instance) {
		assembly ("memory-safe") {
			let ptr := mload(0x40)
			mstore(
				ptr,
				0x3d605d80600a3d3981f336603057343d52307f00000000000000000000000000
			)
			mstore(
				add(ptr, 0x13),
				0x830d2d700a97af574b186c80d40429385d24241565b08a7c559ba283a964d9b1
			)
			mstore(
				add(ptr, 0x33),
				0x60203da23d3df35b3d3d3d3d363d3d37363d7300000000000000000000000000
			)
			mstore(add(ptr, 0x46), shl(0x60, implementation))
			mstore(
				add(ptr, 0x5a),
				0x5af43d3d93803e605b57fd5bf300000000000000000000000000000000000000
			)
			instance := create(0, ptr, 0x67)
		}
		if (instance == address(0)) revert CreateError();
	}

	function cloneDeterministic(
		address implementation,
		bytes32 salt
	) internal returns (address instance) {
		assembly ("memory-safe") {
			let ptr := mload(0x40)
			mstore(
				ptr,
				0x3d605d80600a3d3981f336603057343d52307f00000000000000000000000000
			)
			mstore(
				add(ptr, 0x13),
				0x830d2d700a97af574b186c80d40429385d24241565b08a7c559ba283a964d9b1
			)
			mstore(
				add(ptr, 0x33),
				0x60203da23d3df35b3d3d3d3d363d3d37363d7300000000000000000000000000
			)
			mstore(add(ptr, 0x46), shl(0x60, implementation))
			mstore(
				add(ptr, 0x5a),
				0x5af43d3d93803e605b57fd5bf300000000000000000000000000000000000000
			)
			instance := create2(0, ptr, 0x67, salt)
		}
		if (instance == address(0)) revert Create2Error();
	}

	/**
	 * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
	 */
	function predictDeterministicAddress(
		address implementation,
		bytes32 salt,
		address deployer
	) internal pure returns (address predicted) {
		assembly ("memory-safe") {
			let ptr := mload(0x40)
			mstore(
				ptr,
				0x3d605d80600a3d3981f336603057343d52307f00000000000000000000000000
			)
			mstore(
				add(ptr, 0x13),
				0x830d2d700a97af574b186c80d40429385d24241565b08a7c559ba283a964d9b1
			)
			mstore(
				add(ptr, 0x33),
				0x60203da23d3df35b3d3d3d3d363d3d37363d7300000000000000000000000000
			)
			mstore(add(ptr, 0x46), shl(0x60, implementation))
			mstore(
				add(ptr, 0x5a),
				0x5af43d3d93803e605b57fd5bf3ff000000000000000000000000000000000000
			)
			mstore(add(ptr, 0x68), shl(0x60, deployer))
			mstore(add(ptr, 0x7c), salt)
			mstore(add(ptr, 0x9c), keccak256(ptr, 0x67))
			predicted := keccak256(add(ptr, 0x67), 0x55)
		}
	}

	/**
	 * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
	 */
	function predictDeterministicAddress(
		address implementation,
		bytes32 salt
	) internal view returns (address predicted) {
		return predictDeterministicAddress(implementation, salt, address(this));
	}
}

File 5 of 10 : IERC20Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20Upgradeable {
    /**
     * @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);
}

File 6 of 10 : SafeTransferLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Caution! This library won't check that a token has code, responsibility is delegated to the caller.
library SafeTransferLib {
    /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/

    /// @dev The ETH transfer has failed.
    error ETHTransferFailed();

    /// @dev The ERC20 `transferFrom` has failed.
    error TransferFromFailed();

    /// @dev The ERC20 `transfer` has failed.
    error TransferFailed();

    /// @dev The ERC20 `approve` has failed.
    error ApproveFailed();

    /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/

    /// @dev Suggested gas stipend for contract receiving ETH
    /// that disallows any storage writes.
    uint256 internal constant _GAS_STIPEND_NO_STORAGE_WRITES = 2300;

    /// @dev Suggested gas stipend for contract receiving ETH to perform a few
    /// storage reads and writes, but low enough to prevent griefing.
    /// Multiply by a small constant (e.g. 2), if needed.
    uint256 internal constant _GAS_STIPEND_NO_GRIEF = 100000;

    /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
    /*                       ETH OPERATIONS                       */
    /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/

    /// @dev Sends `amount` (in wei) ETH to `to`.
    /// Reverts upon failure.
    function safeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and check if it succeeded or not.
            if iszero(call(gas(), to, amount, 0, 0, 0, 0)) {
                // Store the function selector of `ETHTransferFailed()`.
                mstore(0x00, 0xb12d13eb)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    /// The `gasStipend` can be set to a low enough value to prevent
    /// storage writes or gas griefing.
    ///
    /// If sending via the normal procedure fails, force sends the ETH by
    /// creating a temporary contract which uses `SELFDESTRUCT` to force send the ETH.
    ///
    /// Reverts if the current contract has insufficient balance.
    function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // If insufficient balance, revert.
            if lt(selfbalance(), amount) {
                // Store the function selector of `ETHTransferFailed()`.
                mstore(0x00, 0xb12d13eb)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            // Transfer the ETH and check if it succeeded or not.
            if iszero(call(gasStipend, to, amount, 0, 0, 0, 0)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                // We can directly use `SELFDESTRUCT` in the contract creation.
                // Compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758
                pop(create(amount, 0x0b, 0x16))
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with a gas stipend
    /// equal to `_GAS_STIPEND_NO_GRIEF`. This gas stipend is a reasonable default
    /// for 99% of cases and can be overriden with the three-argument version of this
    /// function if necessary.
    ///
    /// If sending via the normal procedure fails, force sends the ETH by
    /// creating a temporary contract which uses `SELFDESTRUCT` to force send the ETH.
    ///
    /// Reverts if the current contract has insufficient balance.
    function forceSafeTransferETH(address to, uint256 amount) internal {
        // Manually inlined because the compiler doesn't inline functions with branches.
        /// @solidity memory-safe-assembly
        assembly {
            // If insufficient balance, revert.
            if lt(selfbalance(), amount) {
                // Store the function selector of `ETHTransferFailed()`.
                mstore(0x00, 0xb12d13eb)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            // Transfer the ETH and check if it succeeded or not.
            if iszero(call(_GAS_STIPEND_NO_GRIEF, to, amount, 0, 0, 0, 0)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                // We can directly use `SELFDESTRUCT` in the contract creation.
                // Compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758
                pop(create(amount, 0x0b, 0x16))
            }
        }
    }

    /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    /// The `gasStipend` can be set to a low enough value to prevent
    /// storage writes or gas griefing.
    ///
    /// Simply use `gasleft()` for `gasStipend` if you don't need a gas stipend.
    ///
    /// Note: Does NOT revert upon failure.
    /// Returns whether the transfer of ETH is successful instead.
    function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and check if it succeeded or not.
            success := call(gasStipend, to, amount, 0, 0, 0, 0)
        }
    }

    /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
    /*                      ERC20 OPERATIONS                      */
    /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for
    /// the current contract to manage.
    function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.

            // Store the function selector of `transferFrom(address,address,uint256)`.
            mstore(0x00, 0x23b872dd)
            mstore(0x20, from) // Store the `from` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x60, amount) // Store the `amount` argument.

            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(eq(mload(0x00), 1), iszero(returndatasize())),
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                // Store the function selector of `TransferFromFailed()`.
                mstore(0x00, 0x7939f424)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends all of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for
    /// the current contract to manage.
    function safeTransferAllFrom(address token, address from, address to)
        internal
        returns (uint256 amount)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.

            mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
            mstore(0x20, from) // Store the `from` argument.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
                )
            ) {
                // Store the function selector of `TransferFromFailed()`.
                mstore(0x00, 0x7939f424)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

            // Store the function selector of `transferFrom(address,address,uint256)`.
            mstore(0x00, 0x23b872dd)
            mstore(0x40, to) // Store the `to` argument.
            // The `amount` argument is already written to the memory word at 0x6a.
            amount := mload(0x60)

            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(eq(mload(0x00), 1), iszero(returndatasize())),
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                // Store the function selector of `TransferFromFailed()`.
                mstore(0x00, 0x7939f424)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransfer(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x1a, to) // Store the `to` argument.
            mstore(0x3a, amount) // Store the `amount` argument.
            // Store the function selector of `transfer(address,uint256)`,
            // left by 6 bytes (enough for 8tb of memory represented by the free memory pointer).
            // We waste 6-3 = 3 bytes to save on 6 runtime gas (PUSH1 0x224 SHL).
            mstore(0x00, 0xa9059cbb000000000000)

            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(eq(mload(0x00), 1), iszero(returndatasize())),
                    call(gas(), token, 0, 0x16, 0x44, 0x00, 0x20)
                )
            ) {
                // Store the function selector of `TransferFailed()`.
                mstore(0x00, 0x90b8ec18)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            // Restore the part of the free memory pointer that was overwritten,
            // which is guaranteed to be zero, if less than 8tb of memory is used.
            mstore(0x3a, 0)
        }
    }

    /// @dev Sends all of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransferAll(address token, address to) internal returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
            mstore(0x20, address()) // Store the address of the current contract.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x3a, 0x20)
                )
            ) {
                // Store the function selector of `TransferFailed()`.
                mstore(0x00, 0x90b8ec18)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

            mstore(0x1a, to) // Store the `to` argument.
            // The `amount` argument is already written to the memory word at 0x3a.
            amount := mload(0x3a)
            // Store the function selector of `transfer(address,uint256)`,
            // left by 6 bytes (enough for 8tb of memory represented by the free memory pointer).
            // We waste 6-3 = 3 bytes to save on 6 runtime gas (PUSH1 0x224 SHL).
            mstore(0x00, 0xa9059cbb000000000000)

            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(eq(mload(0x00), 1), iszero(returndatasize())),
                    call(gas(), token, 0, 0x16, 0x44, 0x00, 0x20)
                )
            ) {
                // Store the function selector of `TransferFailed()`.
                mstore(0x00, 0x90b8ec18)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            // Restore the part of the free memory pointer that was overwritten,
            // which is guaranteed to be zero, if less than 8tb of memory is used.
            mstore(0x3a, 0)
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// Reverts upon failure.
    function safeApprove(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x1a, to) // Store the `to` argument.
            mstore(0x3a, amount) // Store the `amount` argument.
            // Store the function selector of `approve(address,uint256)`,
            // left by 6 bytes (enough for 8tb of memory represented by the free memory pointer).
            // We waste 6-3 = 3 bytes to save on 6 runtime gas (PUSH1 0x224 SHL).
            mstore(0x00, 0x095ea7b3000000000000)

            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(eq(mload(0x00), 1), iszero(returndatasize())),
                    call(gas(), token, 0, 0x16, 0x44, 0x00, 0x20)
                )
            ) {
                // Store the function selector of `ApproveFailed()`.
                mstore(0x00, 0x3e3f8f73)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            // Restore the part of the free memory pointer that was overwritten,
            // which is guaranteed to be zero, if less than 8tb of memory is used.
            mstore(0x3a, 0)
        }
    }

    /// @dev Returns the amount of ERC20 `token` owned by `account`.
    /// Returns zero if the `token` does not exist.
    function balanceOf(address token, address account) internal view returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
            mstore(0x20, account) // Store the `account` argument.
            amount :=
                mul(
                    mload(0x20),
                    and( // The arguments of `and` are evaluated from right to left.
                        gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                        staticcall(gas(), token, 0x1c, 0x24, 0x20, 0x20)
                    )
                )
        }
    }
}

File 7 of 10 : IWETH.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.16;

import { IERC20Upgradeable as IERC20 } from "openzeppelin-upgradeable/interfaces/IERC20Upgradeable.sol";

interface IWETH is IERC20 {
	event Deposit(address indexed dst, uint wad);
	event Withdrawal(address indexed src, uint wad);

	function deposit() external payable;

	function withdraw(uint wad) external;
}

File 8 of 10 : ISonaSwap.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.6;

interface ISonaSwap {
	function getUsdEthPrice() external view returns (uint256 price);

	function swapWEthForUSDC(uint256 _amount) external returns (uint256 amountOut);

	function swapEthForUSDC() external payable returns (uint256 amountOut);

	function getQuote(
		uint256 _amount,
		uint256 _rate
	) external pure returns (uint256 minimumAmount) ;

	function getQuote(
		uint256 _amount
	) external view returns (uint256 minimumAmount);
}

File 9 of 10 : Ownable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
/// @dev While the ownable portion follows [EIP-173](https://eips.ethereum.org/EIPS/eip-173)
/// for compatibility, the nomenclature for the 2-step ownership handover
/// may be unique to this codebase.
abstract contract Ownable {
    /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/

    /// @dev The caller is not authorized to call the function.
    error Unauthorized();

    /// @dev The `newOwner` cannot be the zero address.
    error NewOwnerIsZeroAddress();

    /// @dev The `pendingOwner` does not have a valid handover request.
    error NoHandoverRequest();

    /// @dev `bytes4(keccak256(bytes("Unauthorized()")))`.
    uint256 private constant _UNAUTHORIZED_ERROR_SELECTOR = 0x82b42900;

    /// @dev `bytes4(keccak256(bytes("NewOwnerIsZeroAddress()")))`.
    uint256 private constant _NEW_OWNER_IS_ZERO_ADDRESS_ERROR_SELECTOR = 0x7448fbae;

    /// @dev `bytes4(keccak256(bytes("NoHandoverRequest()")))`.
    uint256 private constant _NO_HANDOVER_REQUEST_ERROR_SELECTOR = 0x6f5e8818;

    /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/

    /// @dev The ownership is transferred from `oldOwner` to `newOwner`.
    /// This event is intentionally kept the same as OpenZeppelin's Ownable to be
    /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
    /// despite it not being as lightweight as a single argument event.
    event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);

    /// @dev An ownership handover to `pendingOwner` has been requested.
    event OwnershipHandoverRequested(address indexed pendingOwner);

    /// @dev The ownership handover to `pendingOwner` has been canceled.
    event OwnershipHandoverCanceled(address indexed pendingOwner);

    /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
    uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
        0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;

    /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
        0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;

    /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
        0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;

    /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/

    /// @dev The owner slot is given by: `not(_OWNER_SLOT_NOT)`.
    /// It is intentionally choosen to be a high value
    /// to avoid collision with lower slots.
    /// The choice of manual storage layout is to enable compatibility
    /// with both regular and upgradeable contracts.
    uint256 private constant _OWNER_SLOT_NOT = 0x8b78c6d8;

    /// The ownership handover slot of `newOwner` is given by:
    /// ```
    ///     mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
    ///     let handoverSlot := keccak256(0x00, 0x20)
    /// ```
    /// It stores the expiry timestamp of the two-step ownership handover.
    uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;

    /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
    /*                     INTERNAL FUNCTIONS                     */
    /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/

    /// @dev Initializes the owner directly without authorization guard.
    /// This function must be called upon initialization,
    /// regardless of whether the contract is upgradeable or not.
    /// This is to enable generalization to both regular and upgradeable contracts,
    /// and to save gas in case the initial owner is not the caller.
    /// For performance reasons, this function will not check if there
    /// is an existing owner.
    function _initializeOwner(address newOwner) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Clean the upper 96 bits.
            newOwner := shr(96, shl(96, newOwner))
            // Store the new value.
            sstore(not(_OWNER_SLOT_NOT), newOwner)
            // Emit the {OwnershipTransferred} event.
            log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
        }
    }

    /// @dev Sets the owner directly without authorization guard.
    function _setOwner(address newOwner) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            let ownerSlot := not(_OWNER_SLOT_NOT)
            // Clean the upper 96 bits.
            newOwner := shr(96, shl(96, newOwner))
            // Emit the {OwnershipTransferred} event.
            log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
            // Store the new value.
            sstore(ownerSlot, newOwner)
        }
    }

    /// @dev Throws if the sender is not the owner.
    function _checkOwner() internal view virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // If the caller is not the stored owner, revert.
            if iszero(eq(caller(), sload(not(_OWNER_SLOT_NOT)))) {
                mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR)
                revert(0x1c, 0x04)
            }
        }
    }

    /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
    /*                  PUBLIC UPDATE FUNCTIONS                   */
    /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/

    /// @dev Allows the owner to transfer the ownership to `newOwner`.
    function transferOwnership(address newOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(shl(96, newOwner)) {
                mstore(0x00, _NEW_OWNER_IS_ZERO_ADDRESS_ERROR_SELECTOR)
                revert(0x1c, 0x04)
            }
        }
        _setOwner(newOwner);
    }

    /// @dev Allows the owner to renounce their ownership.
    function renounceOwnership() public payable virtual onlyOwner {
        _setOwner(address(0));
    }

    /// @dev Request a two-step ownership handover to the caller.
    /// The request will be automatically expire in 48 hours (172800 seconds) by default.
    function requestOwnershipHandover() public payable virtual {
        unchecked {
            uint256 expires = block.timestamp + ownershipHandoverValidFor();
            /// @solidity memory-safe-assembly
            assembly {
                // Compute and set the handover slot to `expires`.
                mstore(0x0c, _HANDOVER_SLOT_SEED)
                mstore(0x00, caller())
                sstore(keccak256(0x0c, 0x20), expires)
                // Emit the {OwnershipHandoverRequested} event.
                log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
            }
        }
    }

    /// @dev Cancels the two-step ownership handover to the caller, if any.
    function cancelOwnershipHandover() public payable virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x20), 0)
            // Emit the {OwnershipHandoverCanceled} event.
            log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
        }
    }

    /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
    /// Reverts if there is no existing ownership handover requested by `pendingOwner`.
    function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            let handoverSlot := keccak256(0x0c, 0x20)
            // If the handover does not exist, or has expired.
            if gt(timestamp(), sload(handoverSlot)) {
                mstore(0x00, _NO_HANDOVER_REQUEST_ERROR_SELECTOR)
                revert(0x1c, 0x04)
            }
            // Set the handover slot to 0.
            sstore(handoverSlot, 0)
        }
        _setOwner(pendingOwner);
    }

    /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
    /*                   PUBLIC READ FUNCTIONS                    */
    /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/

    /// @dev Returns the owner of the contract.
    function owner() public view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := sload(not(_OWNER_SLOT_NOT))
        }
    }

    /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
    function ownershipHandoverExpiresAt(address pendingOwner)
        public
        view
        virtual
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the handover slot.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            // Load the handover slot.
            result := sload(keccak256(0x0c, 0x20))
        }
    }

    /// @dev Returns how long a two-step ownership handover is valid for in seconds.
    function ownershipHandoverValidFor() public view virtual returns (uint64) {
        return 48 * 3600;
    }

    /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
    /*                         MODIFIERS                          */
    /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/

    /// @dev Marks a function as only callable by the owner.
    modifier onlyOwner() virtual {
        _checkOwner();
        _;
    }
}

File 10 of 10 : IERC20Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC20.sol)

pragma solidity ^0.8.0;

import "../token/ERC20/IERC20Upgradeable.sol";

Settings
{
  "remappings": [
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin/=lib/openzeppelin-contracts/contracts/",
    "@openzeppelin/=lib_v7/v3-periphery/node_modules/@openzeppelin/",
    "erc721a-upgradeable/=lib/ERC721A-Upgradeable/contracts/",
    "openzeppelin-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "solady/=lib/solady/src/",
    "closedsea/=lib/closedsea/src/",
    "solmate/=lib/solmate/src/",
    "murky/=lib/murky/src/",
    "ERC721A-Upgradeable/=lib/ERC721A-Upgradeable/contracts/",
    "chainlink/=lib/chainlink/",
    "common/=lib/common/",
    "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
    "erc721a/=lib/closedsea/lib/erc721a/contracts/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "operator-filter-registry/=lib/closedsea/lib/operator-filter-registry/",
    "v3-core/=lib/v3-core/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "libraries": {
    "contracts/utils/AddressableTokenId.sol": {
      "AddressableTokenId": "0xe5e085EA620d100e951Fb6687d8064d2a2aAB48A"
    },
    "contracts/utils/ZeroCheck.sol": {
      "ZeroCheck": "0xdABAAD81d0453699A7d25D277Ea5edD7b89ceDD4"
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract IWETH","name":"_weth","type":"address"},{"internalType":"contract IERC20Upgradeable","name":"_usdc","type":"address"},{"internalType":"contract ISonaSwap","name":"_swap","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CreateError","type":"error"},{"inputs":[{"internalType":"address","name":"newController","type":"address"}],"name":"InvalidNewController","type":"error"},{"inputs":[{"internalType":"uint256","name":"accountsLength","type":"uint256"},{"internalType":"uint256","name":"allocationsLength","type":"uint256"}],"name":"InvalidSplit__AccountsAndAllocationsMismatch","type":"error"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"InvalidSplit__AccountsOutOfOrder","type":"error"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"InvalidSplit__AllocationMustBePositive","type":"error"},{"inputs":[{"internalType":"uint32","name":"allocationsSum","type":"uint32"}],"name":"InvalidSplit__InvalidAllocationsSum","type":"error"},{"inputs":[{"internalType":"uint32","name":"distributorFee","type":"uint32"}],"name":"InvalidSplit__InvalidDistributorFee","type":"error"},{"inputs":[{"internalType":"bytes32","name":"hash","type":"bytes32"}],"name":"InvalidSplit__InvalidHash","type":"error"},{"inputs":[{"internalType":"uint256","name":"accountsLength","type":"uint256"}],"name":"InvalidSplit__TooFewAccounts","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"UnauthorizedController","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"split","type":"address"}],"name":"CancelControlTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"split","type":"address"},{"indexed":true,"internalType":"address","name":"previousController","type":"address"},{"indexed":true,"internalType":"address","name":"newController","type":"address"}],"name":"ControlTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"split","type":"address"}],"name":"CreateSplit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"split","type":"address"},{"indexed":true,"internalType":"contract IERC20Upgradeable","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"DistributeERC20","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"split","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"DistributeETH","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"split","type":"address"},{"indexed":true,"internalType":"address","name":"newPotentialController","type":"address"}],"name":"InitiateControlTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"split","type":"address"}],"name":"UpdateSplit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"ethAmount","type":"uint256"},{"indexed":false,"internalType":"contract IERC20Upgradeable[]","name":"tokens","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"tokenAmounts","type":"uint256[]"}],"name":"Withdrawal","type":"event"},{"inputs":[],"name":"PERCENTAGE_SCALE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"USDC","outputs":[{"internalType":"contract IERC20Upgradeable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH9","outputs":[{"internalType":"contract IWETH","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"split","type":"address"}],"name":"acceptControl","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"split","type":"address"}],"name":"cancelControlTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"uint32[]","name":"percentAllocations","type":"uint32[]"}],"name":"createSplit","outputs":[{"internalType":"address","name":"split","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"uint32[]","name":"percentAllocations","type":"uint32[]"}],"internalType":"struct ISplitMain.SplitInput[]","name":"splits","type":"tuple[]"}],"name":"createSplits","outputs":[{"internalType":"address[]","name":"splitAddresses","type":"address[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"split","type":"address"},{"internalType":"contract IERC20Upgradeable","name":"token","type":"address"},{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"uint32[]","name":"percentAllocations","type":"uint32[]"}],"name":"distributeERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"split","type":"address"},{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"uint32[]","name":"percentAllocations","type":"uint32[]"}],"name":"distributeETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"split","type":"address"}],"name":"getController","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"contract IERC20Upgradeable","name":"token","type":"address"}],"name":"getERC20Balance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getETHBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"split","type":"address"}],"name":"getHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ownershipHandoverValidFor","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"swap","outputs":[{"internalType":"contract ISonaSwap","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"split","type":"address"},{"internalType":"address","name":"newController","type":"address"}],"name":"transferControl","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"split","type":"address"},{"internalType":"contract IERC20Upgradeable","name":"token","type":"address"},{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"uint32[]","name":"percentAllocations","type":"uint32[]"}],"name":"updateAndDistributeERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"split","type":"address"},{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"uint32[]","name":"percentAllocations","type":"uint32[]"}],"name":"updateAndDistributeETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"split","type":"address"},{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"uint32[]","name":"percentAllocations","type":"uint32[]"}],"name":"updateSplit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ISonaSwap","name":"_newSwap","type":"address"}],"name":"updateSwap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"walletImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"withdrawETH","type":"uint256"},{"internalType":"contract IERC20Upgradeable[]","name":"tokens","type":"address[]"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

60e06040523480156200001157600080fd5b5060405162002caa38038062002caa833981016040819052620000349162000108565b6040516200004290620000e1565b604051809103906000f0801580156200005f573d6000803e3d6000fd5b506001600160a01b0390811660805283811660a05282811660c052600080546001600160a01b0319169183169190911790556200009c33620000a5565b5050506200015c565b6001600160a01b0316638b78c6d8198190558060007f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a350565b6102bc80620029ee83390190565b6001600160a01b03811681146200010557600080fd5b50565b6000806000606084860312156200011e57600080fd5b83516200012b81620000ef565b60208501519093506200013e81620000ef565b60408501519092506200015181620000ef565b809150509250925092565b60805160a05160c05161281c620001d2600039600081816103da01528181610b6401528181611d410152611d8e0152600081816102ae01528181610c110152818161192d01528181611a3601528181611b0401528181611b7f0152611c1b01526000818161032a0152611379015261281c6000f3fe6080604052600436106101c65760003560e01c806389a30271116100f7578063c7de644011610095578063f04e283e11610064578063f04e283e14610540578063f2fde38b14610553578063f76decfe14610566578063fee81cf41461058657600080fd5b8063c7de6440146104c2578063d0e4b2f4146104e2578063d7533f0214610502578063e40dcfe31461052057600080fd5b80639ffab000116100d15780639ffab00014610435578063afaed66314610455578063c3a8962c14610482578063c458f5ed146104a257600080fd5b806389a30271146103c85780638da5cb5b146103fc57806394e6fd601461041557600080fd5b806354d1f13d116101645780638117abc11161013e5780638117abc1146103185780638119c0651461034c57806388c662aa1461036c57806389063570146103a857600080fd5b806354d1f13d146102e85780636e5f6919146102f0578063715018a61461031057600080fd5b806329701c3f116101a057806329701c3f146102455780633bb66a7b146102655780633f26479e146102855780634aa4a4fc1461029c57600080fd5b80631267c6da146101d25780631da0b8fc146101f4578063256929621461023d57600080fd5b366101cd57005b600080fd5b3480156101de57600080fd5b506101f26101ed3660046122c0565b6105b9565b005b34801561020057600080fd5b5061022a61020f3660046122c0565b6001600160a01b031660009081526003602052604090205490565b6040519081526020015b60405180910390f35b6101f2610652565b34801561025157600080fd5b506101f2610260366004612329565b6106a2565b34801561027157600080fd5b5061022a6102803660046122c0565b61076b565b34801561029157600080fd5b5061022a620f424081565b3480156102a857600080fd5b506102d07f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610234565b6101f26107c6565b3480156102fc57600080fd5b506101f261030b3660046123ac565b610802565b6101f261090d565b34801561032457600080fd5b506102d07f000000000000000000000000000000000000000000000000000000000000000081565b34801561035857600080fd5b506000546102d0906001600160a01b031681565b34801561037857600080fd5b506102d06103873660046122c0565b6001600160a01b039081166000908152600360205260409020600101541690565b3480156103b457600080fd5b506101f26103c3366004612329565b610921565b3480156103d457600080fd5b506102d07f000000000000000000000000000000000000000000000000000000000000000081565b34801561040857600080fd5b50638b78c6d819546102d0565b34801561042157600080fd5b506101f26104303660046122c0565b610a59565b34801561044157600080fd5b506101f2610450366004612408565b610a83565b34801561046157600080fd5b5061047561047036600461249d565b610de7565b60405161023491906124df565b34801561048e57600080fd5b5061022a61049d36600461252c565b610ee1565b3480156104ae57600080fd5b506101f26104bd366004612329565b610fa8565b3480156104ce57600080fd5b506101f26104dd3660046122c0565b6110f5565b3480156104ee57600080fd5b506101f26104fd36600461252c565b6111c0565b34801561050e57600080fd5b506040516202a3008152602001610234565b34801561052c57600080fd5b506102d061053b366004612565565b611290565b6101f261054e3660046122c0565b611402565b6101f26105613660046122c0565b611442565b34801561057257600080fd5b506101f2610581366004612408565b611469565b34801561059257600080fd5b5061022a6105a13660046122c0565b63389a75e1600c908152600091909152602090205490565b6001600160a01b0381811660009081526003602052604090206001015482911633146105ff576040516311ca4b2760e31b81523360048201526024015b60405180910390fd5b6001600160a01b03821660008181526003602052604080822060020180546001600160a01b0319169055517f6c2460a415b84be3720c209fe02f2cad7a6bcba21e8637afe8957b7ec4b6ef879190a25050565b60006202a30067ffffffffffffffff164201905063389a75e1600c5233600052806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a250565b6001600160a01b0385811660009081526003602052604090206001015486911633146106e3576040516311ca4b2760e31b81523360048201526024016105f6565b848480806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250506040805160208088028281018201909352878252909350879250869182918501908490808284376000920191909152506107549250849150839050611597565b610761888888888861174c565b5050505050505050565b6001600160a01b038116600090815260036020526040812054810361079157600061079d565b816001600160a01b0316315b6001600160a01b0383166000908152600160205260409020546107c091906125db565b92915050565b63389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2565b60008167ffffffffffffffff81111561081d5761081d6125ee565b604051908082528060200260200182016040528015610846578160200160208202803683370190505b5090506000841561085d5761085a86611809565b90505b60005b838110156108bd576108988786868481811061087e5761087e612604565b905060200201602081019061089391906122c0565b61185c565b8382815181106108aa576108aa612604565b6020908102919091010152600101610860565b50856001600160a01b03167fa9e30bf144f83390a4fe47562a4e16892108102221c674ff538da0b72a83d174828686866040516108fd949392919061261a565b60405180910390a2505050505050565b6109156118ca565b61091f60006118e5565b565b6001600160a01b038581166000908152600360205260409020600101548691163314610962576040516311ca4b2760e31b81523360048201526024016105f6565b848480806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250506040805160208088028281018201909352878252909350879250869182918501908490808284376000920191909152506109d39250849150839050611597565b6109e0888888888861174c565b610a4e8888888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808c0282810182019093528b82529093508b92508a91829185019084908082843760009201919091525061192392505050565b505050505050505050565b610a616118ca565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b83838080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808702828101820190935286825290935086925085918291850190849080828437600092019190915250610af49250849150839050611597565b610b628887878080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808b0282810182019093528a82529093508a925089918291850190849080828437600092019190915250611e1592505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316876001600160a01b031603610c0f57610c0a888888888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808c0282810182019093528b82529093508b92508a918291850190849080828437600092019190915250611e6592505050565b610761565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316876001600160a01b031603610cbc57610cb68887878080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808b0282810182019093528a82529093508a92508991829185019084908082843760009201919091525061192392505050565b50610761565b6040516370a0823160e01b81526001600160a01b038981166004830152600091908916906370a0823190602401602060405180830381865afa158015610d06573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d2a91906126ab565b604051633e0f9fff60e11b81526001600160a01b038a8116600483015260248201839052919250908a1690637c1f3ffe90604401600060405180830381600087803b158015610d7857600080fd5b505af1158015610d8c573d6000803e3d6000fd5b505050506001600160a01b0388811660009081526002602090815260408083208d85168452600383528184206001015490941683529290529081208054839290610dd79084906125db565b9091555050505050505050505050565b6060818067ffffffffffffffff811115610e0357610e036125ee565b604051908082528060200260200182016040528015610e2c578160200160208202803683370190505b50915060005b81811015610ed957610e9d858583818110610e4f57610e4f612604565b9050602002810190610e6191906126c4565b610e6b90806126e4565b878785818110610e7d57610e7d612604565b9050602002810190610e8f91906126c4565b61053b9060208101906126e4565b838281518110610eaf57610eaf612604565b6001600160a01b039092166020928302919091019091015280610ed18161272e565b915050610e32565b505092915050565b6001600160a01b0382166000908152600360205260408120548103610f07576000610f71565b6040516370a0823160e01b81526001600160a01b0384811660048301528316906370a0823190602401602060405180830381865afa158015610f4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f7191906126ab565b6001600160a01b03808416600090815260026020908152604080832093881683529290522054610fa191906125db565b9392505050565b838380806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250506040805160208087028281018201909352868252909350869250859182918501908490808284376000920191909152506110199250849150839050611597565b6110878787878080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808b0282810182019093528a82529093508a925089918291850190849080828437600092019190915250611e1592505050565b6107618787878080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808b0282810182019093528a82529093508a92508991829185019084908082843760009201919091525061192392505050565b6001600160a01b038181166000908152600360205260409020600201548291163314611136576040516311ca4b2760e31b81523360048201526024016105f6565b6001600160a01b038083166000818152600360205260408082206002810180546001600160a01b031916905560010154905133949190911692917f943d69cf2bbe08a9d44b3c4ce6da17d939d758739370620871ce99a6437866d091a4506001600160a01b0316600090815260036020526040902060010180546001600160a01b03191633179055565b6001600160a01b038281166000908152600360205260409020600101548391163314611201576040516311ca4b2760e31b81523360048201526024016105f6565b816001600160a01b0381166112345760405163c369130760e01b81526001600160a01b03821660048201526024016105f6565b6001600160a01b0384811660008181526003602052604080822060020180546001600160a01b0319169488169485179055517f107cf6ea8668d533df1aab5bb8b6315bb0c25f0b6c955558d09368f290668fc79190a350505050565b6000848480806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250506040805160208088028281018201909352878252909350879250869182918501908490808284376000920191909152506113039250849150839050611597565b600061137288888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808c0282810182019093528b82529093508b92508a91829185019084908082843760009201919091525061209c92505050565b905061139d7f00000000000000000000000000000000000000000000000000000000000000006120cf565b6001600160a01b0381166000818152600360205260408082206001810180546001600160a01b031916331790558590555192965090917f8d5f9943c664a3edaf4d3eb18cc5e2c45a7d2dc5869be33d33bbc0fff9bc25909190a2505050949350505050565b61140a6118ca565b63389a75e1600c52806000526020600c20805442111561143257636f5e88186000526004601cfd5b6000905561143f816118e5565b50565b61144a6118ca565b8060601b61146057637448fbae6000526004601cfd5b61143f816118e5565b6001600160a01b0386811660009081526003602052604090206001015487911633146114aa576040516311ca4b2760e31b81523360048201526024016105f6565b8484808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152505060408051602080880282810182019093528782529093508792508691829185019084908082843760009201919091525061151b9250849150839050611597565b611528898888888861174c565b610a4e898989898080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808d0282810182019093528c82529093508c92508b918291850190849080828437600092019190915250611e6592505050565b6002825110156115bf578151604051630e8c626560e41b81526004016105f691815260200190565b80518251146115ee578151815160405163b34f351d60e01b8152600481019290925260248201526044016105f6565b620f42406115fb8261217e565b63ffffffff16146116315761160f8161217e565b60405163fcc487c160e01b815263ffffffff90911660048201526024016105f6565b81516000190160005b818110156116fd5783816001018151811061165757611657612604565b60200260200101516001600160a01b031684828151811061167a5761167a612604565b60200260200101516001600160a01b0316106116ac5760405163ac6bd23360e01b8152600481018290526024016105f6565b600063ffffffff168382815181106116c6576116c6612604565b602002602001015163ffffffff16036116f557604051630db7e4c760e01b8152600481018290526024016105f6565b60010161163a565b50600063ffffffff1682828151811061171857611718612604565b602002602001015163ffffffff160361174757604051630db7e4c760e01b8152600481018290526024016105f6565b505050565b60006117bb8585808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152505060408051602080890282810182019093528882529093508892508791829185019084908082843760009201919091525061209c92505050565b6001600160a01b0387166000818152600360205260408082208490555192935090917f45e1e99513dd915ac128b94953ca64c6375717ea1894b3114db08cdca51debd29190a2505050505050565b6001600160a01b038116600090815260016020819052604082205461182e9190612747565b6001600160a01b03831660008181526001602081905260409091205590915061185790826121c3565b919050565b6001600160a01b03808216600090815260026020908152604080832093861683529290529081205461189090600190612747565b6001600160a01b038084166000818152600260209081526040808320948916835293905291909120600190559091506107c09084836121e3565b638b78c6d81954331461091f576382b429006000526004601cfd5b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660008181526002602090815260408083209488168084529482528083205460019092528083205490516370a0823160e01b815260048101959095529193849391929184916370a0823190602401602060405180830381865afa1580156119b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119dc91906126ab565b90506001600160a01b0388163181156119f6576001820391505b8315611a03576001840393505b8215611a10576001830392505b8383018201810194508115611aa257604051633e0f9fff60e11b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018490528a1690637c1f3ffe90604401600060405180830381600087803b158015611a8957600080fd5b505af1158015611a9d573d6000803e3d6000fd5b505050505b8015611b7757604051632ac3affd60e21b8152600481018290526001600160a01b038a169063ab0ebff490602401600060405180830381600087803b158015611aea57600080fd5b505af1158015611afe573d6000803e3d6000fd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015611b5d57600080fd5b505af1158015611b71573d6000803e3d6000fd5b50505050505b8215611bf2577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0846040518263ffffffff1660e01b81526004016000604051808303818588803b158015611bd857600080fd5b505af1158015611bec573d6000803e3d6000fd5b50505050505b60005460405163095ea7b360e01b81526001600160a01b039182166004820152602481018790527f00000000000000000000000000000000000000000000000000000000000000009091169063095ea7b3906044016020604051808303816000875af1158015611c66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c8a919061275a565b506000546040516322287e0b60e21b8152600481018790526001600160a01b03909116906388a1f82c906024016020604051808303816000875af1158015611cd6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cfa91906126ab565b885190965060005b81811015611e07576000611d3a898b8481518110611d2257611d22612604565b602002602001015163ffffffff16620f424091020490565b9050611d807f00000000000000000000000000000000000000000000000000000000000000008c8481518110611d7257611d72612604565b60200260200101518361222c565b611dfe576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660009081526002602052604081208c518392908e9086908110611dd357611dd3612604565b6020908102919091018101516001600160a01b03168252810191909152604001600020805490910190555b50600101611d02565b505050505050509392505050565b6000611e21838361209c565b6001600160a01b0385166000908152600360205260409020549091508114611e5f5760405163dd5ff45760e01b8152600481018290526024016105f6565b50505050565b6001600160a01b038381166000818152600260209081526040808320948916808452949091528082205490516370a0823160e01b815260048101949094529092909183916370a0823190602401602060405180830381865afa158015611ecf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ef391906126ab565b90508015611f0057600019015b8115611f0d576001820391505b81810192508115611f42576001600160a01b038087166000908152600260209081526040808320938b16835292905220600190555b856001600160a01b0316876001600160a01b03167f5ce93041411cc768a3969c4b4575a472183e7487eb37987fef9f0044d9a95e2185604051611f8791815260200190565b60405180910390a38015611ff857604051633e0f9fff60e11b81526001600160a01b03878116600483015260248201839052881690637c1f3ffe90604401600060405180830381600087803b158015611fdf57600080fd5b505af1158015611ff3573d6000803e3d6000fd5b505050505b845160005b81811015610a4e57600061201d86888481518110611d2257611d22612604565b905061203589898481518110611d7257611d72612604565b612093576001600160a01b038916600090815260026020526040812089518392908b908690811061206857612068612604565b6020908102919091018101516001600160a01b03168252810191909152604001600020805490910190555b50600101611ffd565b600082826040516020016120b192919061277c565b60405160208183030381529060405280519060200120905092915050565b6000604051723d605d80600a3d3981f336603057343d52307f60681b81527f830d2d700a97af574b186c80d40429385d24241565b08a7c559ba283a964d9b160138201527260203da23d3df35b3d3d3d3d363d3d37363d7360681b60338201528260601b60468201526c5af43d3d93803e605b57fd5bf360981b605a8201526067816000f09150506001600160a01b03811661185757604051630985da9b60e41b815260040160405180910390fd5b8051600090815b818110156121bc5783818151811061219f5761219f612604565b6020026020010151836121b291906127eb565b9250600101612185565b5050919050565b60008060008084865af16121df5763b12d13eb6000526004601cfd5b5050565b81601a5280603a5269a9059cbb00000000000060005260206000604460166000875af13d156001600051141716612222576390b8ec186000526004601cfd5b6000603a52505050565b60405163a9059cbb60e01b81526001600160a01b038381166004830152602482018390526000919085169063a9059cbb906044016020604051808303816000875af115801561227f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122a3919061275a565b949350505050565b6001600160a01b038116811461143f57600080fd5b6000602082840312156122d257600080fd5b8135610fa1816122ab565b60008083601f8401126122ef57600080fd5b50813567ffffffffffffffff81111561230757600080fd5b6020830191508360208260051b850101111561232257600080fd5b9250929050565b60008060008060006060868803121561234157600080fd5b853561234c816122ab565b9450602086013567ffffffffffffffff8082111561236957600080fd5b61237589838a016122dd565b9096509450604088013591508082111561238e57600080fd5b5061239b888289016122dd565b969995985093965092949392505050565b600080600080606085870312156123c257600080fd5b84356123cd816122ab565b935060208501359250604085013567ffffffffffffffff8111156123f057600080fd5b6123fc878288016122dd565b95989497509550505050565b6000806000806000806080878903121561242157600080fd5b863561242c816122ab565b9550602087013561243c816122ab565b9450604087013567ffffffffffffffff8082111561245957600080fd5b6124658a838b016122dd565b9096509450606089013591508082111561247e57600080fd5b5061248b89828a016122dd565b979a9699509497509295939492505050565b600080602083850312156124b057600080fd5b823567ffffffffffffffff8111156124c757600080fd5b6124d3858286016122dd565b90969095509350505050565b6020808252825182820181905260009190848201906040850190845b818110156125205783516001600160a01b0316835292840192918401916001016124fb565b50909695505050505050565b6000806040838503121561253f57600080fd5b823561254a816122ab565b9150602083013561255a816122ab565b809150509250929050565b6000806000806040858703121561257b57600080fd5b843567ffffffffffffffff8082111561259357600080fd5b61259f888389016122dd565b909650945060208701359150808211156125b857600080fd5b506123fc878288016122dd565b634e487b7160e01b600052601160045260246000fd5b808201808211156107c0576107c06125c5565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b84815260606020808301829052908201849052600090859060808401835b8781101561266657833561264b816122ab565b6001600160a01b031682529282019290820190600101612638565b508481036040860152855180825290820192508186019060005b8181101561269c57825185529383019391830191600101612680565b50929998505050505050505050565b6000602082840312156126bd57600080fd5b5051919050565b60008235603e198336030181126126da57600080fd5b9190910192915050565b6000808335601e198436030181126126fb57600080fd5b83018035915067ffffffffffffffff82111561271657600080fd5b6020019150600581901b360382131561232257600080fd5b600060018201612740576127406125c5565b5060010190565b818103818111156107c0576107c06125c5565b60006020828403121561276c57600080fd5b81518015158114610fa157600080fd5b825160009082906020808701845b838110156127af5781516001600160a01b03168552938201939082019060010161278a565b5050855181870193925060005b818110156127de57845163ffffffff16845293820193928201926001016127bc565b5091979650505050505050565b63ffffffff818116838216019080821115612808576128086125c5565b509291505056fea164736f6c6343000812000a60a060405234801561001057600080fd5b503360805260805161027261004a60003960008181604b0152818160bc015281816101080152818161013c015261018601526102726000f3fe6080604052600436106100345760003560e01c80630e769b2b146100395780637c1f3ffe14610089578063ab0ebff41461009e575b600080fd5b34801561004557600080fd5b5061006d7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200160405180910390f35b61009c610097366004610214565b6100b1565b005b61009c6100ac36600461024c565b610131565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146100f9576040516282b42960e81b815260040160405180910390fd5b61012d6001600160a01b0383167f0000000000000000000000000000000000000000000000000000000000000000836101af565b5050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610179576040516282b42960e81b815260040160405180910390fd5b6101ac6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016826101f8565b50565b81601a5280603a5269a9059cbb00000000000060005260206000604460166000875af13d1560016000511417166101ee576390b8ec186000526004601cfd5b6000603a52505050565b60008060008084865af161012d5763b12d13eb6000526004601cfd5b6000806040838503121561022757600080fd5b82356001600160a01b038116811461023e57600080fd5b946020939093013593505050565b60006020828403121561025e57600080fd5b503591905056fea164736f6c6343000812000a000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000e386b025180dc6c0459c7f74476150e8c93e9843

Deployed Bytecode

0x6080604052600436106101c65760003560e01c806389a30271116100f7578063c7de644011610095578063f04e283e11610064578063f04e283e14610540578063f2fde38b14610553578063f76decfe14610566578063fee81cf41461058657600080fd5b8063c7de6440146104c2578063d0e4b2f4146104e2578063d7533f0214610502578063e40dcfe31461052057600080fd5b80639ffab000116100d15780639ffab00014610435578063afaed66314610455578063c3a8962c14610482578063c458f5ed146104a257600080fd5b806389a30271146103c85780638da5cb5b146103fc57806394e6fd601461041557600080fd5b806354d1f13d116101645780638117abc11161013e5780638117abc1146103185780638119c0651461034c57806388c662aa1461036c57806389063570146103a857600080fd5b806354d1f13d146102e85780636e5f6919146102f0578063715018a61461031057600080fd5b806329701c3f116101a057806329701c3f146102455780633bb66a7b146102655780633f26479e146102855780634aa4a4fc1461029c57600080fd5b80631267c6da146101d25780631da0b8fc146101f4578063256929621461023d57600080fd5b366101cd57005b600080fd5b3480156101de57600080fd5b506101f26101ed3660046122c0565b6105b9565b005b34801561020057600080fd5b5061022a61020f3660046122c0565b6001600160a01b031660009081526003602052604090205490565b6040519081526020015b60405180910390f35b6101f2610652565b34801561025157600080fd5b506101f2610260366004612329565b6106a2565b34801561027157600080fd5b5061022a6102803660046122c0565b61076b565b34801561029157600080fd5b5061022a620f424081565b3480156102a857600080fd5b506102d07f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b6040516001600160a01b039091168152602001610234565b6101f26107c6565b3480156102fc57600080fd5b506101f261030b3660046123ac565b610802565b6101f261090d565b34801561032457600080fd5b506102d07f000000000000000000000000ac1365ac74c8f38d4a0184744da8cef18299602581565b34801561035857600080fd5b506000546102d0906001600160a01b031681565b34801561037857600080fd5b506102d06103873660046122c0565b6001600160a01b039081166000908152600360205260409020600101541690565b3480156103b457600080fd5b506101f26103c3366004612329565b610921565b3480156103d457600080fd5b506102d07f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4881565b34801561040857600080fd5b50638b78c6d819546102d0565b34801561042157600080fd5b506101f26104303660046122c0565b610a59565b34801561044157600080fd5b506101f2610450366004612408565b610a83565b34801561046157600080fd5b5061047561047036600461249d565b610de7565b60405161023491906124df565b34801561048e57600080fd5b5061022a61049d36600461252c565b610ee1565b3480156104ae57600080fd5b506101f26104bd366004612329565b610fa8565b3480156104ce57600080fd5b506101f26104dd3660046122c0565b6110f5565b3480156104ee57600080fd5b506101f26104fd36600461252c565b6111c0565b34801561050e57600080fd5b506040516202a3008152602001610234565b34801561052c57600080fd5b506102d061053b366004612565565b611290565b6101f261054e3660046122c0565b611402565b6101f26105613660046122c0565b611442565b34801561057257600080fd5b506101f2610581366004612408565b611469565b34801561059257600080fd5b5061022a6105a13660046122c0565b63389a75e1600c908152600091909152602090205490565b6001600160a01b0381811660009081526003602052604090206001015482911633146105ff576040516311ca4b2760e31b81523360048201526024015b60405180910390fd5b6001600160a01b03821660008181526003602052604080822060020180546001600160a01b0319169055517f6c2460a415b84be3720c209fe02f2cad7a6bcba21e8637afe8957b7ec4b6ef879190a25050565b60006202a30067ffffffffffffffff164201905063389a75e1600c5233600052806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a250565b6001600160a01b0385811660009081526003602052604090206001015486911633146106e3576040516311ca4b2760e31b81523360048201526024016105f6565b848480806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250506040805160208088028281018201909352878252909350879250869182918501908490808284376000920191909152506107549250849150839050611597565b610761888888888861174c565b5050505050505050565b6001600160a01b038116600090815260036020526040812054810361079157600061079d565b816001600160a01b0316315b6001600160a01b0383166000908152600160205260409020546107c091906125db565b92915050565b63389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2565b60008167ffffffffffffffff81111561081d5761081d6125ee565b604051908082528060200260200182016040528015610846578160200160208202803683370190505b5090506000841561085d5761085a86611809565b90505b60005b838110156108bd576108988786868481811061087e5761087e612604565b905060200201602081019061089391906122c0565b61185c565b8382815181106108aa576108aa612604565b6020908102919091010152600101610860565b50856001600160a01b03167fa9e30bf144f83390a4fe47562a4e16892108102221c674ff538da0b72a83d174828686866040516108fd949392919061261a565b60405180910390a2505050505050565b6109156118ca565b61091f60006118e5565b565b6001600160a01b038581166000908152600360205260409020600101548691163314610962576040516311ca4b2760e31b81523360048201526024016105f6565b848480806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250506040805160208088028281018201909352878252909350879250869182918501908490808284376000920191909152506109d39250849150839050611597565b6109e0888888888861174c565b610a4e8888888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808c0282810182019093528b82529093508b92508a91829185019084908082843760009201919091525061192392505050565b505050505050505050565b610a616118ca565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b83838080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808702828101820190935286825290935086925085918291850190849080828437600092019190915250610af49250849150839050611597565b610b628887878080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808b0282810182019093528a82529093508a925089918291850190849080828437600092019190915250611e1592505050565b7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b0316876001600160a01b031603610c0f57610c0a888888888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808c0282810182019093528b82529093508b92508a918291850190849080828437600092019190915250611e6592505050565b610761565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316876001600160a01b031603610cbc57610cb68887878080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808b0282810182019093528a82529093508a92508991829185019084908082843760009201919091525061192392505050565b50610761565b6040516370a0823160e01b81526001600160a01b038981166004830152600091908916906370a0823190602401602060405180830381865afa158015610d06573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d2a91906126ab565b604051633e0f9fff60e11b81526001600160a01b038a8116600483015260248201839052919250908a1690637c1f3ffe90604401600060405180830381600087803b158015610d7857600080fd5b505af1158015610d8c573d6000803e3d6000fd5b505050506001600160a01b0388811660009081526002602090815260408083208d85168452600383528184206001015490941683529290529081208054839290610dd79084906125db565b9091555050505050505050505050565b6060818067ffffffffffffffff811115610e0357610e036125ee565b604051908082528060200260200182016040528015610e2c578160200160208202803683370190505b50915060005b81811015610ed957610e9d858583818110610e4f57610e4f612604565b9050602002810190610e6191906126c4565b610e6b90806126e4565b878785818110610e7d57610e7d612604565b9050602002810190610e8f91906126c4565b61053b9060208101906126e4565b838281518110610eaf57610eaf612604565b6001600160a01b039092166020928302919091019091015280610ed18161272e565b915050610e32565b505092915050565b6001600160a01b0382166000908152600360205260408120548103610f07576000610f71565b6040516370a0823160e01b81526001600160a01b0384811660048301528316906370a0823190602401602060405180830381865afa158015610f4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f7191906126ab565b6001600160a01b03808416600090815260026020908152604080832093881683529290522054610fa191906125db565b9392505050565b838380806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250506040805160208087028281018201909352868252909350869250859182918501908490808284376000920191909152506110199250849150839050611597565b6110878787878080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808b0282810182019093528a82529093508a925089918291850190849080828437600092019190915250611e1592505050565b6107618787878080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808b0282810182019093528a82529093508a92508991829185019084908082843760009201919091525061192392505050565b6001600160a01b038181166000908152600360205260409020600201548291163314611136576040516311ca4b2760e31b81523360048201526024016105f6565b6001600160a01b038083166000818152600360205260408082206002810180546001600160a01b031916905560010154905133949190911692917f943d69cf2bbe08a9d44b3c4ce6da17d939d758739370620871ce99a6437866d091a4506001600160a01b0316600090815260036020526040902060010180546001600160a01b03191633179055565b6001600160a01b038281166000908152600360205260409020600101548391163314611201576040516311ca4b2760e31b81523360048201526024016105f6565b816001600160a01b0381166112345760405163c369130760e01b81526001600160a01b03821660048201526024016105f6565b6001600160a01b0384811660008181526003602052604080822060020180546001600160a01b0319169488169485179055517f107cf6ea8668d533df1aab5bb8b6315bb0c25f0b6c955558d09368f290668fc79190a350505050565b6000848480806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250506040805160208088028281018201909352878252909350879250869182918501908490808284376000920191909152506113039250849150839050611597565b600061137288888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808c0282810182019093528b82529093508b92508a91829185019084908082843760009201919091525061209c92505050565b905061139d7f000000000000000000000000ac1365ac74c8f38d4a0184744da8cef1829960256120cf565b6001600160a01b0381166000818152600360205260408082206001810180546001600160a01b031916331790558590555192965090917f8d5f9943c664a3edaf4d3eb18cc5e2c45a7d2dc5869be33d33bbc0fff9bc25909190a2505050949350505050565b61140a6118ca565b63389a75e1600c52806000526020600c20805442111561143257636f5e88186000526004601cfd5b6000905561143f816118e5565b50565b61144a6118ca565b8060601b61146057637448fbae6000526004601cfd5b61143f816118e5565b6001600160a01b0386811660009081526003602052604090206001015487911633146114aa576040516311ca4b2760e31b81523360048201526024016105f6565b8484808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152505060408051602080880282810182019093528782529093508792508691829185019084908082843760009201919091525061151b9250849150839050611597565b611528898888888861174c565b610a4e898989898080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808d0282810182019093528c82529093508c92508b918291850190849080828437600092019190915250611e6592505050565b6002825110156115bf578151604051630e8c626560e41b81526004016105f691815260200190565b80518251146115ee578151815160405163b34f351d60e01b8152600481019290925260248201526044016105f6565b620f42406115fb8261217e565b63ffffffff16146116315761160f8161217e565b60405163fcc487c160e01b815263ffffffff90911660048201526024016105f6565b81516000190160005b818110156116fd5783816001018151811061165757611657612604565b60200260200101516001600160a01b031684828151811061167a5761167a612604565b60200260200101516001600160a01b0316106116ac5760405163ac6bd23360e01b8152600481018290526024016105f6565b600063ffffffff168382815181106116c6576116c6612604565b602002602001015163ffffffff16036116f557604051630db7e4c760e01b8152600481018290526024016105f6565b60010161163a565b50600063ffffffff1682828151811061171857611718612604565b602002602001015163ffffffff160361174757604051630db7e4c760e01b8152600481018290526024016105f6565b505050565b60006117bb8585808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152505060408051602080890282810182019093528882529093508892508791829185019084908082843760009201919091525061209c92505050565b6001600160a01b0387166000818152600360205260408082208490555192935090917f45e1e99513dd915ac128b94953ca64c6375717ea1894b3114db08cdca51debd29190a2505050505050565b6001600160a01b038116600090815260016020819052604082205461182e9190612747565b6001600160a01b03831660008181526001602081905260409091205590915061185790826121c3565b919050565b6001600160a01b03808216600090815260026020908152604080832093861683529290529081205461189090600190612747565b6001600160a01b038084166000818152600260209081526040808320948916835293905291909120600190559091506107c09084836121e3565b638b78c6d81954331461091f576382b429006000526004601cfd5b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355565b6001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2811660008181526002602090815260408083209488168084529482528083205460019092528083205490516370a0823160e01b815260048101959095529193849391929184916370a0823190602401602060405180830381865afa1580156119b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119dc91906126ab565b90506001600160a01b0388163181156119f6576001820391505b8315611a03576001840393505b8215611a10576001830392505b8383018201810194508115611aa257604051633e0f9fff60e11b81526001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281166004830152602482018490528a1690637c1f3ffe90604401600060405180830381600087803b158015611a8957600080fd5b505af1158015611a9d573d6000803e3d6000fd5b505050505b8015611b7757604051632ac3affd60e21b8152600481018290526001600160a01b038a169063ab0ebff490602401600060405180830381600087803b158015611aea57600080fd5b505af1158015611afe573d6000803e3d6000fd5b505050507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015611b5d57600080fd5b505af1158015611b71573d6000803e3d6000fd5b50505050505b8215611bf2577f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0846040518263ffffffff1660e01b81526004016000604051808303818588803b158015611bd857600080fd5b505af1158015611bec573d6000803e3d6000fd5b50505050505b60005460405163095ea7b360e01b81526001600160a01b039182166004820152602481018790527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc29091169063095ea7b3906044016020604051808303816000875af1158015611c66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c8a919061275a565b506000546040516322287e0b60e21b8152600481018790526001600160a01b03909116906388a1f82c906024016020604051808303816000875af1158015611cd6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cfa91906126ab565b885190965060005b81811015611e07576000611d3a898b8481518110611d2257611d22612604565b602002602001015163ffffffff16620f424091020490565b9050611d807f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb488c8481518110611d7257611d72612604565b60200260200101518361222c565b611dfe576001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb481660009081526002602052604081208c518392908e9086908110611dd357611dd3612604565b6020908102919091018101516001600160a01b03168252810191909152604001600020805490910190555b50600101611d02565b505050505050509392505050565b6000611e21838361209c565b6001600160a01b0385166000908152600360205260409020549091508114611e5f5760405163dd5ff45760e01b8152600481018290526024016105f6565b50505050565b6001600160a01b038381166000818152600260209081526040808320948916808452949091528082205490516370a0823160e01b815260048101949094529092909183916370a0823190602401602060405180830381865afa158015611ecf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ef391906126ab565b90508015611f0057600019015b8115611f0d576001820391505b81810192508115611f42576001600160a01b038087166000908152600260209081526040808320938b16835292905220600190555b856001600160a01b0316876001600160a01b03167f5ce93041411cc768a3969c4b4575a472183e7487eb37987fef9f0044d9a95e2185604051611f8791815260200190565b60405180910390a38015611ff857604051633e0f9fff60e11b81526001600160a01b03878116600483015260248201839052881690637c1f3ffe90604401600060405180830381600087803b158015611fdf57600080fd5b505af1158015611ff3573d6000803e3d6000fd5b505050505b845160005b81811015610a4e57600061201d86888481518110611d2257611d22612604565b905061203589898481518110611d7257611d72612604565b612093576001600160a01b038916600090815260026020526040812089518392908b908690811061206857612068612604565b6020908102919091018101516001600160a01b03168252810191909152604001600020805490910190555b50600101611ffd565b600082826040516020016120b192919061277c565b60405160208183030381529060405280519060200120905092915050565b6000604051723d605d80600a3d3981f336603057343d52307f60681b81527f830d2d700a97af574b186c80d40429385d24241565b08a7c559ba283a964d9b160138201527260203da23d3df35b3d3d3d3d363d3d37363d7360681b60338201528260601b60468201526c5af43d3d93803e605b57fd5bf360981b605a8201526067816000f09150506001600160a01b03811661185757604051630985da9b60e41b815260040160405180910390fd5b8051600090815b818110156121bc5783818151811061219f5761219f612604565b6020026020010151836121b291906127eb565b9250600101612185565b5050919050565b60008060008084865af16121df5763b12d13eb6000526004601cfd5b5050565b81601a5280603a5269a9059cbb00000000000060005260206000604460166000875af13d156001600051141716612222576390b8ec186000526004601cfd5b6000603a52505050565b60405163a9059cbb60e01b81526001600160a01b038381166004830152602482018390526000919085169063a9059cbb906044016020604051808303816000875af115801561227f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122a3919061275a565b949350505050565b6001600160a01b038116811461143f57600080fd5b6000602082840312156122d257600080fd5b8135610fa1816122ab565b60008083601f8401126122ef57600080fd5b50813567ffffffffffffffff81111561230757600080fd5b6020830191508360208260051b850101111561232257600080fd5b9250929050565b60008060008060006060868803121561234157600080fd5b853561234c816122ab565b9450602086013567ffffffffffffffff8082111561236957600080fd5b61237589838a016122dd565b9096509450604088013591508082111561238e57600080fd5b5061239b888289016122dd565b969995985093965092949392505050565b600080600080606085870312156123c257600080fd5b84356123cd816122ab565b935060208501359250604085013567ffffffffffffffff8111156123f057600080fd5b6123fc878288016122dd565b95989497509550505050565b6000806000806000806080878903121561242157600080fd5b863561242c816122ab565b9550602087013561243c816122ab565b9450604087013567ffffffffffffffff8082111561245957600080fd5b6124658a838b016122dd565b9096509450606089013591508082111561247e57600080fd5b5061248b89828a016122dd565b979a9699509497509295939492505050565b600080602083850312156124b057600080fd5b823567ffffffffffffffff8111156124c757600080fd5b6124d3858286016122dd565b90969095509350505050565b6020808252825182820181905260009190848201906040850190845b818110156125205783516001600160a01b0316835292840192918401916001016124fb565b50909695505050505050565b6000806040838503121561253f57600080fd5b823561254a816122ab565b9150602083013561255a816122ab565b809150509250929050565b6000806000806040858703121561257b57600080fd5b843567ffffffffffffffff8082111561259357600080fd5b61259f888389016122dd565b909650945060208701359150808211156125b857600080fd5b506123fc878288016122dd565b634e487b7160e01b600052601160045260246000fd5b808201808211156107c0576107c06125c5565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b84815260606020808301829052908201849052600090859060808401835b8781101561266657833561264b816122ab565b6001600160a01b031682529282019290820190600101612638565b508481036040860152855180825290820192508186019060005b8181101561269c57825185529383019391830191600101612680565b50929998505050505050505050565b6000602082840312156126bd57600080fd5b5051919050565b60008235603e198336030181126126da57600080fd5b9190910192915050565b6000808335601e198436030181126126fb57600080fd5b83018035915067ffffffffffffffff82111561271657600080fd5b6020019150600581901b360382131561232257600080fd5b600060018201612740576127406125c5565b5060010190565b818103818111156107c0576107c06125c5565b60006020828403121561276c57600080fd5b81518015158114610fa157600080fd5b825160009082906020808701845b838110156127af5781516001600160a01b03168552938201939082019060010161278a565b5050855181870193925060005b818110156127de57845163ffffffff16845293820193928201926001016127bc565b5091979650505050505050565b63ffffffff818116838216019080821115612808576128086125c5565b509291505056fea164736f6c6343000812000a

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000e386b025180dc6c0459c7f74476150e8c93e9843

-----Decoded View---------------
Arg [0] : _weth (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
Arg [1] : _usdc (address): 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
Arg [2] : _swap (address): 0xE386B025180dc6c0459C7F74476150e8C93e9843

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Arg [1] : 000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
Arg [2] : 000000000000000000000000e386b025180dc6c0459c7f74476150e8c93e9843


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
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.