ETH Price: $1,801.45 (+9.61%)

Contract

0xbfa5AE6F1108B12d26bC9EF1254eb55932d86d37

Overview

ETH Balance

0 ETH

ETH Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Set DNA Router A...109245502025-03-05 14:25:3948 days ago1741184739IN
0xbfa5AE6F...932d86d37
0 ETH00.00010038

Latest 25 internal transactions (View All)

Parent Transaction Hash Block From To
130390572025-04-23 13:09:1333 mins ago1745413753
0xbfa5AE6F...932d86d37
0 ETH
130390572025-04-23 13:09:1333 mins ago1745413753
0xbfa5AE6F...932d86d37
0 ETH
130388562025-04-23 13:02:3140 mins ago1745413351
0xbfa5AE6F...932d86d37
0.00003292 ETH
130388562025-04-23 13:02:3140 mins ago1745413351
0xbfa5AE6F...932d86d37
0.00003292 ETH
130375812025-04-23 12:20:011 hr ago1745410801
0xbfa5AE6F...932d86d37
0 ETH
130375812025-04-23 12:20:011 hr ago1745410801
0xbfa5AE6F...932d86d37
0 ETH
130356262025-04-23 11:14:512 hrs ago1745406891
0xbfa5AE6F...932d86d37
0 ETH
130356262025-04-23 11:14:512 hrs ago1745406891
0xbfa5AE6F...932d86d37
0 ETH
130329822025-04-23 9:46:433 hrs ago1745401603
0xbfa5AE6F...932d86d37
0.00003125 ETH
130329822025-04-23 9:46:433 hrs ago1745401603
0xbfa5AE6F...932d86d37
0.00003125 ETH
130324522025-04-23 9:29:034 hrs ago1745400543
0xbfa5AE6F...932d86d37
0 ETH
130324522025-04-23 9:29:034 hrs ago1745400543
0xbfa5AE6F...932d86d37
0 ETH
130316292025-04-23 9:01:374 hrs ago1745398897
0xbfa5AE6F...932d86d37
0.00003308 ETH
130316292025-04-23 9:01:374 hrs ago1745398897
0xbfa5AE6F...932d86d37
0.00003308 ETH
130307482025-04-23 8:32:155 hrs ago1745397135
0xbfa5AE6F...932d86d37
0.00000024 ETH
130307482025-04-23 8:32:155 hrs ago1745397135
0xbfa5AE6F...932d86d37
0.00000024 ETH
130273272025-04-23 6:38:137 hrs ago1745390293
0xbfa5AE6F...932d86d37
0.00000019 ETH
130273272025-04-23 6:38:137 hrs ago1745390293
0xbfa5AE6F...932d86d37
0.00000019 ETH
130271992025-04-23 6:33:577 hrs ago1745390037
0xbfa5AE6F...932d86d37
0.00000002 ETH
130271992025-04-23 6:33:577 hrs ago1745390037
0xbfa5AE6F...932d86d37
0.00000002 ETH
130256692025-04-23 5:42:578 hrs ago1745386977
0xbfa5AE6F...932d86d37
0.00000006 ETH
130256692025-04-23 5:42:578 hrs ago1745386977
0xbfa5AE6F...932d86d37
0.00000006 ETH
130251802025-04-23 5:26:398 hrs ago1745385999
0xbfa5AE6F...932d86d37
0.00003362 ETH
130251802025-04-23 5:26:398 hrs ago1745385999
0xbfa5AE6F...932d86d37
0.00003362 ETH
130247472025-04-23 5:12:138 hrs ago1745385133
0xbfa5AE6F...932d86d37
0.00003027 ETH
View All Internal Transactions

Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
DNAController

Compiler Version
v0.8.24+commit.e11b9ed9

Optimization Enabled:
Yes with 65535 runs

Other Settings:
paris EvmVersion
File 1 of 17 : DNAController.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import "@openzeppelin/contracts/access/Ownable.sol";
import "./DNARouter.sol";
import "./BaseAggregator.sol";
import { Permit2, Permit2Helper } from "./Permit2Helper.sol";
import "./lib/Common.sol";

contract DNAController is Ownable, Permit2Helper {

    DNARouter public dnaRouter;
    address public dnaTokenAddr;

    // Mapping to track taxable tokens
    mapping(address => bool) public isTaxable;

    // Errors
    error ZeroAddress();
    error InvalidFeePercent();
    error InvalidFeeToken();
    error InvalidTokenAddresses();
    error InvalidTaxableTokens();
    error InvalidFeeAmount();

    /// @dev Constructor to set the base aggregator contract address
    /// @param _dnaRouter Address of the BaseAggregator contract
    constructor(ISignatureTransfer permit2, DNARouter _dnaRouter, address _dnaTokenAddr) Ownable(_msgSender()) Permit2Helper(permit2) {
        dnaRouter = _dnaRouter;
        dnaTokenAddr = _dnaTokenAddr;
        isTaxable[_dnaTokenAddr] = true;
    }

    // Configure taxable tokens
    function setTaxableToken(address _tokenAddr, bool _taxable) external onlyOwner {
        if (_tokenAddr == address(0)) {
            revert ZeroAddress();
        }
        isTaxable[_tokenAddr] = _taxable;
    }

    function isTaxableToken(address _tokenAddr) public view returns (bool) {
        return isTaxable[_tokenAddr];
    }

    // set DNA Router Address
    function setDNARouterAddress(DNARouter _dnaRouter) external onlyOwner {
        require (address(_dnaRouter) != address(0), "ZeroAddress");
        dnaRouter = _dnaRouter;
    }

    /// @notice Executes the respective swap function in the BaseAggregator based on input parameters
    function executeSwap(
        Common.ExecuteSwapParam memory executeSwapParam
    ) external payable {

        // validations
        // should be called only in taxable Tokens
        if (!isTaxable[executeSwapParam.sellTokenAddress] && !isTaxable[executeSwapParam.buyTokenAddress]) {
            revert InvalidTaxableTokens();
        }
        
        if (executeSwapParam.sellTokenAddress == address(dnaTokenAddr) || executeSwapParam.buyTokenAddress == address(dnaTokenAddr)) {
            executeSwapParam.feeAmount = 0;
        }

        if (executeSwapParam.sellTokenAddress != address(0)) {

            address recipient = address(dnaRouter);
            uint256 sellTokenBalanceBefore;
            uint256 transferredSellAmount = executeSwapParam.sellAmount;

            if (isTaxable[executeSwapParam.sellTokenAddress]) {
                recipient = address(this);
                sellTokenBalanceBefore = ERC20(executeSwapParam.sellTokenAddress).balanceOf(address(this));
            }

            // Use Permit2 for token approval and transfer
            permit2.permitTransferFrom(
                ISignatureTransfer.PermitTransferFrom({
                    permitted: ISignatureTransfer.TokenPermissions({ token: executeSwapParam.sellTokenAddress, amount: executeSwapParam.sellAmount }),
                    nonce: executeSwapParam.permit.nonce,
                    deadline: executeSwapParam.permit.deadline
                }),
                ISignatureTransfer.SignatureTransferDetails({ to: recipient, requestedAmount: executeSwapParam.sellAmount }),
                msg.sender,
                executeSwapParam.permit.signature
            );

            // Todo: testnet 
            // ERC20(executeSwapParam.sellTokenAddress).transferFrom(msg.sender, recipient, executeSwapParam.sellAmount);

            // If taxable, transfer remaining balance to dnaRouter
            if (isTaxable[executeSwapParam.sellTokenAddress]) {

                transferredSellAmount = ERC20(executeSwapParam.sellTokenAddress).balanceOf(address(this)) - sellTokenBalanceBefore;

                if (transferredSellAmount > 0) {
                    SafeTransferLib.safeTransfer(
                        ERC20(executeSwapParam.sellTokenAddress),
                        address(dnaRouter),
                        transferredSellAmount
                    );
                }
            }

            // If both sellToken and buyToken are specified, call token-to-token function
            if (executeSwapParam.buyTokenAddress != address(0)) {

                // Track buy token balance before the swap
                uint256 buyTokenBalanceBefore = ERC20(executeSwapParam.buyTokenAddress).balanceOf(address(this));
                executeSwapParam.sellAmount = transferredSellAmount;

                if (executeSwapParam.intermediateTokenAddress == address(0)) {
                    dnaRouter.fillQuoteTokenToToken(
                        executeSwapParam,
                        msg.sender
                    );
                } else {
                    dnaRouter.fillQuoteTokenToTokenViaToken(
                        executeSwapParam,
                        msg.sender
                    );
                }

                // If taxable, transfer remaining balance to dnaRouter
                if (isTaxable[executeSwapParam.buyTokenAddress] ) {

                    // Calculate the actual tokens received from the swap and transfer swapped buytoken back to userAddress
                    uint256 receivedBuyTokenAmount = ERC20(executeSwapParam.buyTokenAddress).balanceOf(address(this)) - buyTokenBalanceBefore;
                    if (receivedBuyTokenAmount > 0) {
                        SafeTransferLib.safeTransfer(
                            ERC20(executeSwapParam.buyTokenAddress), 
                            address(msg.sender), 
                            receivedBuyTokenAmount
                        );
                    }
                }
            }
            else { 
                // If buyToken is address(0), call token-to-ETH function
                // feeToken will be 5e15 for 0.5%
                dnaRouter.fillQuoteTokenToEth(
                    executeSwapParam.sellTokenAddress,
                    executeSwapParam.target,
                    executeSwapParam.swapCallData,
                    transferredSellAmount,
                    executeSwapParam.feeAmount,
                    msg.sender,
                    executeSwapParam.permit
                );
            } 
        }
        else {

            // Track buy token balance before the swap
            uint256 buyTokenBalanceBefore = ERC20(executeSwapParam.buyTokenAddress).balanceOf(address(this));

            // If sellToken is address(0), call ETH-to-token function
            dnaRouter.fillQuoteEthToToken{value: msg.value}(
                executeSwapParam.buyTokenAddress,
                executeSwapParam.target,
                executeSwapParam.swapCallData,
                executeSwapParam.feeAmount,
                msg.sender
            );

            // Calculate the actual tokens received from the swap and transfer swapped buytoken back to userAddress
            uint256 receivedBuyTokenAmount = ERC20(executeSwapParam.buyTokenAddress).balanceOf(address(this)) - buyTokenBalanceBefore;
            if (receivedBuyTokenAmount > 0) {
                SafeTransferLib.safeTransfer(
                    ERC20(executeSwapParam.buyTokenAddress), 
                    address(msg.sender), 
                    receivedBuyTokenAmount
                );
            }

        }
    }

    receive() external payable {}

    /// @dev method to withdraw ERC20 tokens (from the fees)
    /// @param token address of the token to withdraw
    /// @param to address that's receiving the tokens
    /// @param amount amount of tokens to withdraw
    function withdrawToken(address token, address to, uint256 amount) external onlyOwner {
        require(to != address(0), "ZERO_ADDRESS");
        SafeTransferLib.safeTransfer(ERC20(token), to, amount);
    }

    /// @dev method to withdraw ETH (from the fees)
    /// @param to address that's receiving the ETH
    /// @param amount amount of ETH to withdraw
    function withdrawEth(address to, uint256 amount) external onlyOwner {
        require(to != address(0), "ZERO_ADDRESS");
        SafeTransferLib.safeTransferETH(to, amount);
    }
}

File 2 of 17 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

File 3 of 17 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

File 4 of 17 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

    uint256 private _status;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    constructor() {
        _status = NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        _status = ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}

File 5 of 17 : BaseAggregator.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import { SafeTransferLib } from "solmate/src/utils/SafeTransferLib.sol";
import { ERC20 } from "solmate/src/tokens/ERC20.sol";
import { Permit2, Permit2Helper } from "./Permit2Helper.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import { ISignatureTransfer } from "./interfaces/ISignatureTransfer.sol";
import { IDNAController } from "./interfaces/IDNAController.sol";
import { IWETH } from "./interfaces/IWETH.sol";
import "./interfaces/IUniswapV2Router02.sol";
import "./interfaces/IUniswapV3Router02.sol";
import "./interfaces/IDNAStaking.sol";
import "./lib/Common.sol";

contract BaseAggregator is Permit2Helper, ReentrancyGuard {

    // Struct to store token data
    struct TokenData {
        bool isAllowed; 
        uint256 balance;
    }

    // dnaController Address
    address public dnaController;
    // UniswapV2 Router Address
    address public uniswapV2RouterAddr;
    // UniswapV2 Router Address
    address public uniswapV3RouterAddr;

    // dna Staking contract address
    address public dnaStakingAddr;

    // company wallet address
    address public companyWalletAddr;
    
    // amount of weth as threshold for staking reward distribution (0.1 WETH)
    // Todo: 
    // uint256 public stakingRewardThreshold =  10**6;
    uint256 public stakingRewardThreshold =  10**17;
    // e.g. for 0.2%, value should be 20. For 20%, value should be 2000
    uint16 public companyFeePercentage;

    uint256 public fixedTokenAmount = 1000000;

    // Store the fee balances and isAllowed for the tracked assets to distribute as staking reward
    mapping(address => TokenData) public feeTokenData;

    event FillQuoteTokenToToken(
        address indexed sellToken,
        address indexed buyToken,
        address indexed user,
        address target,
        uint256 amountSold,
        uint256 amountBought,
        FeeToken feeToken,
        uint256 feeAmount
    );
    event FillQuoteTokenToEth(
        address indexed sellToken,
        address indexed user,
        address target,
        uint256 amountSold,
        uint256 amountBought,
        uint256 feeAmount
    );
    event FillQuoteEthToToken(
        address indexed buyToken,
        address indexed user,
        address target,
        uint256 amountSold,
        uint256 amountBought,
        uint256 feeAmount
    );
    event FillQuoteTokenToTokenViaToken(
        address indexed sellToken,
        address indexed intermediateToken,
        address indexed buyToken,
        address user,
        address target,
        address nextTarget,
        uint256 amountSold,
        uint256 amountBought,
        FeeToken feeToken,
        uint256 feeAmount
    );

    event StakeTriggered(
        address indexed feeToken, 
        uint256 distributionAmount,
        uint256 companyFeeAmount
    );

    /// @dev Set of allowed swapTargets.
    mapping(address => bool) public swapTargets;

    /// @dev modifier that ensures only approved targets can be called
    modifier onlyApprovedTarget(address target) {
        require(swapTargets[target], "TARGET_NOT_AUTH");
        _;
    }

    constructor(ISignatureTransfer permit2) Permit2Helper(permit2) { }

    /// @param buyTokenAddress the address of token that the user should receive
    /// @param target the address of the aggregator contract that will exec the swap
    /// @param swapCallData the memory that will be passed to the aggregator contract
    /// @param feeAmount the amount of ETH that we will take as a fee
    /// @param userAddress the address in which at last swapped token need to transfer
    function fillQuoteEthToToken(
        address buyTokenAddress,
        address payable target,
        bytes memory swapCallData,
        uint256 feeAmount,
        address userAddress
    )
        external
        payable
        nonReentrant
        onlyApprovedTarget(target)
    {

        // Validations
        if (msg.sender == dnaController) {
            require(
                (
                    IDNAController(dnaController).isTaxableToken(buyTokenAddress)
                ),
                "Unauthorized"
            );
        } else {
            require(
                (
                    !IDNAController(dnaController).isTaxableToken(buyTokenAddress)
                ),
                "Unauthorized"
            );
        }

        // 1 - Get the initial balances
        uint256 initialTokenBalance = ERC20(buyTokenAddress).balanceOf(address(this));
        uint256 initialEthAmount = address(this).balance - msg.value;
        uint256 sellAmount = msg.value - feeAmount;

        // 2 - Call the encoded swap function call on the contract at `target`,
        // passing along any ETH attached to this function call to cover protocol fees
        // minus our fees, which are kept in this contract
        _performSwap(target, swapCallData, sellAmount);

        // 3 - Make sure we received the tokens
        uint256 finalTokenBalance = ERC20(buyTokenAddress).balanceOf(address(this));
        require(initialTokenBalance < finalTokenBalance, "NO_TOKENS");

        // 4 - Send the received tokens back to the user
        uint256 tokensToSend = finalTokenBalance - initialTokenBalance;
        address toAddr = IDNAController(dnaController).isTaxableToken(buyTokenAddress) ? dnaController : userAddress;
        SafeTransferLib.safeTransfer(ERC20(buyTokenAddress), toAddr, tokensToSend);

        // 5 - Return the remaining ETH to the user (if any)
        {
            uint256 finalEthAmount = address(this).balance - feeAmount;
            if (finalEthAmount > initialEthAmount) {
                uint256 ethDiff = finalEthAmount - initialEthAmount;
                SafeTransferLib.safeTransferETH(userAddress, ethDiff);
                sellAmount -= ethDiff; // We don't want to include refund amount in the sellAmount when emitting event
            }
        }

        emit FillQuoteEthToToken(buyTokenAddress, target, userAddress, sellAmount, tokensToSend, feeAmount);
    }

    function fillQuoteTokenToToken(
        Common.ExecuteSwapParam calldata executeSwapParam,
        address userAddress
    )
        external
        payable
        nonReentrant
        onlyApprovedTarget(executeSwapParam.target)
    {

        require(executeSwapParam.intermediateTokenAddress == address(0), "INVALID_INTERMEDIATE_TOKEN");

        // 1 - Get the initial output token balance
        uint256 initialOutputTokenAmount = ERC20(executeSwapParam.buyTokenAddress).balanceOf(address(this));

        // 2 - Move the tokens to this contract (which includes our fees)
        if (msg.sender == dnaController) {
            // Validations
            require(
                (
                    IDNAController(dnaController).isTaxableToken(executeSwapParam.sellTokenAddress) || 
                    IDNAController(dnaController).isTaxableToken(executeSwapParam.buyTokenAddress) 
                ),
                "Unauthorized"
            );
        } else {
            require(
                (
                    (!IDNAController(dnaController).isTaxableToken(executeSwapParam.sellTokenAddress) &&
                    !IDNAController(dnaController).isTaxableToken(executeSwapParam.buyTokenAddress))
                ),
                "Unauthorized"
            );
            executePermitTransfer(executeSwapParam.sellTokenAddress, executeSwapParam.sellAmount, executeSwapParam.permit);
        }

        

        // 3 - Approve the aggregator's contract to swap the tokens if needed
        uint256 tokensToSwap = executeSwapParam.feeToken == FeeToken.INPUT ? executeSwapParam.sellAmount - executeSwapParam.feeAmount : executeSwapParam.sellAmount;
        SafeTransferLib.safeApprove(ERC20(executeSwapParam.sellTokenAddress), executeSwapParam.target, tokensToSwap);

        // 4 - Call the encoded swap function call on the contract at `target`,
        _performSwap(executeSwapParam.target, executeSwapParam.swapCallData, msg.value);

        // 5 - Check that the tokens were fully spent during the swap
        uint256 allowance = ERC20(executeSwapParam.sellTokenAddress).allowance(address(this), executeSwapParam.target);
        require(allowance == 0, "ALLOWANCE_NOT_ZERO");

        // 6 - Make sure we received the tokens
        uint256 finalOutputTokenAmount = ERC20(executeSwapParam.buyTokenAddress).balanceOf(address(this));
        require(initialOutputTokenAmount < finalOutputTokenAmount, "NO_TOKENS");

        // 7 - Send tokens to the user
        uint256 tokensDiff = finalOutputTokenAmount - initialOutputTokenAmount;
        uint256 tokensToSend = executeSwapParam.feeToken == FeeToken.OUTPUT ? tokensDiff - executeSwapParam.feeAmount : tokensDiff;

        // Process collected fee as staking reward
        address feeTokenAddr = executeSwapParam.feeToken == FeeToken.INPUT ? executeSwapParam.sellTokenAddress : executeSwapParam.buyTokenAddress;
        if (executeSwapParam.feeAmount > 0 && feeTokenData[feeTokenAddr].isAllowed) {
            checkStakeForDistribution(feeTokenAddr, executeSwapParam.feeAmount);
        }

        address toAddr = IDNAController(dnaController).isTaxableToken(executeSwapParam.buyTokenAddress) ? dnaController : userAddress;
        SafeTransferLib.safeTransfer(ERC20(executeSwapParam.buyTokenAddress), toAddr, tokensToSend);

        emit FillQuoteTokenToToken(
            executeSwapParam.sellTokenAddress, 
            executeSwapParam.buyTokenAddress, 
            userAddress, 
            executeSwapParam.target, 
            tokensToSwap, 
            tokensToSend, 
            executeSwapParam.feeToken, 
            executeSwapParam.feeAmount
        );
    }

    /// @dev method that executes ERC20 to ETH token swaps with the ability to take a fee from the output
    /// @param sellTokenAddress the address of token that the user is selling
    /// @param target the address of the aggregator contract that will exec the swap
    /// @param swapCallData the memory that will be passed to the aggregator contract
    /// @param sellAmount the amount of tokens that the user is selling
    /// @param feePercentage the amount of ETH that we will take as a fee with 1e18 precision
    /// @param userAddress the address in which at last swapped token need to transfer
    /// @param permit struct containing the nonce, deadline, v, r and s values of the permit data
    function fillQuoteTokenToEth(
        address sellTokenAddress,
        address payable target,
        bytes memory swapCallData,
        uint256 sellAmount,
        uint256 feePercentage,
        address userAddress,
        Permit2 memory permit
    )
        external
        payable
        nonReentrant
        onlyApprovedTarget(target)
    {
        // 1 - Get the initial ETH amount
        uint256 initialEthAmount = address(this).balance - msg.value;
        address wethAddr = IUniswapV2Router02(uniswapV2RouterAddr).WETH();
        uint256 initialWethBalance = ERC20(wethAddr).balanceOf(address(this));

        // 2 - Move the tokens to this contract
        if (msg.sender == dnaController) {
            // Validations
            require(
                (
                    IDNAController(dnaController).isTaxableToken(sellTokenAddress)
                ),
                "Unauthorized"
            );
        } else {
            require(
                (
                    !IDNAController(dnaController).isTaxableToken(sellTokenAddress)
                ),
                "Unauthorized"
            );
            executePermitTransfer(sellTokenAddress, sellAmount, permit);
        }

        // 3 - Approve the aggregator's contract to swap the tokens
        SafeTransferLib.safeApprove(ERC20(sellTokenAddress), target, sellAmount);

        // 4 - Call the encoded swap function call on the contract at `target`,
        _performSwap(target, swapCallData, msg.value);

        // 5 - Check that the tokens were fully spent during the swap
        uint256 allowance = ERC20(sellTokenAddress).allowance(address(this), target);
        require(allowance == 0, "ALLOWANCE_NOT_ZERO");

        // 6 - Convert only the newly received WETH to ETH
        if (target == uniswapV3RouterAddr) {
            
            uint256 wethReceived = ERC20(wethAddr).balanceOf(address(this)) - initialWethBalance;

            if (wethReceived > 0) {
                IWETH(wethAddr).withdraw(wethReceived); // Unwrap only the received WETH to ETH
            }
        }

        // 7 - Subtract the fees and send the rest to the user
        // Fees will be held in this contract
        uint256 finalEthAmount = address(this).balance;
        uint256 ethDiff = finalEthAmount - initialEthAmount;

        require(ethDiff > 0, "NO_ETH_BACK");

        uint256 fees;
        uint256 ethToSend;
        if (feePercentage > 0 && sellTokenAddress != IDNAController(dnaController).dnaTokenAddr()) {
            fees = (ethDiff * feePercentage) / 1e18;
            ethToSend = ethDiff - fees;
            SafeTransferLib.safeTransferETH(userAddress, ethToSend);
            // when there's no fee, 1inch sends the funds directly to the user
            // we check to prevent sending 0 ETH in that case
        } else if (ethDiff > 0) {
            ethToSend = ethDiff;
            SafeTransferLib.safeTransferETH(userAddress, ethToSend);
        }

        emit FillQuoteTokenToEth(sellTokenAddress, userAddress, target, sellAmount, ethToSend, fees);
    }

    function fillQuoteTokenToTokenViaToken(
        Common.ExecuteSwapParam calldata executeSwapParam,
        address userAddress
    ) external payable nonReentrant {
        require(executeSwapParam.intermediateTokenAddress != address(0), "INVALID_INTERMEDIATE_TOKEN");
        require(!IDNAController(dnaController).isTaxableToken(executeSwapParam.intermediateTokenAddress),"Unauthorized");

        // 1 - Get the initial output token balance
        uint256 initialOutputTokenAmount = ERC20(executeSwapParam.intermediateTokenAddress).balanceOf(address(this));

        // 2 - Move the tokens to this contract (which includes our fees)
        if (msg.sender == dnaController) {
            // Validations
            require(
                (
                    IDNAController(dnaController).isTaxableToken(executeSwapParam.sellTokenAddress) || 
                    IDNAController(dnaController).isTaxableToken(executeSwapParam.buyTokenAddress) 
                ),
                "Unauthorized"
            );
        } else {
            require(
                (
                    (!IDNAController(dnaController).isTaxableToken(executeSwapParam.sellTokenAddress) &&
                    !IDNAController(dnaController).isTaxableToken(executeSwapParam.buyTokenAddress))
                ),
                "Unauthorized"
            );
            executePermitTransfer(executeSwapParam.sellTokenAddress, executeSwapParam.sellAmount, executeSwapParam.permit);
        }

        // 3 - Approve the aggregator's contract to swap the tokens if needed
        uint256 tokensToSwap = executeSwapParam.feeToken == FeeToken.INPUT ? executeSwapParam.sellAmount - executeSwapParam.feeAmount : executeSwapParam.sellAmount;
        SafeTransferLib.safeApprove(ERC20(executeSwapParam.sellTokenAddress), executeSwapParam.target, tokensToSwap);

        // 4 - Call the encoded swap function call on the contract at `target`,
        _performSwap(executeSwapParam.target, executeSwapParam.swapCallData, msg.value);

        // 5 - Check that the tokens were fully spent during the swap
        uint256 allowance = ERC20(executeSwapParam.sellTokenAddress).allowance(address(this), executeSwapParam.target);
        require(allowance == 0, "ALLOWANCE_NOT_ZERO");

        // 6 - Make sure we received the tokens
        uint256 finalOutputTokenAmount = ERC20(executeSwapParam.intermediateTokenAddress).balanceOf(address(this));
        require(initialOutputTokenAmount < finalOutputTokenAmount, "NO_TOKENS");

        // 7 - Send tokens to the user
        uint256 tokensDiff = finalOutputTokenAmount - initialOutputTokenAmount;
        uint256 tokensToSend = executeSwapParam.feeToken == FeeToken.OUTPUT ? tokensDiff - executeSwapParam.feeAmount : tokensDiff;

        // Process collected fee as staking reward
        address feeTokenAddr = executeSwapParam.feeToken == FeeToken.INPUT ? executeSwapParam.sellTokenAddress : executeSwapParam.buyTokenAddress;
        if (executeSwapParam.feeAmount > 0 && feeTokenData[feeTokenAddr].isAllowed) {
            checkStakeForDistribution(feeTokenAddr, executeSwapParam.feeAmount);
        }

        // second swap

        // 8 - create SwapCallData for the next swap
        address routerAddress = executeSwapParam.target == uniswapV2RouterAddr ? uniswapV3RouterAddr : uniswapV2RouterAddr;
        bytes memory nextSwapCallData = createSwapCallData(
            routerAddress, 
            executeSwapParam.pathV2,
            executeSwapParam.pathV3,
            tokensToSend
        );

        // 9 - Get the initial output token balance
        initialOutputTokenAmount = ERC20(executeSwapParam.buyTokenAddress).balanceOf(address(this));
        
        // 10 - Approve the aggregator's contract to swap the tokens if needed
        address nextTarget = executeSwapParam.target == uniswapV2RouterAddr ? uniswapV3RouterAddr : uniswapV2RouterAddr;
        SafeTransferLib.safeApprove(ERC20(executeSwapParam.intermediateTokenAddress), nextTarget, tokensToSend);

        // 11 - Call the encoded swap function call on the contract at `target`,
        _performSwap(nextTarget, nextSwapCallData, msg.value);

        // 12 - Check that the tokens were fully spent during the swap
        allowance = ERC20(executeSwapParam.intermediateTokenAddress).allowance(address(this), nextTarget);
        require(allowance == 0, "ALLOWANCE_NOT_ZERO");

        // 13 - Make sure we received the tokens
        finalOutputTokenAmount = ERC20(executeSwapParam.buyTokenAddress).balanceOf(address(this));
        require(initialOutputTokenAmount < finalOutputTokenAmount, "NO_TOKENS");

        tokensDiff = finalOutputTokenAmount - initialOutputTokenAmount;
        tokensToSend = tokensDiff;

        // 14 - Send tokens to the user
        address toAddr = IDNAController(dnaController).isTaxableToken(executeSwapParam.buyTokenAddress) ? dnaController : userAddress;
        SafeTransferLib.safeTransfer(ERC20(executeSwapParam.buyTokenAddress), toAddr, tokensToSend);

        emit FillQuoteTokenToTokenViaToken(
            executeSwapParam.sellTokenAddress, 
            executeSwapParam.intermediateTokenAddress,
            executeSwapParam.buyTokenAddress, 
            userAddress, 
            executeSwapParam.target, 
            nextTarget,
            tokensToSwap, 
            tokensToSend, 
            executeSwapParam.feeToken, 
            executeSwapParam.feeAmount
        );
       
    }

    function createSwapCallData(
        address routerAddress,
        address[] memory pathV2,
        bytes memory pathV3,
        uint256 amountIn
    ) public view returns (bytes memory) {
        if (routerAddress == uniswapV3RouterAddr) {
            // Create swapCallData for V3 router
            IUniswapV3Router02.ExactInputParams memory params = IUniswapV3Router02.ExactInputParams({
                path: pathV3,
                recipient: address(this),
                amountIn: amountIn,
                amountOutMinimum: 0
            });

            return abi.encodeWithSelector(
                IUniswapV3Router02.exactInput.selector,
                params
            );
        } else {
            // Create swapCallData for V2 router
            return abi.encodeWithSelector(
                IUniswapV2Router02.swapExactTokensForTokensSupportingFeeOnTransferTokens.selector,
                amountIn, 
                0,       
                pathV2,     
                address(this), 
                block.timestamp + 5 minutes
            );
        }
    }


    function executePermitTransfer(
        address token,
        uint256 amount,
        Permit2 memory permit
    ) internal {
        permit2.permitTransferFrom(
            ISignatureTransfer.PermitTransferFrom({
                permitted: ISignatureTransfer.TokenPermissions({ token: token, amount: amount }),
                nonce: permit.nonce,
                deadline: permit.deadline
            }),
            ISignatureTransfer.SignatureTransferDetails({ to: address(this), requestedAmount: amount }),
            msg.sender,
            permit.signature
        );
        // Todo: testnet
        // ERC20(token).transferFrom(msg.sender, address(this), amount);
    }

    // Function to trigger reward distribution in Staking contract
    function checkStakeForDistribution(address feeToken, uint256 feeAmount) internal {

        feeTokenData[feeToken].balance += feeAmount;

        uint256 companyFeeAmount = (feeTokenData[feeToken].balance * companyFeePercentage) / 10000; // 20% for company
        uint256 distributionAmount = feeTokenData[feeToken].balance - companyFeeAmount; // 80% for stake Reward

        // need to check buy calling getAmountsOut to get the output in weth
        uint256 feeInWETHToken = feeToken == IUniswapV2Router02(uniswapV2RouterAddr).WETH() ? distributionAmount : getFeesInWETHFromToken(feeToken, feeTokenData[feeToken].balance);

        // Check if buyback condition is met
        if (feeInWETHToken >= stakingRewardThreshold) {

            ERC20(feeToken).approve(dnaStakingAddr, distributionAmount);

            // distribute the reward in staking
            IDNAStaking(dnaStakingAddr).notifyRewardAmount(feeToken, distributionAmount);

            // Check that the tokens were fully distibuted in staking
            uint256 allowance = ERC20(feeToken).allowance(address(this), dnaStakingAddr);
            require(allowance == 0, "ALLOWANCE_NOT_ZERO");

            feeTokenData[feeToken].balance = 0;

            SafeTransferLib.safeTransfer(ERC20(feeToken), companyWalletAddr, companyFeeAmount);

            emit StakeTriggered(feeToken, distributionAmount, companyFeeAmount);

        }
    }

    // Function to get the equivalent Weth value for a given fee token
    function getFeesInWETHFromToken(address tokenAddress, uint256 tokenAmount) internal view returns (uint256) {

        // Fetch the price of the fee token in WETH via fixed amount
        uint[] memory tokenPriceInWETH = IUniswapV2Router02(uniswapV2RouterAddr).getAmountsOut(
            fixedTokenAmount, 
            getPathForTokenToWETH(tokenAddress)
        );

        // The equivalent WETH value for the fee amount
        return (tokenPriceInWETH[1]*tokenAmount)/fixedTokenAmount;
    }

    function getPathForTokenToWETH(address tokenAddress)
    internal
    view
    returns (address[] memory)
    {
        address[] memory path = new address[](2);
        // If token is not WETH, path is Token -> WETH address
        path[0] = tokenAddress;
        path[1] = IUniswapV2Router02(uniswapV2RouterAddr).WETH();

        return path;
    }

    function _performSwap(address target, bytes memory swapCallData, uint256 amount ) private {
        (bool success, bytes memory res) = target.call{ value: amount }(swapCallData);
        if (!success) {
            assembly {
                let returndata_size := mload(res)
                revert(add(32, res), returndata_size)
            }
        }
    }

}

File 6 of 17 : DNARouter.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import { SafeTransferLib } from "solmate/src/utils/SafeTransferLib.sol";
import { ERC20 } from "solmate/src/tokens/ERC20.sol";
import { BaseAggregator } from "./BaseAggregator.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { ISignatureTransfer } from "./interfaces/ISignatureTransfer.sol";

contract DNARouter is BaseAggregator, Ownable {
    /// @dev Event emitted when a swap target gets added
    event SwapTargetAdded(address indexed target);

    /// @dev Event emitted when a swap target gets removed
    event SwapTargetRemoved(address indexed target);

    /// @dev Event emitted when token fees are withdrawn
    event TokenWithdrawn(address indexed token, address indexed target, uint256 amount);

    /// @dev Event emitted when ETH fees are withdrawn
    event EthWithdrawn(address indexed target, uint256 amount);

    constructor(
        address[] memory _swapTargets,
        ISignatureTransfer _permit2,
        address _uniswapV3routerAddr,
        address _companyWallet,
        address _uniswapV2RouterAddr
    )
        Ownable(_msgSender())
        BaseAggregator(_permit2)
    {
        for (uint256 i = 0; i < _swapTargets.length; i++) {
            swapTargets[_swapTargets[i]] = true;
        }
        uniswapV2RouterAddr = _uniswapV2RouterAddr;
        uniswapV3RouterAddr = _uniswapV3routerAddr;
        companyWalletAddr = _companyWallet;
        companyFeePercentage = 2000; // 20%
    }

    /// @dev We don't want to accept any ETH, except refunds from aggregators
    /// or the owner (for testing purposes), which can also withdraw
    /// This is done by evaluating the value of status, which is set to 2
    /// only during swaps due to the "nonReentrant" modifier
    receive() external payable {
        require(_reentrancyGuardEntered() || msg.sender == owner(), "NO_RECEIVE");
    }

    // set DNAController Address
    function setDnaController(address _dnaController) external onlyOwner {
        require (_dnaController != address(0), "ZeroAddress");
        dnaController = _dnaController;
    }

    // set DNAStaking Address
    function setDNAStakingAddress(address _dnaStakingAddr) external onlyOwner {
        require (_dnaStakingAddr != address(0), "ZeroAddress");
        dnaStakingAddr = _dnaStakingAddr;
    }

    // set company Fee Percentage
    function setCompanyFeePercentage(uint16 newValue) external onlyOwner {
        // newValue should not be greater then 10000(100%)
        require(newValue <= 10000, "InvalidFeeValue" ) ;
        companyFeePercentage = newValue;
    }

    // set Staking Reward Threshold
    function setStakingRewardThreshold(uint256 _stakingRewardThreshold) external onlyOwner {
        require (_stakingRewardThreshold > 0, "Invalid Value");
        stakingRewardThreshold = _stakingRewardThreshold;
    }

    // Function to set the allowed status of a token (only callable by admin/owner)
    function setFeeTokenData(address tokenAddress, bool isAllowed) external onlyOwner {
        feeTokenData[tokenAddress].isAllowed = isAllowed;
    }

    // Function to set balance of the allowed token (only callable by admin/owner)
    function sponsorTokens(address tokenAddress, uint256 amount) external {
        require(feeTokenData[tokenAddress].isAllowed, "Invalid Fee Token");
        ERC20(tokenAddress).transferFrom(msg.sender, address(this), amount);
        feeTokenData[tokenAddress].balance = amount;
    }

    // set Fixed Token Amount
    function setFixedTokenAmount(uint256 _fixedTokenAmount) external onlyOwner {
        require (_fixedTokenAmount > 0, "Invalid Value");
        fixedTokenAmount = _fixedTokenAmount;
    }

    /// @dev method to add or remove swap targets from swapTargets
    /// This is required so we only approve "trusted" swap targets
    /// to transfer tokens out of this contract
    /// @param target address of the swap target to add
    /// @param add flag to add or remove the swap target
    function updateSwapTargets(address target, bool add) external onlyOwner {
        swapTargets[target] = add;
        if (add) {
            emit SwapTargetAdded(target);
        } else {
            emit SwapTargetRemoved(target);
        }
    }

    /// @dev method to withdraw ERC20 tokens (from the fees)
    /// @param token address of the token to withdraw
    /// @param to address that's receiving the tokens
    /// @param amount amount of tokens to withdraw
    function withdrawToken(address token, address to, uint256 amount) external onlyOwner {
        require(to != address(0), "ZERO_ADDRESS");
        SafeTransferLib.safeTransfer(ERC20(token), to, amount);
        emit TokenWithdrawn(token, to, amount);
    }

    /// @dev method to withdraw ETH (from the fees)
    /// @param to address that's receiving the ETH
    /// @param amount amount of ETH to withdraw
    function withdrawEth(address to, uint256 amount) external onlyOwner {
        require(to != address(0), "ZERO_ADDRESS");
        SafeTransferLib.safeTransferETH(to, amount);
        emit EthWithdrawn(to, amount);
    }
}

File 7 of 17 : IDNAController.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

interface IDNAController {
    function isTaxableToken(address _tokenAddr) external view returns (bool);
    function dnaTokenAddr() external view returns (address);
}

File 8 of 17 : IDNAStaking.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

interface IDNAStaking {
    function stakeEvolutionRewards( address _staker, uint256 amount ) external;
    function notifyRewardAmount(
        address _rewardsToken,
        uint256 reward
    ) external; 
}

File 9 of 17 : IEIP712.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

interface IEIP712 {
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 10 of 17 : ISignatureTransfer.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import {IEIP712} from "./IEIP712.sol";

/// @title SignatureTransfer
/// @notice Handles ERC20 token transfers through signature based actions
/// @dev Requires user's token approval on the Permit2 contract
interface ISignatureTransfer is IEIP712 {
    /// @notice Thrown when the requested amount for a transfer is larger than the permissioned amount
    /// @param maxAmount The maximum amount a spender can request to transfer
    error InvalidAmount(uint256 maxAmount);

    /// @notice Thrown when the number of tokens permissioned to a spender does not match the number of tokens being transferred
    /// @dev If the spender does not need to transfer the number of tokens permitted, the spender can request amount 0 to be transferred
    error LengthMismatch();

    /// @notice Emits an event when the owner successfully invalidates an unordered nonce.
    event UnorderedNonceInvalidation(address indexed owner, uint256 word, uint256 mask);

    /// @notice The token and amount details for a transfer signed in the permit transfer signature
    struct TokenPermissions {
        // ERC20 token address
        address token;
        // the maximum amount that can be spent
        uint256 amount;
    }

    /// @notice The signed permit message for a single token transfer
    struct PermitTransferFrom {
        TokenPermissions permitted;
        // a unique value for every token owner's signature to prevent signature replays
        uint256 nonce;
        // deadline on the permit signature
        uint256 deadline;
    }

    /// @notice Specifies the recipient address and amount for batched transfers.
    /// @dev Recipients and amounts correspond to the index of the signed token permissions array.
    /// @dev Reverts if the requested amount is greater than the permitted signed amount.
    struct SignatureTransferDetails {
        // recipient address
        address to;
        // spender requested amount
        uint256 requestedAmount;
    }

    /// @notice Used to reconstruct the signed permit message for multiple token transfers
    /// @dev Do not need to pass in spender address as it is required that it is msg.sender
    /// @dev Note that a user still signs over a spender address
    struct PermitBatchTransferFrom {
        // the tokens and corresponding amounts permitted for a transfer
        TokenPermissions[] permitted;
        // a unique value for every token owner's signature to prevent signature replays
        uint256 nonce;
        // deadline on the permit signature
        uint256 deadline;
    }

    /// @notice A map from token owner address and a caller specified word index to a bitmap. Used to set bits in the bitmap to prevent against signature replay protection
    /// @dev Uses unordered nonces so that permit messages do not need to be spent in a certain order
    /// @dev The mapping is indexed first by the token owner, then by an index specified in the nonce
    /// @dev It returns a uint256 bitmap
    /// @dev The index, or wordPosition is capped at type(uint248).max
    function nonceBitmap(address, uint256) external view returns (uint256);

    /// @notice Transfers a token using a signed permit message
    /// @dev Reverts if the requested amount is greater than the permitted signed amount
    /// @param permit The permit data signed over by the owner
    /// @param owner The owner of the tokens to transfer
    /// @param transferDetails The spender's requested transfer details for the permitted token
    /// @param signature The signature to verify
    function permitTransferFrom(
        PermitTransferFrom memory permit,
        SignatureTransferDetails calldata transferDetails,
        address owner,
        bytes calldata signature
    ) external;

    /// @notice Transfers a token using a signed permit message
    /// @notice Includes extra data provided by the caller to verify signature over
    /// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition
    /// @dev Reverts if the requested amount is greater than the permitted signed amount
    /// @param permit The permit data signed over by the owner
    /// @param owner The owner of the tokens to transfer
    /// @param transferDetails The spender's requested transfer details for the permitted token
    /// @param witness Extra data to include when checking the user signature
    /// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash
    /// @param signature The signature to verify
    function permitWitnessTransferFrom(
        PermitTransferFrom memory permit,
        SignatureTransferDetails calldata transferDetails,
        address owner,
        bytes32 witness,
        string calldata witnessTypeString,
        bytes calldata signature
    ) external;

    /// @notice Transfers multiple tokens using a signed permit message
    /// @param permit The permit data signed over by the owner
    /// @param owner The owner of the tokens to transfer
    /// @param transferDetails Specifies the recipient and requested amount for the token transfer
    /// @param signature The signature to verify
    function permitTransferFrom(
        PermitBatchTransferFrom memory permit,
        SignatureTransferDetails[] calldata transferDetails,
        address owner,
        bytes calldata signature
    ) external;

    /// @notice Transfers multiple tokens using a signed permit message
    /// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition
    /// @notice Includes extra data provided by the caller to verify signature over
    /// @param permit The permit data signed over by the owner
    /// @param owner The owner of the tokens to transfer
    /// @param transferDetails Specifies the recipient and requested amount for the token transfer
    /// @param witness Extra data to include when checking the user signature
    /// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash
    /// @param signature The signature to verify
    function permitWitnessTransferFrom(
        PermitBatchTransferFrom memory permit,
        SignatureTransferDetails[] calldata transferDetails,
        address owner,
        bytes32 witness,
        string calldata witnessTypeString,
        bytes calldata signature
    ) external;

    /// @notice Invalidates the bits specified in mask for the bitmap at the word position
    /// @dev The wordPos is maxed at type(uint248).max
    /// @param wordPos A number to index the nonceBitmap at
    /// @param mask A bitmap masked against msg.sender's current bitmap at the word position
    function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) external;
}

File 11 of 17 : IUniswapV2Router02.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

interface IUniswapV2Router02 {
    function swapExactETHForTokens(
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external payable returns (uint[] memory amounts);
    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
    function factory() external pure returns (address);
    function WETH() external pure returns (address);
    function addLiquidityETH(
        address token,
        uint amountTokenDesired,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
    function getAmountsOut(
        uint amountIn,
        address[] calldata path
    ) external view returns (uint[] memory amounts);
    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
}

File 12 of 17 : IUniswapV3Router02.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

interface IUniswapV3Router02 {
    struct ExactInputParams {
        bytes path;
        address recipient;
        uint256 amountIn;
        uint256 amountOutMinimum;
    }
    function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);
}

File 13 of 17 : IWETH.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

interface IWETH {
    function deposit() external payable;
    function transfer(address to, uint value) external returns (bool);
    function withdraw(uint) external;
}

File 14 of 17 : Common.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.24;
import { Permit2, Permit2Helper } from "../Permit2Helper.sol";

enum FeeToken {
    INPUT,
    OUTPUT
}

library Common {
    
    struct ExecuteSwapParam {
        address sellTokenAddress;          // The token the user is selling (use address(0) for ETH)
        address intermediateTokenAddress;  // The token the user is swapping in between
        address buyTokenAddress;           // The token the user wants to buy (use address(0) for ETH)
        address payable target;            // The target aggregator contract address for swap
        bytes swapCallData;                // The calldata for the swap operation
        uint256 sellAmount;                // The amount of tokens/ETH to sell
        FeeToken feeToken;                 // The token to be used for the fee (ignored for ETH-related functions)
        uint256 feeAmount;                 // The amount of the tokens to sell that we will take as a fee
        Permit2 permit;                    // The permit details for the sellToken (only for token-based swaps)
        address[] pathV2;                  // Path for the second swap via intermediate token 
        bytes pathV3;                      // Bytes path with fee tier for V3
    }
}

File 15 of 17 : Permit2Helper.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import { ISignatureTransfer } from "./interfaces/ISignatureTransfer.sol";

struct Permit2 {
    uint256 nonce;
    uint256 deadline;
    bytes signature;
}

contract Permit2Helper {
    ISignatureTransfer public immutable permit2;

    constructor(ISignatureTransfer _permit2) {
        permit2 = _permit2;
    }
}

File 16 of 17 : ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 amount);

    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /*//////////////////////////////////////////////////////////////
                            METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*//////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(address => mapping(address => uint256)) public allowance;

    /*//////////////////////////////////////////////////////////////
                            EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*//////////////////////////////////////////////////////////////
                               ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*//////////////////////////////////////////////////////////////
                             EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                ),
                                owner,
                                spender,
                                value,
                                nonces[owner]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(address(0), to, amount);
    }

    function _burn(address from, uint256 amount) internal virtual {
        balanceOf[from] -= amount;

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }
}

File 17 of 17 : SafeTransferLib.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {ERC20} from "../tokens/ERC20.sol";

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
library SafeTransferLib {
    /*//////////////////////////////////////////////////////////////
                             ETH OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferETH(address to, uint256 amount) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and store if it succeeded or not.
            success := call(gas(), to, amount, 0, 0, 0, 0)
        }

        require(success, "ETH_TRANSFER_FAILED");
    }

    /*//////////////////////////////////////////////////////////////
                            ERC20 OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferFrom(
        ERC20 token,
        address from,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
            mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
            // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
            success := call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)

            // 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 and token has code.
            if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
                success := iszero(or(iszero(extcodesize(token)), returndatasize())) 
            }
        }

        require(success, "TRANSFER_FROM_FAILED");
    }

    function safeTransfer(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
            // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
            success := call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)

            // 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 and token has code.
            if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
                success := iszero(or(iszero(extcodesize(token)), returndatasize())) 
            }
        }

        require(success, "TRANSFER_FAILED");
    }

    function safeApprove(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
            // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
            success := call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)

            // 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 and token has code.
            if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
                success := iszero(or(iszero(extcodesize(token)), returndatasize())) 
            }
        }

        require(success, "APPROVE_FAILED");
    }
}

Settings
{
  "viaIR": true,
  "optimizer": {
    "enabled": true,
    "runs": 65535
  },
  "evmVersion": "paris",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"contract ISignatureTransfer","name":"permit2","type":"address"},{"internalType":"contract DNARouter","name":"_dnaRouter","type":"address"},{"internalType":"address","name":"_dnaTokenAddr","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidFeeAmount","type":"error"},{"inputs":[],"name":"InvalidFeePercent","type":"error"},{"inputs":[],"name":"InvalidFeeToken","type":"error"},{"inputs":[],"name":"InvalidTaxableTokens","type":"error"},{"inputs":[],"name":"InvalidTokenAddresses","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"dnaRouter","outputs":[{"internalType":"contract DNARouter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dnaTokenAddr","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"sellTokenAddress","type":"address"},{"internalType":"address","name":"intermediateTokenAddress","type":"address"},{"internalType":"address","name":"buyTokenAddress","type":"address"},{"internalType":"address payable","name":"target","type":"address"},{"internalType":"bytes","name":"swapCallData","type":"bytes"},{"internalType":"uint256","name":"sellAmount","type":"uint256"},{"internalType":"enum FeeToken","name":"feeToken","type":"uint8"},{"internalType":"uint256","name":"feeAmount","type":"uint256"},{"components":[{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct Permit2","name":"permit","type":"tuple"},{"internalType":"address[]","name":"pathV2","type":"address[]"},{"internalType":"bytes","name":"pathV3","type":"bytes"}],"internalType":"struct Common.ExecuteSwapParam","name":"executeSwapParam","type":"tuple"}],"name":"executeSwap","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isTaxable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddr","type":"address"}],"name":"isTaxableToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"permit2","outputs":[{"internalType":"contract ISignatureTransfer","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract DNARouter","name":"_dnaRouter","type":"address"}],"name":"setDNARouterAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddr","type":"address"},{"internalType":"bool","name":"_taxable","type":"bool"}],"name":"setTaxableToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawEth","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

60a03461011657601f611b9a38819003918201601f19168301916001600160401b0383118484101761011b578084926060946040528339810103126101165780516001600160a01b039182821682036101165760208101519083821680920361011657604001518381168091036101165733156100fd578060409360009384549160018060a01b031992338482161787558751983391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08880a360805281600154161760015560025416176002558152600360205220600160ff19825416179055611a68908161013282396080518181816105f601526114640152f35b604051631e4fbdf760e01b815260006004820152602490fd5b600080fd5b634e487b7160e01b600052604160045260246000fdfe6080604052600436101561001b575b361561001957600080fd5b005b6000803560e01c806301e336671461148857806312261ee71461141957806316eebebd146110d15780631b9a91a41461134857806320cbe712146112f65780633729c5ca146112a457806368f47877146111d5578063715018a614611139578063819906fe146110d15780638da5cb5b14611080578063adc855b814610289578063d70f82021461019c5763f2fde38b146100b6575061000e565b346101995760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610199576100ed611504565b6100f5611906565b73ffffffffffffffffffffffffffffffffffffffff80911690811561016857600054827fffffffffffffffffffffffff0000000000000000000000000000000000000000821617600055167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a380f35b602483604051907f1e4fbdf70000000000000000000000000000000000000000000000000000000082526004820152fd5b80fd5b50346101995760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101995760043573ffffffffffffffffffffffffffffffffffffffff8116809103610285576101f5611906565b8015610227577fffffffffffffffffffffffff0000000000000000000000000000000000000000600154161760015580f35b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f5a65726f416464726573730000000000000000000000000000000000000000006044820152fd5b5080fd5b5060207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101995767ffffffffffffffff6004351161019957610160807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc600435360301126102855760405190810181811067ffffffffffffffff8211176110535760405261031f600435600401611527565b815261032f602460043501611527565b6020820152610342604460043501611527565b60408201526064600435013573ffffffffffffffffffffffffffffffffffffffff8116810361104f5760608201526084600435013567ffffffffffffffff811161104f57610397906004369181350101611604565b608082015260043560a481013560a083015260c40135600281101561104f5760c082015260043560e481013560e0830152610104013567ffffffffffffffff811161104f576004350160607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc823603011261104f576040519061041982611548565b600481013582526024810135602083015260448101359067ffffffffffffffff8211610f4757600461044e9236920101611604565b6040820152610100820152610124600435013567ffffffffffffffff811161104f57366023826004350101121561104f5760048181350101359067ffffffffffffffff8211611022578160051b604051926104ac60208301856115c3565b8352602083019036602482856004350101011161101e57906024836004350101915b602481856004350101018310611002575050505061012082015267ffffffffffffffff6101446004350135116102855761051336600480356101448101350101611604565b61014082015273ffffffffffffffffffffffffffffffffffffffff8151168252600360205260ff6040832054161580610fd6575b610fac5773ffffffffffffffffffffffffffffffffffffffff81511673ffffffffffffffffffffffffffffffffffffffff60025416808214908115610f88575b50610f7d575b15610d5a5773ffffffffffffffffffffffffffffffffffffffff60015416828360a08401519273ffffffffffffffffffffffffffffffffffffffff8551168252600360205260ff604083205416610cbd575b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8651169060a087015160405192610641846115a7565b835260208301526101008701519060208251920151936040519361066485611548565b8452602084019283526040840194855260a089015173ffffffffffffffffffffffffffffffffffffffff6040519261069b846115a7565b168252602082015260406101008a01510151823b15610cb9578694610767869261074e61071a956040519a8b998a9889977f30f28b7a0000000000000000000000000000000000000000000000000000000089526004890190516020809173ffffffffffffffffffffffffffffffffffffffff81511684520151910152565b516044870152516064860152805173ffffffffffffffffffffffffffffffffffffffff1660848601526020015160a4850152565b3360c484015261010060e48401526101048301906116de565b03925af180156109cd57610ca5575b505073ffffffffffffffffffffffffffffffffffffffff8351168452600360205260ff604085205416610bbe575b506040820151839073ffffffffffffffffffffffffffffffffffffffff1615610a9c575073ffffffffffffffffffffffffffffffffffffffff60408301511690604051906020826024817f70a0823100000000000000000000000000000000000000000000000000000000968782523060048301525afa918215610a91578592610a5d575b5060a0840152602083015173ffffffffffffffffffffffffffffffffffffffff166109d8578373ffffffffffffffffffffffffffffffffffffffff60015416803b15610285578160405180927fe06949b00000000000000000000000000000000000000000000000000000000082528183816108a9338c600484016117a2565b03925af180156109cd576109b5575b50505b73ffffffffffffffffffffffffffffffffffffffff6040840151168452600360205260ff6040852054166108f1575b5050505b80f35b602073ffffffffffffffffffffffffffffffffffffffff6040850151169260246040518095819382523060048301525afa80156109aa578490610971575b610939925061173c565b80610945575b806108ea565b61096a9173ffffffffffffffffffffffffffffffffffffffff60403392015116611957565b388061093f565b506020823d6020116109a2575b8161098b602093836115c3565b8101031261099d57610939915161092f565b600080fd5b3d915061097e565b6040513d86823e3d90fd5b6109be90611593565b6109c95783386108b8565b8380fd5b6040513d84823e3d90fd5b8373ffffffffffffffffffffffffffffffffffffffff60015416803b15610285578160405180927f01aa94bb000000000000000000000000000000000000000000000000000000008252818381610a33338c600484016117a2565b03925af180156109cd57610a49575b50506108bb565b610a5290611593565b6109c9578338610a42565b9091506020813d602011610a89575b81610a79602093836115c3565b8101031261099d57519038610829565b3d9150610a6c565b6040513d87823e3d90fd5b919073ffffffffffffffffffffffffffffffffffffffff6001541673ffffffffffffffffffffffffffffffffffffffff8351169073ffffffffffffffffffffffffffffffffffffffff60608501511660808501519261010060e087015196015190833b15610bba57610b9788968793610b57996040519a8b998a9889977f7c965eed0000000000000000000000000000000000000000000000000000000089526004890152602488015260e0604488015260e48701906116de565b92606486015260848501523360a48501527ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8483030160c4850152611778565b03925af180156109cd57610baa57505080f35b610bb390611593565b6101995780f35b8780fd5b60249150602073ffffffffffffffffffffffffffffffffffffffff845116604051938480927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa80156109aa578490610c71575b610c25925061173c565b80610c31575b386107a4565b610c6c8173ffffffffffffffffffffffffffffffffffffffff84511673ffffffffffffffffffffffffffffffffffffffff6001541690611957565b610c2b565b506020823d602011610c9d575b81610c8b602093836115c3565b8101031261099d57610c259151610c1b565b3d9150610c7e565b610cae90611593565b6109c9578338610776565b8680fd5b50505030836024602073ffffffffffffffffffffffffffffffffffffffff865116604051928380927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa9081156109cd578291610d25575b50916105df565b9150506020813d602011610d52575b81610d41602093836115c3565b8101031261099d5784905138610d1e565b3d9150610d34565b73ffffffffffffffffffffffffffffffffffffffff6040820151166040516020816024817f70a0823100000000000000000000000000000000000000000000000000000000958682523060048301525afa9081156109aa578491610f4b575b5073ffffffffffffffffffffffffffffffffffffffff600154168473ffffffffffffffffffffffffffffffffffffffff6040860151169173ffffffffffffffffffffffffffffffffffffffff6060870151169060808701519160e088015194823b15610f4757610e6b95604051968795869485947f64d9f7970000000000000000000000000000000000000000000000000000000086526004860152602485015260a0604485015260a48401906116de565b906064830152336084830152039134905af18015610a9157610f34575b50602073ffffffffffffffffffffffffffffffffffffffff6040850151169260246040518095819382523060048301525afa80156109aa578490610f00575b610ed1925061173c565b80610edb57505080f35b6108ee9173ffffffffffffffffffffffffffffffffffffffff60403392015116611957565b506020823d602011610f2c575b81610f1a602093836115c3565b8101031261099d57610ed19151610ec7565b3d9150610f0d565b610f4090949194611593565b9238610e88565b8480fd5b90506020813d602011610f75575b81610f66602093836115c3565b810103126109c9575138610db9565b3d9150610f59565b8260e083015261058d565b905073ffffffffffffffffffffffffffffffffffffffff6040840151161438610587565b60046040517f9f57533c000000000000000000000000000000000000000000000000000000008152fd5b5073ffffffffffffffffffffffffffffffffffffffff604082015116825260ff60408320541615610547565b60208060249361101186611527565b81520193019291506104ce565b8580fd5b6024847f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b8280fd5b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b503461019957807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101995773ffffffffffffffffffffffffffffffffffffffff6020915416604051908152f35b50346101995760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101995760ff604060209273ffffffffffffffffffffffffffffffffffffffff611125611504565b168152600384522054166040519015158152f35b503461019957807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019957611170611906565b8073ffffffffffffffffffffffffffffffffffffffff81547fffffffffffffffffffffffff000000000000000000000000000000000000000081168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b50346101995760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101995761120d611504565b6024359081151580920361104f5773ffffffffffffffffffffffffffffffffffffffff90611239611906565b16801561127a5782526003602052604082209060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00835416911617905580f35b60046040517fd92e233d000000000000000000000000000000000000000000000000000000008152fd5b503461019957807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019957602073ffffffffffffffffffffffffffffffffffffffff60025416604051908152f35b503461019957807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019957602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b50346101995760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101995780808080611384611504565b61138c611906565b6113ad73ffffffffffffffffffffffffffffffffffffffff82161515611679565b602435905af1156113bb5780f35b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4554485f5452414e534645525f4641494c4544000000000000000000000000006044820152fd5b503461019957807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019957602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346101995760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610199576114c0611504565b73ffffffffffffffffffffffffffffffffffffffff6024358181169291838203610f47576114fa6108ee946114f3611906565b1515611679565b6044359216611957565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361099d57565b359073ffffffffffffffffffffffffffffffffffffffff8216820361099d57565b6060810190811067ffffffffffffffff82111761156457604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b67ffffffffffffffff811161156457604052565b6040810190811067ffffffffffffffff82111761156457604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761156457604052565b81601f8201121561099d5780359067ffffffffffffffff8211611564576040519261165760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f86011601856115c3565b8284526020838301011161099d57816000926020809301838601378301015290565b1561168057565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f5a45524f5f4144445245535300000000000000000000000000000000000000006044820152fd5b919082519283825260005b8481106117285750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b6020818301810151848301820152016116e9565b9190820391821161174957565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b906060604061179f93805184526020810151602085015201519181604082015201906116de565b90565b909291926040825273ffffffffffffffffffffffffffffffffffffffff80825116604084015260209181838201511660608501528160408201511660808501528160608201511660a085015260808101519061180c610160928360c08801526101a08701906116de565b9060a081015160e087015260c0810151906101009160028110156118d757828895949395015260e08101519361012094858901528101519061187b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc09361014093858b830301858c0152611778565b948101519383898703019089015286808551968781520194019460005b888282106118c1575050506118ba9450015190868303016101808701526116de565b9416910152565b8751891687529687019690950194600101611898565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff60005416330361192757565b60246040517f118cdaa7000000000000000000000000000000000000000000000000000000008152336004820152fd5b91604460209260009273ffffffffffffffffffffffffffffffffffffffff604051927fa9059cbb000000000000000000000000000000000000000000000000000000008452166004830152602482015282855af19081601f3d11600160005114161516611a25575b50156119c757565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152fd5b3b153d17159050386119bf56fea2646970667358221220eabc33b23eb09ec0a9dc5978f7d9e2ebdcaec0d02dec4cd4d4cdb3aab2f25e1664736f6c63430008180033000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba30000000000000000000000008274b0265c101027961e561e526acabb2c237113000000000000000000000000ed49fe44fd4249a09843c2ba4bba7e50beca7113

Deployed Bytecode

0x6080604052600436101561001b575b361561001957600080fd5b005b6000803560e01c806301e336671461148857806312261ee71461141957806316eebebd146110d15780631b9a91a41461134857806320cbe712146112f65780633729c5ca146112a457806368f47877146111d5578063715018a614611139578063819906fe146110d15780638da5cb5b14611080578063adc855b814610289578063d70f82021461019c5763f2fde38b146100b6575061000e565b346101995760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610199576100ed611504565b6100f5611906565b73ffffffffffffffffffffffffffffffffffffffff80911690811561016857600054827fffffffffffffffffffffffff0000000000000000000000000000000000000000821617600055167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a380f35b602483604051907f1e4fbdf70000000000000000000000000000000000000000000000000000000082526004820152fd5b80fd5b50346101995760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101995760043573ffffffffffffffffffffffffffffffffffffffff8116809103610285576101f5611906565b8015610227577fffffffffffffffffffffffff0000000000000000000000000000000000000000600154161760015580f35b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f5a65726f416464726573730000000000000000000000000000000000000000006044820152fd5b5080fd5b5060207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101995767ffffffffffffffff6004351161019957610160807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc600435360301126102855760405190810181811067ffffffffffffffff8211176110535760405261031f600435600401611527565b815261032f602460043501611527565b6020820152610342604460043501611527565b60408201526064600435013573ffffffffffffffffffffffffffffffffffffffff8116810361104f5760608201526084600435013567ffffffffffffffff811161104f57610397906004369181350101611604565b608082015260043560a481013560a083015260c40135600281101561104f5760c082015260043560e481013560e0830152610104013567ffffffffffffffff811161104f576004350160607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc823603011261104f576040519061041982611548565b600481013582526024810135602083015260448101359067ffffffffffffffff8211610f4757600461044e9236920101611604565b6040820152610100820152610124600435013567ffffffffffffffff811161104f57366023826004350101121561104f5760048181350101359067ffffffffffffffff8211611022578160051b604051926104ac60208301856115c3565b8352602083019036602482856004350101011161101e57906024836004350101915b602481856004350101018310611002575050505061012082015267ffffffffffffffff6101446004350135116102855761051336600480356101448101350101611604565b61014082015273ffffffffffffffffffffffffffffffffffffffff8151168252600360205260ff6040832054161580610fd6575b610fac5773ffffffffffffffffffffffffffffffffffffffff81511673ffffffffffffffffffffffffffffffffffffffff60025416808214908115610f88575b50610f7d575b15610d5a5773ffffffffffffffffffffffffffffffffffffffff60015416828360a08401519273ffffffffffffffffffffffffffffffffffffffff8551168252600360205260ff604083205416610cbd575b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba31673ffffffffffffffffffffffffffffffffffffffff8651169060a087015160405192610641846115a7565b835260208301526101008701519060208251920151936040519361066485611548565b8452602084019283526040840194855260a089015173ffffffffffffffffffffffffffffffffffffffff6040519261069b846115a7565b168252602082015260406101008a01510151823b15610cb9578694610767869261074e61071a956040519a8b998a9889977f30f28b7a0000000000000000000000000000000000000000000000000000000089526004890190516020809173ffffffffffffffffffffffffffffffffffffffff81511684520151910152565b516044870152516064860152805173ffffffffffffffffffffffffffffffffffffffff1660848601526020015160a4850152565b3360c484015261010060e48401526101048301906116de565b03925af180156109cd57610ca5575b505073ffffffffffffffffffffffffffffffffffffffff8351168452600360205260ff604085205416610bbe575b506040820151839073ffffffffffffffffffffffffffffffffffffffff1615610a9c575073ffffffffffffffffffffffffffffffffffffffff60408301511690604051906020826024817f70a0823100000000000000000000000000000000000000000000000000000000968782523060048301525afa918215610a91578592610a5d575b5060a0840152602083015173ffffffffffffffffffffffffffffffffffffffff166109d8578373ffffffffffffffffffffffffffffffffffffffff60015416803b15610285578160405180927fe06949b00000000000000000000000000000000000000000000000000000000082528183816108a9338c600484016117a2565b03925af180156109cd576109b5575b50505b73ffffffffffffffffffffffffffffffffffffffff6040840151168452600360205260ff6040852054166108f1575b5050505b80f35b602073ffffffffffffffffffffffffffffffffffffffff6040850151169260246040518095819382523060048301525afa80156109aa578490610971575b610939925061173c565b80610945575b806108ea565b61096a9173ffffffffffffffffffffffffffffffffffffffff60403392015116611957565b388061093f565b506020823d6020116109a2575b8161098b602093836115c3565b8101031261099d57610939915161092f565b600080fd5b3d915061097e565b6040513d86823e3d90fd5b6109be90611593565b6109c95783386108b8565b8380fd5b6040513d84823e3d90fd5b8373ffffffffffffffffffffffffffffffffffffffff60015416803b15610285578160405180927f01aa94bb000000000000000000000000000000000000000000000000000000008252818381610a33338c600484016117a2565b03925af180156109cd57610a49575b50506108bb565b610a5290611593565b6109c9578338610a42565b9091506020813d602011610a89575b81610a79602093836115c3565b8101031261099d57519038610829565b3d9150610a6c565b6040513d87823e3d90fd5b919073ffffffffffffffffffffffffffffffffffffffff6001541673ffffffffffffffffffffffffffffffffffffffff8351169073ffffffffffffffffffffffffffffffffffffffff60608501511660808501519261010060e087015196015190833b15610bba57610b9788968793610b57996040519a8b998a9889977f7c965eed0000000000000000000000000000000000000000000000000000000089526004890152602488015260e0604488015260e48701906116de565b92606486015260848501523360a48501527ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8483030160c4850152611778565b03925af180156109cd57610baa57505080f35b610bb390611593565b6101995780f35b8780fd5b60249150602073ffffffffffffffffffffffffffffffffffffffff845116604051938480927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa80156109aa578490610c71575b610c25925061173c565b80610c31575b386107a4565b610c6c8173ffffffffffffffffffffffffffffffffffffffff84511673ffffffffffffffffffffffffffffffffffffffff6001541690611957565b610c2b565b506020823d602011610c9d575b81610c8b602093836115c3565b8101031261099d57610c259151610c1b565b3d9150610c7e565b610cae90611593565b6109c9578338610776565b8680fd5b50505030836024602073ffffffffffffffffffffffffffffffffffffffff865116604051928380927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa9081156109cd578291610d25575b50916105df565b9150506020813d602011610d52575b81610d41602093836115c3565b8101031261099d5784905138610d1e565b3d9150610d34565b73ffffffffffffffffffffffffffffffffffffffff6040820151166040516020816024817f70a0823100000000000000000000000000000000000000000000000000000000958682523060048301525afa9081156109aa578491610f4b575b5073ffffffffffffffffffffffffffffffffffffffff600154168473ffffffffffffffffffffffffffffffffffffffff6040860151169173ffffffffffffffffffffffffffffffffffffffff6060870151169060808701519160e088015194823b15610f4757610e6b95604051968795869485947f64d9f7970000000000000000000000000000000000000000000000000000000086526004860152602485015260a0604485015260a48401906116de565b906064830152336084830152039134905af18015610a9157610f34575b50602073ffffffffffffffffffffffffffffffffffffffff6040850151169260246040518095819382523060048301525afa80156109aa578490610f00575b610ed1925061173c565b80610edb57505080f35b6108ee9173ffffffffffffffffffffffffffffffffffffffff60403392015116611957565b506020823d602011610f2c575b81610f1a602093836115c3565b8101031261099d57610ed19151610ec7565b3d9150610f0d565b610f4090949194611593565b9238610e88565b8480fd5b90506020813d602011610f75575b81610f66602093836115c3565b810103126109c9575138610db9565b3d9150610f59565b8260e083015261058d565b905073ffffffffffffffffffffffffffffffffffffffff6040840151161438610587565b60046040517f9f57533c000000000000000000000000000000000000000000000000000000008152fd5b5073ffffffffffffffffffffffffffffffffffffffff604082015116825260ff60408320541615610547565b60208060249361101186611527565b81520193019291506104ce565b8580fd5b6024847f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b8280fd5b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b503461019957807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101995773ffffffffffffffffffffffffffffffffffffffff6020915416604051908152f35b50346101995760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101995760ff604060209273ffffffffffffffffffffffffffffffffffffffff611125611504565b168152600384522054166040519015158152f35b503461019957807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019957611170611906565b8073ffffffffffffffffffffffffffffffffffffffff81547fffffffffffffffffffffffff000000000000000000000000000000000000000081168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b50346101995760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101995761120d611504565b6024359081151580920361104f5773ffffffffffffffffffffffffffffffffffffffff90611239611906565b16801561127a5782526003602052604082209060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00835416911617905580f35b60046040517fd92e233d000000000000000000000000000000000000000000000000000000008152fd5b503461019957807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019957602073ffffffffffffffffffffffffffffffffffffffff60025416604051908152f35b503461019957807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019957602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b50346101995760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101995780808080611384611504565b61138c611906565b6113ad73ffffffffffffffffffffffffffffffffffffffff82161515611679565b602435905af1156113bb5780f35b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4554485f5452414e534645525f4641494c4544000000000000000000000000006044820152fd5b503461019957807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019957602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3168152f35b50346101995760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610199576114c0611504565b73ffffffffffffffffffffffffffffffffffffffff6024358181169291838203610f47576114fa6108ee946114f3611906565b1515611679565b6044359216611957565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361099d57565b359073ffffffffffffffffffffffffffffffffffffffff8216820361099d57565b6060810190811067ffffffffffffffff82111761156457604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b67ffffffffffffffff811161156457604052565b6040810190811067ffffffffffffffff82111761156457604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761156457604052565b81601f8201121561099d5780359067ffffffffffffffff8211611564576040519261165760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f86011601856115c3565b8284526020838301011161099d57816000926020809301838601378301015290565b1561168057565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f5a45524f5f4144445245535300000000000000000000000000000000000000006044820152fd5b919082519283825260005b8481106117285750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b6020818301810151848301820152016116e9565b9190820391821161174957565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b906060604061179f93805184526020810151602085015201519181604082015201906116de565b90565b909291926040825273ffffffffffffffffffffffffffffffffffffffff80825116604084015260209181838201511660608501528160408201511660808501528160608201511660a085015260808101519061180c610160928360c08801526101a08701906116de565b9060a081015160e087015260c0810151906101009160028110156118d757828895949395015260e08101519361012094858901528101519061187b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc09361014093858b830301858c0152611778565b948101519383898703019089015286808551968781520194019460005b888282106118c1575050506118ba9450015190868303016101808701526116de565b9416910152565b8751891687529687019690950194600101611898565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff60005416330361192757565b60246040517f118cdaa7000000000000000000000000000000000000000000000000000000008152336004820152fd5b91604460209260009273ffffffffffffffffffffffffffffffffffffffff604051927fa9059cbb000000000000000000000000000000000000000000000000000000008452166004830152602482015282855af19081601f3d11600160005114161516611a25575b50156119c757565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152fd5b3b153d17159050386119bf56fea2646970667358221220eabc33b23eb09ec0a9dc5978f7d9e2ebdcaec0d02dec4cd4d4cdb3aab2f25e1664736f6c63430008180033

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

000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba30000000000000000000000008274b0265c101027961e561e526acabb2c237113000000000000000000000000ed49fe44fd4249a09843c2ba4bba7e50beca7113

-----Decoded View---------------
Arg [0] : permit2 (address): 0x000000000022D473030F116dDEE9F6B43aC78BA3
Arg [1] : _dnaRouter (address): 0x8274b0265C101027961e561e526AcabB2c237113
Arg [2] : _dnaTokenAddr (address): 0xED49fE44fD4249A09843C2Ba4bba7e50BECa7113

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3
Arg [1] : 0000000000000000000000008274b0265c101027961e561e526acabb2c237113
Arg [2] : 000000000000000000000000ed49fe44fd4249a09843c2ba4bba7e50beca7113


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
[ 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.