S Price: $0.802124 (+0.03%)

Contract

0x9e93900f1ECd59AB2F4880F6aA78C752383845D3

Overview

S Balance

Sonic LogoSonic LogoSonic Logo0 S

S Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

Parent Transaction Hash Block From To
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Blacksail_SwapX_QoL

Compiler Version
v0.8.20+commit.a1b79de6

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion
File 1 of 3 : Blacksail_SwapX_QoL.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import "../interfacing/ichi/IICHIVault.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

// Helper contract for normal liquidity withdrawal
contract Blacksail_SwapX_QoL {
    event WithdrawLiquidity(
        address indexed token0,
        uint256 amount0,
        address indexed token1,
        uint256 amount1,
        address indexed to
    );
    event AddLiquidity(
        address indexed tokenIn,
        uint256 amountIn,
        address indexed tokenOut,
        uint256 amountOut,
        address indexed to
    );

    constructor() {}

    /**
     * @notice Allows users to withdraw their liquidity from an ICHI vault without relying on the native platform's UI.
     * @dev This function is designed to provide a streamlined withdrawal process, especially for users who are
     *      not staked in the platform's gauge and therefore cannot withdraw liquidity directly via the native UI.
     *      It transfers the user's full LP token balance to the vault, withdrawing the corresponding amounts
     *      of Token0 and Token1 back to the user.
     * @param ichiVault The address of the ICHI vault from which liquidity will be withdrawn.
     *
     * Emits a {WithdrawLiquidity} event with the withdrawn token amounts and recipient details.
     */
    function removeLiquidity(address ichiVault) external {
        uint256 amount0;
        uint256 amount1;

        address token0 = IICHIVault(ichiVault).token0();
        address token1 = IICHIVault(ichiVault).token1();

        uint256 liquidity = IERC20(ichiVault).balanceOf(msg.sender);

        IERC20(ichiVault).transferFrom(msg.sender, address(this), liquidity);

        (amount0, amount1) = IICHIVault(ichiVault).withdraw(
            liquidity,
            msg.sender
        );

        emit WithdrawLiquidity(token0, amount0, token1, amount1, msg.sender);
    }

    /**
     * @dev Adds liquidity to an ICHI vault by depositing the specified amount of tokens.
     * @param ichiVault The address of the ICHI vault to which liquidity will be added.
     * @param _amount The amount of deposit tokens to be added as liquidity.
     *
     * Requirements:
     * - `_amount` must not exceed the caller's balance of the deposit token.
     * - Caller must approve this contract to transfer `_amount` of the deposit token on their behalf.
     *
     * Functionality:
     * - Determines the deposit token and the side (0 or 1) based on the vault configuration.
     * - Transfers `_amount` of the deposit token from the caller to this contract.
     * - Based on the side, deposits the `_amount` into the appropriate side of the ICHI vault.
     * - Emits an `AddLiquidity` event upon successful deposit.
     *
     * Emits:
     * - {AddLiquidity} event indicating the details of the liquidity added.
     */
    function addLiquidity(address ichiVault, uint256 _amount) external {
        (address depositToken, uint side) = getIchiDepositToken(ichiVault);
        require(
            _amount <= IERC20(depositToken).balanceOf(msg.sender),
            "Exceeds balance"
        );

        IERC20(depositToken).transferFrom(msg.sender, address(this), _amount);

        uint256 shares;

        IERC20(depositToken).approve(ichiVault, _amount);

        if (side == 0) {
            shares = IICHIVault(ichiVault).deposit(_amount, 0, msg.sender);
        }

        if (side == 1) {
            shares = IICHIVault(ichiVault).deposit(0, _amount, msg.sender);
        }

        emit AddLiquidity(depositToken, _amount, ichiVault, shares, msg.sender);
    }

    /**
     * @dev Retrieves the eligible deposit token and its corresponding side for a given ICHI vault.
     * @param ichiVault The address of the ICHI vault.
     * @return The address of the eligible deposit token and the side (0 for token0, 1 for token1).
     *
     * Functionality:
     * - Checks if `token0` or `token1` is allowed for deposits in the specified ICHI vault.
     * - Returns the address of the eligible token and its associated side (0 for token0, 1 for token1).
     * - Reverts if both `token0` and `token1` are eligible, as this is considered invalid behavior.
     *
     * Requirements:
     * - The ICHI vault must allow exactly one token (`token0` or `token1`) for deposits.
     *
     * Reverts:
     * - If both `token0` and `token1` are eligible, with the message "!eligible".
     */
    function getIchiDepositToken(
        address ichiVault
    ) public view returns (address, uint) {
        bool tok0 = IICHIVault(ichiVault).allowToken0();
        bool tok1 = IICHIVault(ichiVault).allowToken1();

        if (tok0 && !tok1) {
            return (IICHIVault(ichiVault).token0(), 0);
        }

        if (tok1 && !tok0) {
            return (IICHIVault(ichiVault).token1(), 1);
        }

        if (tok0 && tok1) {
            revert("!eliglble");
        }
    }
}

File 2 of 3 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

File 3 of 3 : IICHIVault.sol
// SPDX-License-Identifier: Unlicense
pragma solidity >=0.5.0;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IICHIVault is IERC20 {
    function ichiVaultFactory() external view returns (address);

    function pool() external view returns (address);

    function token0() external view returns (address);

    function allowToken0() external view returns (bool);

    function token1() external view returns (address);

    function allowToken1() external view returns (bool);

    function fee() external view returns (uint24);

    function tickSpacing() external view returns (int24);

    function affiliate() external view returns (address);

    function baseLower() external view returns (int24);

    function baseUpper() external view returns (int24);

    function limitLower() external view returns (int24);

    function limitUpper() external view returns (int24);

    function deposit0Max() external view returns (uint256);

    function deposit1Max() external view returns (uint256);

    function maxTotalSupply() external view returns (uint256);

    function hysteresis() external view returns (uint256);

    function getTotalAmounts() external view returns (uint256, uint256);

    function deposit(uint256, uint256, address) external returns (uint256);

    function withdraw(uint256, address) external returns (uint256, uint256);

    function rebalance(
        int24 _baseLower,
        int24 _baseUpper,
        int24 _limitLower,
        int24 _limitUpper,
        int256 swapQuantity
    ) external;

    function setDepositMax(uint256 _deposit0Max, uint256 _deposit1Max) external;

    function setAffiliate(address _affiliate) external;

    event DeployICHIVault(
        address indexed sender,
        address indexed pool,
        bool allowToken0,
        bool allowToken1,
        address owner,
        uint256 twapPeriod
    );

    event SetTwapPeriod(address sender, uint32 newTwapPeriod);

    event Deposit(address indexed sender, address indexed to, uint256 shares, uint256 amount0, uint256 amount1);

    event Withdraw(address indexed sender, address indexed to, uint256 shares, uint256 amount0, uint256 amount1);

    event Rebalance(
        int24 tick,
        uint256 totalAmount0,
        uint256 totalAmount1,
        uint256 feeAmount0,
        uint256 feeAmount1,
        uint256 totalSupply
    );

    event MaxTotalSupply(address indexed sender, uint256 maxTotalSupply);

    event Hysteresis(address indexed sender, uint256 hysteresis);

    event DepositMax(address indexed sender, uint256 deposit0Max, uint256 deposit1Max);

    event Affiliate(address indexed sender, address affiliate);
}

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

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"tokenIn","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":true,"internalType":"address","name":"tokenOut","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountOut","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"AddLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token0","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":true,"internalType":"address","name":"token1","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"WithdrawLiquidity","type":"event"},{"inputs":[{"internalType":"address","name":"ichiVault","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"addLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"ichiVault","type":"address"}],"name":"getIchiDepositToken","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"ichiVault","type":"address"}],"name":"removeLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"}]

6080806040523461001657610869908161001c8239f35b600080fdfe60406080815260048036101561001457600080fd5b600091823560e01c806356688700146102e0578063976c2bb7146102905763d798f86e1461004157600080fd5b3461028c5760209081600319360112610288576001600160a01b0391826100666105d1565b1691805193630dfe168160e01b855282858781875afa9485156101b2578795610269575b50815163d21220a760e01b81529583878281885afa96871561025f578897610230575b5082516370a0823160e01b81523382820152908482602481895afa9182156102265789926101f3575b5083516323b872dd60e01b8152338282019081523060208201526040810184905286908290819060600103818d8b5af180156101e957916044918694936101bc575b508a84519889948593627b8a6760e11b85528401523360248401525af180156101b2578793889161017a575b835194855284015233958116941692507f5d6102171d38f5a0418b7f4b7affa937788ec4c74960ea0f04c6c4d5c8f85ba891a480f35b9350508184813d83116101ab575b61019281836105ec565b810103126101a7578351938301518493610144565b8680fd5b503d610188565b82513d89823e3d90fd5b6101db90883d8a116101e2575b6101d381836105ec565b810190610643565b5038610118565b503d6101c9565b85513d8c823e3d90fd5b9091508481813d831161021f575b61020b81836105ec565b8101031261021b575190386100d6565b8880fd5b503d610201565b84513d8b823e3d90fd5b610251919750843d8611610258575b61024981836105ec565b810190610624565b95386100ad565b503d61023f565b83513d8a823e3d90fd5b610281919550833d85116102585761024981836105ec565b933861008a565b8380fd5b8280fd5b8382346102dc5760203660031901126102dc576102d8906102b76102b26105d1565b61065b565b91516001600160a01b03909116815260208101919091529081906040820190565b0390f35b5080fd5b50903461028c578160031936011261028c576102fa6105d1565b91602435916103088461065b565b83516370a0823160e01b815233848201526001600160a01b039283169492919060209081816024818a5afa9081156101e9578a916105a4575b5087116105705783516323b872dd60e01b8152338682019081523060208201526040810189905282908290819060600103818d8b5af180156101e957610553575b50835163095ea7b360e01b81526001600160a01b038916868201908152602081018990528a9391908390829081900360400181878c5af180156105495761052c575b5080156104b8575b600114610407575b7fb43a337f0fe94ef11751a039d0524b95e0cf3e41c1edd5c2e798133995751edb9450835196875286015233951693a480f35b905087835194638dbdbe6d60e01b865285015285602485015233604485015280846064818b868c165af1801561025f578890610467575b7fb43a337f0fe94ef11751a039d0524b95e0cf3e41c1edd5c2e798133995751edb9450906103d4565b508084813d83116104b1575b61047d81836105ec565b810103126104ad577fb43a337f0fe94ef11751a039d0524b95e0cf3e41c1edd5c2e798133995751edb935161043e565b8780fd5b503d610473565b8451638dbdbe6d60e01b8152868101899052602481018b905233604482015290925081816064818d8d89165af180156101e9578a906104fa575b9290506103cc565b508181813d8311610525575b61051081836105ec565b8101031261052157600190516104f2565b8980fd5b503d610506565b61054290833d85116101e2576101d381836105ec565b50386103c4565b86513d8d823e3d90fd5b61056990823d84116101e2576101d381836105ec565b5038610382565b8460649185519162461bcd60e51b8352820152600f60248201526e457863656564732062616c616e636560881b6044820152fd5b90508181813d83116105ca575b6105bb81836105ec565b81010312610521575138610341565b503d6105b1565b600435906001600160a01b03821682036105e757565b600080fd5b90601f8019910116810190811067ffffffffffffffff82111761060e57604052565b634e487b7160e01b600052604160045260246000fd5b908160209103126105e757516001600160a01b03811681036105e75790565b908160209103126105e7575180151581036105e75790565b60408051631fde87bb60e21b81526000938493909260209290916001600160a01b0316858483600481855afa928315610827578193610808575b50835162df906d60e61b8152918583600481845afa9283156107fe5782936107df575b5083806107d7575b61077c578280610774575b6107175750508161070f575b506106e0575050565b60649250519062461bcd60e51b82526004820152600960248201526821656c69676c626c6560b81b6044820152fd5b9050386106d7565b845163d21220a760e01b81529698509396509284925085915060049082905afa93841561076a57509261074d575b505090600190565b6107639250803d106102585761024981836105ec565b3880610745565b51903d90823e3d90fd5b5083156106cb565b9150965082849395965060049492505193848092630dfe168160e01b82525afa9283156107ce575084926107b1575b50509190565b6107c79250803d106102585761024981836105ec565b38806107ab565b513d86823e3d90fd5b5082156106c0565b6107f7919350863d88116101e2576101d381836105ec565b91386106b8565b85513d84823e3d90fd5b610820919350853d87116101e2576101d381836105ec565b9138610695565b508351903d90823e3d90fdfea2646970667358221220b62ebd5a95ba58f7fe2c36199d698d9aab150510f7adf169631723c5e599e75464736f6c63430008140033

Deployed Bytecode

0x60406080815260048036101561001457600080fd5b600091823560e01c806356688700146102e0578063976c2bb7146102905763d798f86e1461004157600080fd5b3461028c5760209081600319360112610288576001600160a01b0391826100666105d1565b1691805193630dfe168160e01b855282858781875afa9485156101b2578795610269575b50815163d21220a760e01b81529583878281885afa96871561025f578897610230575b5082516370a0823160e01b81523382820152908482602481895afa9182156102265789926101f3575b5083516323b872dd60e01b8152338282019081523060208201526040810184905286908290819060600103818d8b5af180156101e957916044918694936101bc575b508a84519889948593627b8a6760e11b85528401523360248401525af180156101b2578793889161017a575b835194855284015233958116941692507f5d6102171d38f5a0418b7f4b7affa937788ec4c74960ea0f04c6c4d5c8f85ba891a480f35b9350508184813d83116101ab575b61019281836105ec565b810103126101a7578351938301518493610144565b8680fd5b503d610188565b82513d89823e3d90fd5b6101db90883d8a116101e2575b6101d381836105ec565b810190610643565b5038610118565b503d6101c9565b85513d8c823e3d90fd5b9091508481813d831161021f575b61020b81836105ec565b8101031261021b575190386100d6565b8880fd5b503d610201565b84513d8b823e3d90fd5b610251919750843d8611610258575b61024981836105ec565b810190610624565b95386100ad565b503d61023f565b83513d8a823e3d90fd5b610281919550833d85116102585761024981836105ec565b933861008a565b8380fd5b8280fd5b8382346102dc5760203660031901126102dc576102d8906102b76102b26105d1565b61065b565b91516001600160a01b03909116815260208101919091529081906040820190565b0390f35b5080fd5b50903461028c578160031936011261028c576102fa6105d1565b91602435916103088461065b565b83516370a0823160e01b815233848201526001600160a01b039283169492919060209081816024818a5afa9081156101e9578a916105a4575b5087116105705783516323b872dd60e01b8152338682019081523060208201526040810189905282908290819060600103818d8b5af180156101e957610553575b50835163095ea7b360e01b81526001600160a01b038916868201908152602081018990528a9391908390829081900360400181878c5af180156105495761052c575b5080156104b8575b600114610407575b7fb43a337f0fe94ef11751a039d0524b95e0cf3e41c1edd5c2e798133995751edb9450835196875286015233951693a480f35b905087835194638dbdbe6d60e01b865285015285602485015233604485015280846064818b868c165af1801561025f578890610467575b7fb43a337f0fe94ef11751a039d0524b95e0cf3e41c1edd5c2e798133995751edb9450906103d4565b508084813d83116104b1575b61047d81836105ec565b810103126104ad577fb43a337f0fe94ef11751a039d0524b95e0cf3e41c1edd5c2e798133995751edb935161043e565b8780fd5b503d610473565b8451638dbdbe6d60e01b8152868101899052602481018b905233604482015290925081816064818d8d89165af180156101e9578a906104fa575b9290506103cc565b508181813d8311610525575b61051081836105ec565b8101031261052157600190516104f2565b8980fd5b503d610506565b61054290833d85116101e2576101d381836105ec565b50386103c4565b86513d8d823e3d90fd5b61056990823d84116101e2576101d381836105ec565b5038610382565b8460649185519162461bcd60e51b8352820152600f60248201526e457863656564732062616c616e636560881b6044820152fd5b90508181813d83116105ca575b6105bb81836105ec565b81010312610521575138610341565b503d6105b1565b600435906001600160a01b03821682036105e757565b600080fd5b90601f8019910116810190811067ffffffffffffffff82111761060e57604052565b634e487b7160e01b600052604160045260246000fd5b908160209103126105e757516001600160a01b03811681036105e75790565b908160209103126105e7575180151581036105e75790565b60408051631fde87bb60e21b81526000938493909260209290916001600160a01b0316858483600481855afa928315610827578193610808575b50835162df906d60e61b8152918583600481845afa9283156107fe5782936107df575b5083806107d7575b61077c578280610774575b6107175750508161070f575b506106e0575050565b60649250519062461bcd60e51b82526004820152600960248201526821656c69676c626c6560b81b6044820152fd5b9050386106d7565b845163d21220a760e01b81529698509396509284925085915060049082905afa93841561076a57509261074d575b505090600190565b6107639250803d106102585761024981836105ec565b3880610745565b51903d90823e3d90fd5b5083156106cb565b9150965082849395965060049492505193848092630dfe168160e01b82525afa9283156107ce575084926107b1575b50509190565b6107c79250803d106102585761024981836105ec565b38806107ab565b513d86823e3d90fd5b5082156106c0565b6107f7919350863d88116101e2576101d381836105ec565b91386106b8565b85513d84823e3d90fd5b610820919350853d87116101e2576101d381836105ec565b9138610695565b508351903d90823e3d90fdfea2646970667358221220b62ebd5a95ba58f7fe2c36199d698d9aab150510f7adf169631723c5e599e75464736f6c63430008140033

Block Transaction 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

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.