OdosV2ContractGuard

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "@openzeppelin/contracts/access/Ownable.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

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

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor () {
        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }

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

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}
"
    },
    "@openzeppelin/contracts/math/Math.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a >= b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow, so we distribute
        return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
    }
}
"
    },
    "@openzeppelin/contracts/math/SafeMath.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        uint256 c = a + b;
        if (c < a) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b > a) return (false, 0);
        return (true, a - b);
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) return (true, 0);
        uint256 c = a * b;
        if (c / a != b) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a / b);
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a % b);
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");
        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SafeMath: subtraction overflow");
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) return 0;
        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");
        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: division by zero");
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: modulo by zero");
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        return a - b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryDiv}.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a % b;
    }
}
"
    },
    "@openzeppelin/contracts/token/ERC20/IERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

    /**
     * @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);
}
"
    },
    "@openzeppelin/contracts/utils/Context.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/*
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with GSN 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 payable) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes memory) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}
"
    },
    "@openzeppelin/contracts/utils/SafeCast.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;


/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 *
 * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and `int256` and then downcasting.
 */
library SafeCast {

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value < 2**128, "SafeCast: value doesn\'t fit in 128 bits");
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        require(value < 2**64, "SafeCast: value doesn\'t fit in 64 bits");
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        require(value < 2**32, "SafeCast: value doesn\'t fit in 32 bits");
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        require(value < 2**16, "SafeCast: value doesn\'t fit in 16 bits");
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits.
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        require(value < 2**8, "SafeCast: value doesn\'t fit in 8 bits");
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        require(value >= 0, "SafeCast: value must be positive");
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v3.1._
     */
    function toInt128(int256 value) internal pure returns (int128) {
        require(value >= -2**127 && value < 2**127, "SafeCast: value doesn\'t fit in 128 bits");
        return int128(value);
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v3.1._
     */
    function toInt64(int256 value) internal pure returns (int64) {
        require(value >= -2**63 && value < 2**63, "SafeCast: value doesn\'t fit in 64 bits");
        return int64(value);
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v3.1._
     */
    function toInt32(int256 value) internal pure returns (int32) {
        require(value >= -2**31 && value < 2**31, "SafeCast: value doesn\'t fit in 32 bits");
        return int32(value);
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v3.1._
     */
    function toInt16(int256 value) internal pure returns (int16) {
        require(value >= -2**15 && value < 2**15, "SafeCast: value doesn\'t fit in 16 bits");
        return int16(value);
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits.
     *
     * _Available since v3.1._
     */
    function toInt8(int256 value) internal pure returns (int8) {
        require(value >= -2**7 && value < 2**7, "SafeCast: value doesn\'t fit in 8 bits");
        return int8(value);
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        require(value < 2**255, "SafeCast: value doesn't fit in an int256");
        return int256(value);
    }
}
"
    },
    "@uniswap/v3-periphery/contracts/libraries/BytesLib.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * @title Solidity Bytes Arrays Utils
 * @author Gonçalo Sá <goncalo.sa@consensys.net>
 *
 * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
 *      The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
 */
pragma solidity >=0.5.0 <0.8.0;

library BytesLib {
    function slice(
        bytes memory _bytes,
        uint256 _start,
        uint256 _length
    ) internal pure returns (bytes memory) {
        require(_length + 31 >= _length, 'slice_overflow');
        require(_start + _length >= _start, 'slice_overflow');
        require(_bytes.length >= _start + _length, 'slice_outOfBounds');

        bytes memory tempBytes;

        assembly {
            switch iszero(_length)
                case 0 {
                    // Get a location of some free memory and store it in tempBytes as
                    // Solidity does for memory variables.
                    tempBytes := mload(0x40)

                    // The first word of the slice result is potentially a partial
                    // word read from the original array. To read it, we calculate
                    // the length of that partial word and start copying that many
                    // bytes into the array. The first word we copy will start with
                    // data we don't care about, but the last `lengthmod` bytes will
                    // land at the beginning of the contents of the new array. When
                    // we're done copying, we overwrite the full first word with
                    // the actual length of the slice.
                    let lengthmod := and(_length, 31)

                    // The multiplication in the next line is necessary
                    // because when slicing multiples of 32 bytes (lengthmod == 0)
                    // the following copy loop was copying the origin's length
                    // and then ending prematurely not copying everything it should.
                    let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                    let end := add(mc, _length)

                    for {
                        // The multiplication in the next line has the same exact purpose
                        // as the one above.
                        let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                    } lt(mc, end) {
                        mc := add(mc, 0x20)
                        cc := add(cc, 0x20)
                    } {
                        mstore(mc, mload(cc))
                    }

                    mstore(tempBytes, _length)

                    //update free-memory pointer
                    //allocating the array padded to 32 bytes like the compiler does now
                    mstore(0x40, and(add(mc, 31), not(31)))
                }
                //if we want a zero-length slice let's just return a zero-length array
                default {
                    tempBytes := mload(0x40)
                    //zero out the 32 bytes slice we are about to return
                    //we need to do it because Solidity does not garbage collect
                    mstore(tempBytes, 0)

                    mstore(0x40, add(tempBytes, 0x20))
                }
        }

        return tempBytes;
    }

    function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
        require(_start + 20 >= _start, 'toAddress_overflow');
        require(_bytes.length >= _start + 20, 'toAddress_outOfBounds');
        address tempAddress;

        assembly {
            tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
        }

        return tempAddress;
    }

    function toUint24(bytes memory _bytes, uint256 _start) internal pure returns (uint24) {
        require(_start + 3 >= _start, 'toUint24_overflow');
        require(_bytes.length >= _start + 3, 'toUint24_outOfBounds');
        uint24 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x3), _start))
        }

        return tempUint;
    }
}
"
    },
    "contracts/guards/contractGuards/odos/OdosV2ContractGuard.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;

import {TxDataUtils} from "../../../utils/TxDataUtils.sol";
import {ITransactionTypes} from "../../../interfaces/ITransactionTypes.sol";
import {SlippageAccumulator, SlippageAccumulatorUser} from "../../../utils/SlippageAccumulatorUser.sol";
import {IOdosRouterV2} from "../../../interfaces/odos/IOdosRouterV2.sol";
import {IPoolManagerLogic} from "../../../interfaces/IPoolManagerLogic.sol";
import {IHasSupportedAsset} from "../../../interfaces/IHasSupportedAsset.sol";

/// @notice Contract guard contract for OdosRouterV2
/// @dev As this contract inherits `SlippageAccumulatorUser`, it also inherits the `ITxTrackingGuard` interface.
contract OdosV2ContractGuard is TxDataUtils, ITransactionTypes, SlippageAccumulatorUser {
  mapping(address => mapping(address => bool)) internal _beforeSwapSrcAssetCheck; // poolLogic -> srcAsset -> bool

  constructor(address _slippageAccumulator) SlippageAccumulatorUser(_slippageAccumulator) {}

  /// @dev  Support methods `swapCompact, `swap`
  /// @param _poolManagerLogic Pool manager logic address
  /// @param _data Transaction data
  /// @return txType Transaction type
  /// @return isPublic If the transaction is public or private
  function txGuard(
    address _poolManagerLogic,
    address _to,
    bytes memory _data
  ) external override returns (uint16 txType, bool) {
    address poolLogic = _accessControl(_poolManagerLogic);
    bytes4 method = getMethod(_data);

    if (method == IOdosRouterV2.swap.selector) {
      bytes memory params = getParams(_data);
      IOdosRouterV2.SwapTokenInfo memory swapTokenInfo = abi.decode(params, (IOdosRouterV2.SwapTokenInfo));
      txType = _verifySwap(swapTokenInfo, _poolManagerLogic, poolLogic);
    } else if (method == IOdosRouterV2.swapCompact.selector) {
      (bool success, bytes memory swapData) = address(this).staticcall(
        abi.encodePacked(this._decodeCompactCalldata.selector, _to, _data)
      );
      require(success, "decodeCompactCalldata failed");
      IOdosRouterV2.SwapTokenInfo memory swapTokenInfo = abi.decode(swapData, (IOdosRouterV2.SwapTokenInfo));
      txType = _verifySwap(swapTokenInfo, _poolManagerLogic, poolLogic);
    }

    return (txType, false);
  }

  function afterTxGuard(address _poolManagerLogic, address _to, bytes memory _data) public override {
    address poolLogic = IPoolManagerLogic(_poolManagerLogic).poolLogic();
    bytes4 method = getMethod(_data);

    if (method == IOdosRouterV2.swap.selector) {
      bytes memory params = getParams(_data);
      IOdosRouterV2.SwapTokenInfo memory swapTokenInfo = abi.decode(params, (IOdosRouterV2.SwapTokenInfo));
      _verifySwapAfterTxGuard(swapTokenInfo, _poolManagerLogic, poolLogic);
      SlippageAccumulatorUser.afterTxGuard(_poolManagerLogic, _to, _data);
    } else if (method == IOdosRouterV2.swapCompact.selector) {
      (bool success, bytes memory swapData) = address(this).staticcall(
        abi.encodePacked(this._decodeCompactCalldata.selector, _to, _data)
      );
      require(success, "decodeCompactCalldata failed");
      IOdosRouterV2.SwapTokenInfo memory swapTokenInfo = abi.decode(swapData, (IOdosRouterV2.SwapTokenInfo));
      _verifySwapAfterTxGuard(swapTokenInfo, _poolManagerLogic, poolLogic);
      SlippageAccumulatorUser.afterTxGuard(_poolManagerLogic, _to, _data);
    }
  }

  function _verifySwap(
    IOdosRouterV2.SwapTokenInfo memory _swapTokenInfo,
    address _poolManagerLogic,
    address _poolLogic
  ) internal returns (uint16 txType) {
    require(_swapTokenInfo.outputReceiver == _poolLogic, "recipient is not pool");
    require(_swapTokenInfo.inputToken != address(0), "invalid input token"); // do not support native ETH
    require(_swapTokenInfo.inputAmount != 0, "invalid input amount"); // do not support entire balance trade
    require(
      IHasSupportedAsset(_poolManagerLogic).isSupportedAsset(_swapTokenInfo.outputToken),
      "unsupported destination asset"
    );

    intermediateSwapData = SlippageAccumulator.SwapData({
      srcAsset: _swapTokenInfo.inputToken,
      dstAsset: _swapTokenInfo.outputToken,
      srcAmount: _getBalance(_swapTokenInfo.inputToken, _poolLogic),
      dstAmount: _getBalance(_swapTokenInfo.outputToken, _poolLogic)
    });
    if (IHasSupportedAsset(_poolManagerLogic).isSupportedAsset(_swapTokenInfo.inputToken)) {
      _beforeSwapSrcAssetCheck[_poolLogic][_swapTokenInfo.inputToken] = true;
    }

    txType = uint16(TransactionType.Exchange);
  }

  function _verifySwapAfterTxGuard(
    IOdosRouterV2.SwapTokenInfo memory _swapTokenInfo,
    address _poolManagerLogic,
    address _poolLogic
  ) internal {
    require(
      IHasSupportedAsset(_poolManagerLogic).isSupportedAsset(_swapTokenInfo.outputToken),
      "unsupported destination asset"
    );

    if (_beforeSwapSrcAssetCheck[_poolLogic][_swapTokenInfo.inputToken]) {
      require(
        IHasSupportedAsset(_poolManagerLogic).isSupportedAsset(_swapTokenInfo.inputToken),
        "unsupported source asset"
      );

      _beforeSwapSrcAssetCheck[_poolLogic][_swapTokenInfo.inputToken] = false;
    }
  }

  /// @notice Decode the compact calldata for a swap
  /// modified from https://github.com/odos-xyz/odos-router-v2/blob/main/contracts/OdosRouterV2.sol#L103
  /// and use assembly to call storage variable `address[] public addressList` in the OdosRouterV2 contract
  function _decodeCompactCalldata() public view returns (IOdosRouterV2.SwapTokenInfo memory tokenInfo) {
    address executor;

    {
      address msgSender = msg.sender;

      assembly {
        // Define function to load in token address, either from calldata or from storage
        function getAddress(currPos, routerAddress) -> result, newPos {
          let inputPos := shr(240, calldataload(currPos))

          switch inputPos
          // Reserve the null address as a special case that can be specified with 2 null bytes
          case 0x0000 {
            newPos := add(currPos, 2)
          }
          // This case means that the address is encoded in the calldata directly following the code
          case 0x0001 {
            result := and(shr(80, calldataload(currPos)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
            newPos := add(currPos, 22)
          }
          // Otherwise we use the case to load in from the cached address list
          default {
            let ptr := mload(0x40) //  the free memory pointer
            let selector := 0xb810fb43 // for function addressList(uint256)
            mstore(ptr, shl(224, selector)) // selector is only 4 bytes, so shift left by (32 - 4) * 8 = 224 bits
            mstore(add(ptr, 4), sub(inputPos, 2)) //  store the uint256 argument after the selector
            let success := staticcall(gas(), routerAddress, ptr, 36, ptr, 32)
            if iszero(success) {
              revert(0, 0)
            }
            result := mload(ptr) // load the address from memory
            newPos := add(currPos, 2) // update the position in calldata
          }
        }
        let result := 0
        let pos := 28 // 4 + 20 + 4   Skip the first 4 bytes of the _decodeCompactCalldata.selector and 20 bytes of _to address and 4 bytes of the swapCompact.selector
        let routerAddress := shr(96, calldataload(4)) // shift to remove upper 12 bytes (96 bits)

        // Load in the input and output token addresses
        result, pos := getAddress(pos, routerAddress)
        mstore(tokenInfo, result)

        result, pos := getAddress(pos, routerAddress)
        mstore(add(tokenInfo, 0x60), result)

        // Load in the input amount - a 0 byte means the full balance is to be used
        let inputAmountLength := shr(248, calldataload(pos))
        pos := add(pos, 1)

        if inputAmountLength {
          mstore(add(tokenInfo, 0x20), shr(mul(sub(32, inputAmountLength), 8), calldataload(pos)))
          pos := add(pos, inputAmountLength)
        }

        // Load in the quoted output amount
        let quoteAmountLength := shr(248, calldataload(pos))
        pos := add(pos, 1)

        let outputQuote := shr(mul(sub(32, quoteAmountLength), 8), calldataload(pos))
        mstore(add(tokenInfo, 0x80), outputQuote)
        pos := add(pos, quoteAmountLength)

        // Load the slippage tolerance and use to get the minimum output amount
        {
          let slippageTolerance := shr(232, calldataload(pos))
          mstore(add(tokenInfo, 0xA0), div(mul(outputQuote, sub(0xFFFFFF, slippageTolerance)), 0xFFFFFF))
        }
        pos := add(pos, 3)

        // Load in the executor address
        executor, pos := getAddress(pos, routerAddress)

        // Load in the destination to send the input to - Zero denotes the executor
        result, pos := getAddress(pos, routerAddress)
        if eq(result, 0) {
          result := executor
        }
        mstore(add(tokenInfo, 0x40), result)

        // Load in the destination to send the output to - Zero denotes msg.sender
        result, pos := getAddress(pos, routerAddress)
        if eq(result, 0) {
          result := msgSender
        }
        mstore(add(tokenInfo, 0xC0), result)
        //
        // we don't need to load the referral code and the pathDefinition
      }
    }
  }
}
"
    },
    "contracts/interfaces/guards/IGuard.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity 0.7.6;

interface IGuard {
  function txGuard(
    address poolManagerLogic,
    address to,
    bytes calldata data
  ) external returns (uint16 txType, bool isPublic); // TODO: eventually update `txType` to be of enum type as per ITransactionTypes
}
"
    },
    "contracts/interfaces/guards/ITxTrackingGuard.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity 0.7.6;

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

interface ITxTrackingGuard is IGuard {
  function isTxTrackingGuard() external view returns (bool);

  function afterTxGuard(address poolManagerLogic, address to, bytes calldata data) external;
}
"
    },
    "contracts/interfaces/IERC20Extended.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity >=0.7.6;

interface IERC20Extended {
  // ERC20 Optional Views
  function name() external view returns (string memory);

  function symbol() external view returns (string memory);

  function decimals() external view returns (uint8);

  // Views
  function totalSupply() external view returns (uint256);

  function balanceOf(address owner) external view returns (uint256);

  function scaledBalanceOf(address user) external view returns (uint256);

  function allowance(address owner, address spender) external view returns (uint256);

  // Mutative functions
  function transfer(address to, uint256 value) external returns (bool);

  function approve(address spender, uint256 value) external returns (bool);

  function transferFrom(address from, address to, uint256 value) external returns (bool);

  // Events
  event Transfer(address indexed from, address indexed to, uint256 value);

  event Approval(address indexed owner, address indexed spender, uint256 value);
}
"
    },
    "contracts/interfaces/IHasAssetInfo.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity >=0.7.6;

interface IHasAssetInfo {
  function isValidAsset(address asset) external view returns (bool);

  function getAssetPrice(address asset) external view returns (uint256);

  function getAssetType(address asset) external view returns (uint16);

  function getMaximumSupportedAssetCount() external view returns (uint256);
}
"
    },
    "contracts/interfaces/IHasGuardInfo.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity 0.7.6;

interface IHasGuardInfo {
  // Get guard
  function getContractGuard(address extContract) external view returns (address);

  // Get asset guard
  function getAssetGuard(address extContract) external view returns (address);
}
"
    },
    "contracts/interfaces/IHasSupportedAsset.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity >=0.7.6;
pragma experimental ABIEncoderV2;

interface IHasSupportedAsset {
  struct Asset {
    address asset;
    bool isDeposit;
  }

  function getSupportedAssets() external view returns (Asset[] memory);

  function isSupportedAsset(address asset) external view returns (bool);
}
"
    },
    "contracts/interfaces/IPoolFactory.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity >=0.7.6;

interface IPoolFactory {
  function governanceAddress() external view returns (address);

  function isPool(address pool) external view returns (bool);

  function customCooldownWhitelist(address from) external view returns (bool);

  function receiverWhitelist(address to) external view returns (bool);

  function emitPoolEvent() external;

  function emitPoolManagerEvent() external;

  function isValidAsset(address asset) external view returns (bool);

  function getAssetPrice(address asset) external view returns (uint256);

  function getAssetHandler() external view returns (address);
}
"
    },
    "contracts/interfaces/IPoolManagerLogic.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity 0.7.6;

interface IPoolManagerLogic {
  function poolLogic() external view returns (address);

  function isDepositAsset(address asset) external view returns (bool);

  function validateAsset(address asset) external view returns (bool);

  function assetValue(address asset) external view returns (uint256);

  function assetValue(address asset, uint256 amount) external view returns (uint256);

  function assetBalance(address asset) external view returns (uint256 balance);

  function factory() external view returns (address);

  function setPoolLogic(address fundAddress) external returns (bool);

  function totalFundValue() external view returns (uint256);

  function isMemberAllowed(address member) external view returns (bool);

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

  function minDepositUSD() external view returns (uint256);

  function getEntryFeeInfo()
    external
    view
    returns (uint256 entryFeeNumerator, uint256 poolFeeShareNumerator, uint256 feeDenominator);

  function getExitFeeInfo()
    external
    view
    returns (uint256 exitFeeNumerator, uint256 poolFeeShareNumerator, uint256 feeDenominator);

  function maxSupplyCap() external view returns (uint256 supplyCap);
}
"
    },
    "contracts/interfaces/ITransactionTypes.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;

/// @title Transaction types used in pool execTransaction() contract guards
/// @dev Gradually migrate to this enum as we update / add new contract guards
interface ITransactionTypes {
  enum TransactionType {
    NotUsed, // 0
    Approve, // 1
    Exchange, // 2
    AddLiquidity, // 3
    RemoveLiquidity, // 4
    Stake, // 5
    Unstake, // 6
    Claim, // 7
    UnstakeAndClaim, // 8
    AaveDeposit, // 9
    AaveWithdraw, // 10
    AaveSetUserUseReserveAsCollateral, // 11
    AaveBorrow, // 12
    AaveRepay, // 13
    AaveSwapBorrowRateMode, // 14
    AaveRebalanceStableBorrowRate, // 15
    BalancerJoinPool, // 16
    BalancerExitPool, // 17
    EasySwapperDeposit, // 18
    EasySwapperWithdraw, // 19
    UniswapV3Mint, // 20
    UniswapV3IncreaseLiquidity, // 21
    UniswapV3DecreaseLiquidity, // 22
    UniswapV3Burn, // 23
    UniswapV3Collect, // 24
    UniswapV3Multicall, // 25
    LyraOpenPosition, // 26
    LyraClosePosition, // 27
    LyraForceClosePosition, // 28
    KwentaFuturesMarket, // 29
    AddLiquiditySingle, // 30
    RemoveLiquiditySingle, // 31
    MaiTx, // 32
    LyraAddCollateral, // 33
    LyraLiquidatePosition, // 34
    KwentaPerpsV2Market, // 35
    RedeemSynth, // 36
    SynthetixV3CreateAccount, // 37
    SynthetixV3DepositCollateral, // 38
    SynthetixV3WithdrawCollateral, // 39
    SynthetixV3DelegateCollateral, // 40
    SynthetixV3MintUSD, // 41
    SynthetixV3BurnUSD, // 42
    SynthetixV3Multicall, // 43
    XRamCreateVest, // 44
    XRamExitVest, // 45
    SynthetixV3Wrap, // 46
    SynthetixV3Unwrap, // 47
    SynthetixV3BuySynth, // 48
    SynthetixV3SellSynth, // 49
    SonneMint, // 50
    SonneRedeem, // 51
    SonneRedeemUnderlying, // 52
    SonneBorrow, // 53
    SonneRepay, // 54
    SonneComptrollerEnterMarkets, // 55
    SonneComptrollerExitMarket, // 56
    SynthetixV3UndelegateCollateral, // 57
    AaveMigrateToV3, // 58
    FlatMoneyStableDeposit, // 59
    FlatMoneyStableWithdraw, // 60
    FlatMoneyCancelOrder, // 61
    SynthetixV3ClaimReward, // 62
    VelodromeCLStake, // 63
    VelodromeCLUnstake, // 64
    VelodromeCLMint, // 65
    VelodromeCLIncreaseLiquidity, // 66
    VelodromeCLDecreaseLiquidity, // 67
    VelodromeCLBurn, // 68
    VelodromeCLCollect, // 69
    VelodromeCLMulticall, // 70
    FlatMoneyLeverageOpen, // 71
    FlatMoneyLeverageAdjust, // 72
    FlatMoneyLeverageClose, // 73
    SynthetixV3PerpsCreateAccount, // 74
    SynthetixV3PerpsModifyCollateral, // 75
    SynthetixV3PerpsCommitOrder, // 76
    CompoundDeposit, // 77
    CompoundWithdraw, // 78
    CompoundClaimRewards, // 79
    RamsesCLMint, // 80
    RamsesCLIncreaseLiquidity, // 81
    RamsesCLDecreaseLiquidity, // 82
    RamsesCLBurn, // 83
    RamsesCLCollect, // 84
    RamsesCLMulticall, // 85
    RamsesCLGetReward, // 86
    EasySwapperV2Deposit, // 87
    EasySwapperV2InitWithdraw, // 88
    EasySwapperV2CompleteWithdrawSingle, // 89
    EasySwapperV2CompleteWithdrawMultiple, // 90
    SafeTransferERC721, // 91
    PancakeCLMint, // 92
    PancakeCLIncreaseLiquidity, // 93
    PancakeCLDecreaseLiquidity, // 94
    PancakeCLBurn, // 95
    PancakeCLCollect, // 96
    PancakeCLMulticall, // 97
    PancakeCLStake, // 98
    PancakeCLUnstake, // 99
    PancakeCLHarvest, // 100
    GmxMulticall, // 101
    GmxClaimFundingFees, // 102
    GmxClaimCollateral, // 103
    GmxCancelOrder, // 104
    GmxCancelDeposit, // 105
    GmxCancelWithdrawal, // 106
    AcrossDepositV3, // 107
    FluidLendingDeposit, // 108
    FluidLendingWithdraw, // 109
    BuyPendlePT, // 110
    SellPendlePT, // 111
    AaveSetEfficiencyMode // 112
  }
}
"
    },
    "contracts/interfaces/odos/IOdosRouterV2.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;

interface IOdosRouterV2 {
  /// @dev Contains all information needed to describe the input and output for a swap
  struct SwapTokenInfo {
    address inputToken;
    uint256 inputAmount;
    address inputReceiver;
    address outputToken;
    uint256 outputQuote;
    uint256 outputMin;
    address outputReceiver;
  }

  /// @notice Custom decoder to swap with compact calldata for efficient execution on L2s
  function swapCompact() external payable returns (uint256);

  /// @notice Externally facing interface for swapping two tokens
  /// @param tokenInfo All information about the tokens being swapped
  /// @param pathDefinition Encoded path definition for executor
  /// @param executor Address of contract that will execute the path
  /// @param referralCode referral code to specify the source of the swap
  function swap(
    SwapTokenInfo memory tokenInfo,
    bytes calldata pathDefinition,
    address executor,
    uint32 referralCode
  ) external payable returns (uint256 amountOut);
}
"
    },
    "contracts/utils/SlippageAccumulator.sol": {
      "content": "//
//        __  __    __  ________  _______    ______   ________
//       /  |/  |  /  |/        |/       \  /      \ /        |
//   ____$$ |$$ |  $$ |$$$$$$$$/ $$$$$$$  |/$$$$$$  |$$$$$$$$/
//  /    $$ |$$ |__$$ |$$ |__    $$ |  $$ |$$ | _$$/ $$ |__
// /$$$$$$$ |$$    $$ |$$    |   $$ |  $$ |$$ |/    |$$    |
// $$ |  $$ |$$$$$$$$ |$$$$$/    $$ |  $$ |$$ |$$$$ |$$$$$/
// $$ \__$$ |$$ |  $$ |$$ |_____ $$ |__$$ |$$ \__$$ |$$ |_____
// $$    $$ |$$ |  $$ |$$       |$$    $$/ $$    $$/ $$       |
//  $$$$$$$/ $$/   $$/ $$$$$$$$/ $$$$$$$/   $$$$$$/  $$$$$$$$/
//
// dHEDGE DAO - https://dhedge.org
//
// Copyright (c) 2025 dHEDGE DAO
//
// SPDX-License-Identifier: MIT

pragma solidity 0.7.6;
pragma abicoder v2;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Math} from "@openzeppelin/contracts/math/Math.sol";
import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol";
import {SafeCast} from "@openzeppelin/contracts/utils/SafeCast.sol";

import {IERC20Extended} from "../interfaces/IERC20Extended.sol";
import {IHasAssetInfo} from "../interfaces/IHasAssetInfo.sol";
import {IHasGuardInfo} from "../interfaces/IHasGuardInfo.sol";
import {IHasSupportedAsset} from "../interfaces/IHasSupportedAsset.sol";

/// @notice Contract to check for accumulated slippage impact for a pool manager.
/// @author dHEDGE
contract SlippageAccumulator is Ownable {
  using SafeMath for *;
  using Math for *;
  using SafeCast for *;

  /// @dev Struct for passing swap related data to the slippage accumulator function.
  /// @param srcAsset Source asset (asset to be exchanged).
  /// @param dstAsset Destination asset (asset being exchanged for).
  /// @param srcAmount Source asset amount.
  /// @param dstAmount Destination asset amount.
  struct SwapData {
    address srcAsset;
    address dstAsset;
    uint256 srcAmount;
    uint256 dstAmount;
  }

  /// @dev Struct to track the slippage data for a poolManager.
  /// @param lastTradeTimestamp Last successful trade's timestamp.
  /// @param accumulatedSlippage The accumulated slippage impact of a poolManager.
  struct ManagerSlippageData {
    uint64 lastTradeTimestamp;
    uint128 accumulatedSlippage;
  }

  event DecayTimeChanged(uint64 oldDecayTime, uint64 newDecayTime);
  event MaxCumulativeSlippageChanged(uint128 oldMaxCumulativeSlippage, uint128 newMaxCumulativeSlippage);

  /// @dev Constant used to multiply the slippage loss percentage with 4 decimal precision.
  uint128 private constant SCALING_FACTOR = 1e6;

  /// @dev dHEDGE poolFactory address.
  address public immutable poolFactory;

  /// @notice Maximum acceptable cumulative trade slippage impact within a period of time (upto 4 decimal precision).
  /// @dev Eg. 5% = 5e4
  uint128 public maxCumulativeSlippage;

  /// @notice Price accumulator decay time.
  /// @dev Eg 6 hours = 21600.
  uint64 public decayTime;

  /// @notice Tracks the last trade timestamp and accumulated slippage for each poolManager.
  mapping(address => ManagerSlippageData) public managerData;

  /// @dev Modifier to make sure caller is a contract guard.
  modifier onlyContractGuard(address to) {
    address contractGuard = IHasGuardInfo(poolFactory).getContractGuard(to);

    require(contractGuard == msg.sender, "Not authorised guard");
    _;
  }

  constructor(address _poolFactory, uint64 _decayTime, uint128 _maxCumulativeSlippage) {
    require(_poolFactory != address(0), "Null address");

    poolFactory = _poolFactory;
    decayTime = _decayTime;
    maxCumulativeSlippage = _maxCumulativeSlippage;
  }

  /// @notice Updates the cumulative slippage impact and reverts if it's beyond limit.
  /// @dev NOTE: It's important that the calling guard checks if the msg.sender in it's scope is authorised.
  /// @dev If the caller is not checked for in the guard, anyone can trigger the `txGuard` transaction and update slippage impact.
  /// @param poolManagerLogic Address of the poolManager whose cumulative impact is stored.
  /// @param router Address of the router contract used for swapping.
  /// @param swapData Common swap data for all guards.
  function updateSlippageImpact(
    address poolManagerLogic,
    address router,
    SwapData calldata swapData
  ) external onlyContractGuard(router) {
    require(swapData.srcAmount != 0, "0 src amount");

    if (IHasSupportedAsset(poolManagerLogic).isSupportedAsset(swapData.srcAsset)) {
      uint256 srcValue = assetValue(swapData.srcAsset, swapData.srcAmount);
      uint256 dstValue = assetValue(swapData.dstAsset, swapData.dstAmount);

      // Only update the cumulative slippage in case the amount received is lesser than amount sent/traded.
      if (dstValue < srcValue) {
        uint128 newSlippage = srcValue.sub(dstValue).mul(SCALING_FACTOR).div(srcValue).toUint128();

        uint128 newCumulativeSlippage = (uint256(newSlippage).add(getCumulativeSlippageImpact(poolManagerLogic)))
          .toUint128();

        require(newCumulativeSlippage < maxCumulativeSlippage, "slippage impact exceeded");

        // Update the last traded timestamp.
        managerData[poolManagerLogic].lastTradeTimestamp = (block.timestamp).toUint64();

        // Update the accumulated slippage impact for the poolManager.
        managerData[poolManagerLogic].accumulatedSlippage = newCumulativeSlippage;
      }
    }
  }

  /// Function to calculate an asset amount's value in usd.
  /// @param asset The asset whose price oracle exists.
  /// @param amount The amount of the `asset`.
  function assetValue(address asset, uint256 amount) public view returns (uint256 value) {
    value = amount.mul(IHasAssetInfo(poolFactory).getAssetPrice(asset)).div(10 ** IERC20Extended(asset).decimals()); // to USD amount
  }

  /// @notice Function to get the cumulative slippage adjusted using decayTime (current cumulative slippage impact).
  /// @param poolManagerLogic Address of the poolManager whose cumulative impact is stored.
  function getCumulativeSlippageImpact(address poolManagerLogic) public view returns (uint128 cumulativeSlippage) {
    ManagerSlippageData memory managerSlippageData = managerData[poolManagerLogic];

    return
      (
        uint256(managerSlippageData.accumulatedSlippage)
          .mul(decayTime.sub(decayTime.min(block.timestamp.sub(managerSlippageData.lastTradeTimestamp))))
          .div(decayTime)
      ).toUint128();
  }

  /**********************************************
   *             Owner Functions                *
   *********************************************/

  /// @notice Function to change decay time for calculating price impact.
  /// @param newDecayTime The new decay time (in seconds).
  function setDecayTime(uint64 newDecayTime) external onlyOwner {
    uint64 oldDecayTime = decayTime;

    decayTime = newDecayTime;

    emit DecayTimeChanged(oldDecayTime, newDecayTime);
  }

  /// @notice Function to change the max acceptable cumulative slippage impact.
  /// @param newMaxCumulativeSlippage The new max acceptable cumulative slippage impact.
  function setMaxCumulativeSlippage(uint128 newMaxCumulativeSlippage) external onlyOwner {
    uint128 oldMaxCumulativeSlippage = maxCumulativeSlippage;

    maxCumulativeSlippage = newMaxCumulativeSlippage;

    emit MaxCumulativeSlippageChanged(oldMaxCumulativeSlippage, newMaxCumulativeSlippage);
  }
}
"
    },
    "contracts/utils/SlippageAccumulatorUser.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;

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

import {IPoolFactory} from "../interfaces/IPoolFactory.sol";
import {IPoolManagerLogic} from "../interfaces/IPoolManagerLogic.sol";
import {ITxTrackingGuard} from "../interfaces/guards/ITxTrackingGuard.sol";

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

abstract contract SlippageAccumulatorUser is ITxTrackingGuard {
  using SafeMath for *;

  bool public override isTxTrackingGuard = true;

  SlippageAccumulator internal immutable slippageAccumulator;

  /// @dev Note that the intermediateSwapData is used to store the data temporarily
  ///      after a swap is completed, this is used to update the slippage impact.
  ///      the `dstAmount` stored in this struct before execution of `afterTxGuard` is the prior balance of the pool for the destination asset.
  SlippageAccumulator.SwapData internal intermediateSwapData;

  constructor(address _slippageAccumulator) {
    require(_slippageAccumulator != address(0), "invalid address");

    slippageAccumulator = SlippageAccumulator(_slippageAccumulator);
  }

  function afterTxGuard(address poolManagerLogic, address to, bytes memory /* data */) public virtual override {
    address poolLogic = _accessControl(poolManagerLogic);

    slippageAccumulator.updateSlippageImpact(
      poolManagerLogic,
      to,
      SlippageAccumulator.SwapData({
        srcAsset: intermediateSwapData.srcAsset,
        dstAsset: intermediateSwapData.dstAsset,
        srcAmount: intermediateSwapData.srcAmount.sub(_getBalance(intermediateSwapData.srcAsset, poolLogic)),
        dstAmount: _getBalance(intermediateSwapData.dstAsset, poolLogic).sub(intermediateSwapData.dstAmount)
      })
    );
    intermediateSwapData = SlippageAccumulator.SwapData(address(0), address(0), 0, 0);
  }

  function _getBalance(address token, address holder) internal view returns (uint256) {
    // This is to avoid reverts during wrap/unwrap attempts via 1inch (which still should revert downstream)
    return (token == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) ? holder.balance : IERC20(token).balanceOf(holder);
  }

  function _accessControl(address poolManagerLogic) internal view returns (address poolLogic) {
    poolLogic = IPoolManagerLogic(poolManagerLogic).poolLogic();

    require(
      msg.sender == poolLogic && IPoolFactory(slippageAccumulator.poolFactory()).isPool(poolLogic),
      "not pool logic"
    );
  }
}
"
    },
    "contracts/utils/TxDataUtils.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity 0.7.6;

import {BytesLib} from "@uniswap/v3-periphery/contracts/libraries/BytesLib.sol";

contract TxDataUtils {
  using BytesLib for bytes;

  function getMethod(bytes memory data) public pure returns (bytes4) {
    return read4left(data, 0);
  }

  function getParams(bytes memory data) public pure returns (bytes memory) {
    return data.slice(4, data.length - 4);
  }

  function getInput(bytes memory data, uint8 inputNum) public pure returns (bytes32) {
    return read32(data, 32 * inputNum + 4, 32);
  }

  function getBytes(bytes memory data, uint8 inputNum, uint256 offset) public pure returns (bytes memory) {
    require(offset < 20, "invalid offset"); // offset is in byte32 slots, not bytes
    offset = offset * 32; // convert offset to bytes
    uint256 bytesLenPos = uint256(read32(data, 32 * inputNum + 4 + offset, 32));
    uint256 bytesLen = uint256(read32(data, bytesLenPos + 4 + offset, 32));
    return data.slice(bytesLenPos + 4 + offset + 32, bytesLen);
  }

  function getArrayLast(bytes memory data, uint8 inputNum) public pure returns (bytes32) {
    bytes32 arrayPos = read32(data, 32 * inputNum + 4, 32);
    bytes32 arrayLen = read32(data, uint256(arrayPos) + 4, 32);
    require(arrayLen > 0, "input is not array");
    return read32(data, uint256(arrayPos) + 4 + (uint256(arrayLen) * 32), 32);
  }

  function getArrayLength(bytes memory data, uint8 inputNum) public pure returns (uint256) {
    bytes32 arrayPos = read32(data, 32 * inputNum + 4, 32);
    return uint256(read32(data, uint256(arrayPos) + 4, 32));
  }

  function getArrayIndex(bytes memory data, uint8 inputNum, uint8 arrayIndex) public pure returns (bytes32) {
    bytes32 arrayPos = read32(data, 32 * inputNum + 4, 32);
    bytes32 arrayLen = read32(data, uint256(arrayPos) + 4, 32);
    require(arrayLen > 0, "input is not array");
    require(uint256(arrayLen) > arrayIndex, "invalid array position");
    return read32(data, uint256(arrayPos) + 4 + ((1 + uint256(arrayIndex)) * 32), 32);
  }

  function read4left(bytes memory data, uint256 offset) public pure returns (bytes4 o) {
    require(data.length >= offset + 4, "Reading bytes out of bounds");
    assembly {
      o := mload(add(data, add(32, offset)))
    }
  }

  function read32(bytes memory data, uint256 offset, uint256 length) public pure returns (bytes32 o) {
    require(data.length >= offset + length, "Reading bytes out of bounds");
    assembly {
      o := mload(add(data, add(32, offset)))
      let lb := sub(32, length)
      if lb {
        o := div(o, exp(2, mul(lb, 8)))
      }
    }
  }

  function convert32toAddress(bytes32 data) public pure returns (address o) {
    return address(uint160(uint256(data)));
  }

  function sliceUint(bytes memory data, uint256 start) internal pure returns (uint256) {
    require(data.length >= start + 32, "slicing out of range");
    uint256 x;
    assembly {
      x := mload(add(data, add(0x20, start)))
    }
    return x;
  }
}
"
    }
  },
  "settings": {
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    },
    "optimizer": {
      "enabled": true,
      "runs": 20
    }
  }
}}

Tags:
ERC20, Multisig, Swap, Multi-Signature, Factory, Oracle|addr:0xfb227c573d4598d03370892038f44b5d8a9e9738|verified:true|block:23418767|tx:0x3ff1ccd8e5db9ce7e5bc38ccd3c975549ef95bd36c69146e75a2f29f27161af8|first_check:1758547033

Submitted on: 2025-09-22 15:17:14

Comments

Log in to comment.

No comments yet.