ETH Price: $2,870.19 (-2.38%)

Contract

0x7b42Ed932f26509465F7cE3FAF76FfCe1275312f
Transaction Hash
Block
From
To
Swap Exact In379052582026-01-17 12:40:178 days ago1768653617IN
0x7b42Ed93...e1275312f
0 ETH00.00000035
Swap Exact In378778972026-01-17 5:04:169 days ago1768626256IN
0x7b42Ed93...e1275312f
0 ETH00.00000035
Swap Exact In378778552026-01-17 5:03:349 days ago1768626214IN
0x7b42Ed93...e1275312f
0 ETH00.00000035
Swap Exact In377906022026-01-16 4:49:2110 days ago1768538961IN
0x7b42Ed93...e1275312f
0 ETH00.00000035
Swap Exact In377905622026-01-16 4:48:4110 days ago1768538921IN
0x7b42Ed93...e1275312f
0 ETH00.00000035
Swap Exact In377767232026-01-16 0:58:0210 days ago1768525082IN
0x7b42Ed93...e1275312f
0 ETH00.00000035
Swap Exact In377766772026-01-16 0:57:1610 days ago1768525036IN
0x7b42Ed93...e1275312f
0 ETH00.00000035
Swap Exact In377759242026-01-16 0:44:4310 days ago1768524283IN
0x7b42Ed93...e1275312f
0 ETH00.00000031
Swap Exact In377672582026-01-15 22:20:1710 days ago1768515617IN
0x7b42Ed93...e1275312f
0 ETH00.00000031
Swap Exact In377385982026-01-15 14:22:3710 days ago1768486957IN
0x7b42Ed93...e1275312f
0 ETH0.000000010.00010031
Swap Exact In377384442026-01-15 14:20:0310 days ago1768486803IN
0x7b42Ed93...e1275312f
0 ETH0.000000010.00010031
Swap Exact In375198962026-01-13 1:37:3513 days ago1768268255IN
0x7b42Ed93...e1275312f
0 ETH00.00000386
Swap Exact In375197722026-01-13 1:35:3113 days ago1768268131IN
0x7b42Ed93...e1275312f
0 ETH00.00000386
Swap Exact In374590542026-01-12 8:43:3313 days ago1768207413IN
0x7b42Ed93...e1275312f
0 ETH0.000000010.0001003
Swap Exact In374588772026-01-12 8:40:3613 days ago1768207236IN
0x7b42Ed93...e1275312f
0 ETH00.0001003
Swap Exact In374464502026-01-12 5:13:2914 days ago1768194809IN
0x7b42Ed93...e1275312f
0 ETH0.000000110.0013
Swap Exact In374464272026-01-12 5:13:0614 days ago1768194786IN
0x7b42Ed93...e1275312f
0 ETH0.000000110.0013
Swap Exact In374450182026-01-12 4:49:3714 days ago1768193377IN
0x7b42Ed93...e1275312f
0 ETH00.00000042
Swap Exact In374449942026-01-12 4:49:1314 days ago1768193353IN
0x7b42Ed93...e1275312f
0 ETH00.00000386
Swap Exact In373602252026-01-11 5:16:2415 days ago1768108584IN
0x7b42Ed93...e1275312f
0 ETH00.00000035
Swap Exact In373602002026-01-11 5:15:5915 days ago1768108559IN
0x7b42Ed93...e1275312f
0 ETH00.00000035
Swap Exact In372864222026-01-10 8:46:2115 days ago1768034781IN
0x7b42Ed93...e1275312f
0 ETH0.000000010.00010025
Swap Exact In372327852026-01-09 17:52:2416 days ago1767981144IN
0x7b42Ed93...e1275312f
0 ETH0.000000010.00010025
Swap Exact In371838642026-01-09 4:17:0317 days ago1767932223IN
0x7b42Ed93...e1275312f
0 ETH00.00000036
Swap Exact In371838392026-01-09 4:16:3817 days ago1767932198IN
0x7b42Ed93...e1275312f
0 ETH00.00000035
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
386586552026-01-26 5:56:5415 mins ago1769407014
0x7b42Ed93...e1275312f
0 ETH
386586552026-01-26 5:56:5415 mins ago1769407014
0x7b42Ed93...e1275312f
0 ETH
385786632026-01-25 7:43:4222 hrs ago1769327022
0x7b42Ed93...e1275312f
0 ETH
385786632026-01-25 7:43:4222 hrs ago1769327022
0x7b42Ed93...e1275312f
0 ETH
385786632026-01-25 7:43:4222 hrs ago1769327022
0x7b42Ed93...e1275312f
0 ETH
385786632026-01-25 7:43:4222 hrs ago1769327022
0x7b42Ed93...e1275312f
0 ETH
385786632026-01-25 7:43:4222 hrs ago1769327022
0x7b42Ed93...e1275312f
0 ETH
385786632026-01-25 7:43:4222 hrs ago1769327022
0x7b42Ed93...e1275312f
0 ETH
385786632026-01-25 7:43:4222 hrs ago1769327022
0x7b42Ed93...e1275312f
0 ETH
385785082026-01-25 7:41:0722 hrs ago1769326867
0x7b42Ed93...e1275312f
0 ETH
385785082026-01-25 7:41:0722 hrs ago1769326867
0x7b42Ed93...e1275312f
0 ETH
385785082026-01-25 7:41:0722 hrs ago1769326867
0x7b42Ed93...e1275312f
0 ETH
385785082026-01-25 7:41:0722 hrs ago1769326867
0x7b42Ed93...e1275312f
0 ETH
385721932026-01-25 5:55:5224 hrs ago1769320552
0x7b42Ed93...e1275312f
0 ETH
385721932026-01-25 5:55:5224 hrs ago1769320552
0x7b42Ed93...e1275312f
0 ETH
385721932026-01-25 5:55:5224 hrs ago1769320552
0x7b42Ed93...e1275312f
0 ETH
385721932026-01-25 5:55:5224 hrs ago1769320552
0x7b42Ed93...e1275312f
0 ETH
385721932026-01-25 5:55:5224 hrs ago1769320552
0x7b42Ed93...e1275312f
0 ETH
385721932026-01-25 5:55:5224 hrs ago1769320552
0x7b42Ed93...e1275312f
0 ETH
385720342026-01-25 5:53:1324 hrs ago1769320393
0x7b42Ed93...e1275312f
0 ETH
385720342026-01-25 5:53:1324 hrs ago1769320393
0x7b42Ed93...e1275312f
0 ETH
385720342026-01-25 5:53:1324 hrs ago1769320393
0x7b42Ed93...e1275312f
0 ETH
385720342026-01-25 5:53:1324 hrs ago1769320393
0x7b42Ed93...e1275312f
0 ETH
385533522026-01-25 0:41:5129 hrs ago1769301711
0x7b42Ed93...e1275312f
0 ETH
385533522026-01-25 0:41:5129 hrs ago1769301711
0x7b42Ed93...e1275312f
0 ETH
View All Internal Transactions

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
PSM3

Compiler Version
v0.8.20+commit.a1b79de6

Optimization Enabled:
Yes with 200 runs

Other Settings:
shanghai EvmVersion
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.13;

import { IERC20 } from "erc20-helpers/interfaces/IERC20.sol";

import { SafeERC20 } from "erc20-helpers/SafeERC20.sol";

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

import { IPSM3 }             from "src/interfaces/IPSM3.sol";
import { IRateProviderLike } from "src/interfaces/IRateProviderLike.sol";

/*
    ███████╗██████╗  █████╗ ██████╗ ██╗  ██╗    ██████╗ ███████╗███╗   ███╗
    ██╔════╝██╔══██╗██╔══██╗██╔══██╗██║ ██╔╝    ██╔══██╗██╔════╝████╗ ████║
    ███████╗██████╔╝███████║██████╔╝█████╔╝     ██████╔╝███████╗██╔████╔██║
    ╚════██║██╔═══╝ ██╔══██║██╔══██╗██╔═██╗     ██╔═══╝ ╚════██║██║╚██╔╝██║
    ███████║██║     ██║  ██║██║  ██║██║  ██╗    ██║     ███████║██║ ╚═╝ ██║
    ╚══════╝╚═╝     ╚═╝  ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝    ╚═╝     ╚══════╝╚═╝     ╚═╝
*/

contract PSM3 is IPSM3, Ownable {

    using SafeERC20 for IERC20;

    uint256 internal immutable _usdcPrecision;
    uint256 internal immutable _usdsPrecision;
    uint256 internal immutable _susdsPrecision;

    IERC20 public override immutable usdc;
    IERC20 public override immutable usds;
    IERC20 public override immutable susds;

    address public override immutable rateProvider;

    address public override pocket;

    uint256 public override totalShares;

    mapping(address user => uint256 shares) public override shares;

    constructor(
        address owner_,
        address usdc_,
        address usds_,
        address susds_,
        address rateProvider_
    )
        Ownable(owner_)
    {
        require(usdc_         != address(0), "PSM3/invalid-usdc");
        require(usds_         != address(0), "PSM3/invalid-usds");
        require(susds_        != address(0), "PSM3/invalid-susds");
        require(rateProvider_ != address(0), "PSM3/invalid-rateProvider");

        require(usdc_ != usds_,  "PSM3/usdc-usds-same");
        require(usdc_ != susds_, "PSM3/usdc-susds-same");
        require(usds_ != susds_, "PSM3/usds-susds-same");

        usdc  = IERC20(usdc_);
        usds  = IERC20(usds_);
        susds = IERC20(susds_);

        rateProvider = rateProvider_;
        pocket       = address(this);

        require(
            IRateProviderLike(rateProvider_).getConversionRate() != 0,
            "PSM3/rate-provider-returns-zero"
        );

        _usdcPrecision  = 10 ** IERC20(usdc_).decimals();
        _usdsPrecision  = 10 ** IERC20(usds_).decimals();
        _susdsPrecision = 10 ** IERC20(susds_).decimals();

        // Necessary to ensure rounding works as expected
        require(_usdcPrecision <= 1e18, "PSM3/usdc-precision-too-high");
        require(_usdsPrecision <= 1e18, "PSM3/usds-precision-too-high");
    }

    /**********************************************************************************************/
    /*** Owner functions                                                                        ***/
    /**********************************************************************************************/

    function setPocket(address newPocket) external override onlyOwner {
        require(newPocket != address(0), "PSM3/invalid-pocket");

        address pocket_ = pocket;

        require(newPocket != pocket_, "PSM3/same-pocket");

        uint256 amountToTransfer = usdc.balanceOf(pocket_);

        if (pocket_ == address(this)) {
            usdc.safeTransfer(newPocket, amountToTransfer);
        } else {
            usdc.safeTransferFrom(pocket_, newPocket, amountToTransfer);
        }

        pocket = newPocket;

        emit PocketSet(pocket_, newPocket, amountToTransfer);
    }

    /**********************************************************************************************/
    /*** Swap functions                                                                         ***/
    /**********************************************************************************************/

    function swapExactIn(
        address assetIn,
        address assetOut,
        uint256 amountIn,
        uint256 minAmountOut,
        address receiver,
        uint256 referralCode
    )
        external override returns (uint256 amountOut)
    {
        require(amountIn != 0,          "PSM3/invalid-amountIn");
        require(receiver != address(0), "PSM3/invalid-receiver");

        amountOut = previewSwapExactIn(assetIn, assetOut, amountIn);

        require(amountOut >= minAmountOut, "PSM3/amountOut-too-low");

        _pullAsset(assetIn, amountIn);
        _pushAsset(assetOut, receiver, amountOut);

        emit Swap(assetIn, assetOut, msg.sender, receiver, amountIn, amountOut, referralCode);
    }

    function swapExactOut(
        address assetIn,
        address assetOut,
        uint256 amountOut,
        uint256 maxAmountIn,
        address receiver,
        uint256 referralCode
    )
        external override returns (uint256 amountIn)
    {
        require(amountOut != 0,         "PSM3/invalid-amountOut");
        require(receiver != address(0), "PSM3/invalid-receiver");

        amountIn = previewSwapExactOut(assetIn, assetOut, amountOut);

        require(amountIn <= maxAmountIn, "PSM3/amountIn-too-high");

        _pullAsset(assetIn, amountIn);
        _pushAsset(assetOut, receiver, amountOut);

        emit Swap(assetIn, assetOut, msg.sender, receiver, amountIn, amountOut, referralCode);
    }

    /**********************************************************************************************/
    /*** Liquidity provision functions                                                          ***/
    /**********************************************************************************************/

    function deposit(address asset, address receiver, uint256 assetsToDeposit)
        external override returns (uint256 newShares)
    {
        require(assetsToDeposit != 0, "PSM3/invalid-amount");

        newShares = previewDeposit(asset, assetsToDeposit);

        shares[receiver] += newShares;
        totalShares      += newShares;

        _pullAsset(asset, assetsToDeposit);

        emit Deposit(asset, msg.sender, receiver, assetsToDeposit, newShares);
    }

    function withdraw(address asset, address receiver, uint256 maxAssetsToWithdraw)
        external override returns (uint256 assetsWithdrawn)
    {
        require(maxAssetsToWithdraw != 0, "PSM3/invalid-amount");

        uint256 sharesToBurn;

        ( sharesToBurn, assetsWithdrawn ) = previewWithdraw(asset, maxAssetsToWithdraw);

        // `previewWithdraw` ensures that `sharesToBurn` <= `shares[msg.sender]`
        unchecked {
            shares[msg.sender] -= sharesToBurn;
            totalShares        -= sharesToBurn;
        }

        _pushAsset(asset, receiver, assetsWithdrawn);

        emit Withdraw(asset, msg.sender, receiver, assetsWithdrawn, sharesToBurn);
    }

    /**********************************************************************************************/
    /*** Deposit/withdraw preview functions                                                     ***/
    /**********************************************************************************************/

    function previewDeposit(address asset, uint256 assetsToDeposit)
        public view override returns (uint256)
    {
        // Convert amount to 1e18 precision denominated in value of USD then convert to shares.
        // NOTE: Don't need to check valid asset here since `_getAssetValue` will revert if invalid
        return convertToShares(_getAssetValue(asset, assetsToDeposit, false));  // Round down
    }

    function previewWithdraw(address asset, uint256 maxAssetsToWithdraw)
        public view override returns (uint256 sharesToBurn, uint256 assetsWithdrawn)
    {
        require(_isValidAsset(asset), "PSM3/invalid-asset");

        uint256 assetBalance = IERC20(asset).balanceOf(_getAssetCustodian(asset));

        assetsWithdrawn = assetBalance < maxAssetsToWithdraw
            ? assetBalance
            : maxAssetsToWithdraw;

        // Get shares to burn, rounding up for both calculations
        sharesToBurn = _convertToSharesRoundUp(_getAssetValue(asset, assetsWithdrawn, true));

        uint256 userShares = shares[msg.sender];

        if (sharesToBurn > userShares) {
            assetsWithdrawn = convertToAssets(asset, userShares);
            sharesToBurn    = userShares;
        }
    }

    /**********************************************************************************************/
    /*** Swap preview functions                                                                 ***/
    /**********************************************************************************************/

    function previewSwapExactIn(address assetIn, address assetOut, uint256 amountIn)
        public view override returns (uint256 amountOut)
    {
        // Round down to get amountOut
        amountOut = _getSwapQuote(assetIn, assetOut, amountIn, false);
    }

    function previewSwapExactOut(address assetIn, address assetOut, uint256 amountOut)
        public view override returns (uint256 amountIn)
    {
        // Round up to get amountIn
        amountIn = _getSwapQuote(assetOut, assetIn, amountOut, true);
    }

    /**********************************************************************************************/
    /*** Conversion functions                                                                   ***/
    /**********************************************************************************************/

    function convertToAssets(address asset, uint256 numShares)
        public view override returns (uint256)
    {
        require(_isValidAsset(asset), "PSM3/invalid-asset");

        uint256 assetValue = convertToAssetValue(numShares);

        if      (asset == address(usdc)) return assetValue * _usdcPrecision / 1e18;
        else if (asset == address(usds)) return assetValue * _usdsPrecision / 1e18;

        // NOTE: Multiplying by 1e27 and dividing by 1e18 cancels to 1e9 in numerator
        return assetValue
            * 1e9
            * _susdsPrecision
            / IRateProviderLike(rateProvider).getConversionRate();
    }

    function convertToAssetValue(uint256 numShares) public view override returns (uint256) {
        uint256 totalShares_ = totalShares;

        if (totalShares_ != 0) {
            return numShares * totalAssets() / totalShares_;
        }
        return numShares;
    }

    function convertToShares(uint256 assetValue) public view override returns (uint256) {
        uint256 totalAssets_ = totalAssets();
        if (totalAssets_ != 0) {
            return assetValue * totalShares / totalAssets_;
        }
        return assetValue;
    }

    function convertToShares(address asset, uint256 assets) public view override returns (uint256) {
        require(_isValidAsset(asset), "PSM3/invalid-asset");
        return convertToShares(_getAssetValue(asset, assets, false));  // Round down
    }

    /**********************************************************************************************/
    /*** Asset value functions                                                                  ***/
    /**********************************************************************************************/

    function totalAssets() public view override returns (uint256) {
        return _getUsdcValue(usdc.balanceOf(pocket))
            +  _getUsdsValue(usds.balanceOf(address(this)))
            +  _getSUsdsValue(susds.balanceOf(address(this)), false);  // Round down
    }

    /**********************************************************************************************/
    /*** Internal valuation functions (deposit/withdraw)                                        ***/
    /**********************************************************************************************/

    function _getAssetValue(address asset, uint256 amount, bool roundUp) internal view returns (uint256) {
        if      (asset == address(usdc))  return _getUsdcValue(amount);
        else if (asset == address(usds))  return _getUsdsValue(amount);
        else if (asset == address(susds)) return _getSUsdsValue(amount, roundUp);
        else revert("PSM3/invalid-asset-for-value");
    }

    function _getUsdcValue(uint256 amount) internal view returns (uint256) {
        return amount * 1e18 / _usdcPrecision;
    }

    function _getUsdsValue(uint256 amount) internal view returns (uint256) {
        return amount * 1e18 / _usdsPrecision;
    }

    function _getSUsdsValue(uint256 amount, bool roundUp) internal view returns (uint256) {
        // NOTE: Multiplying by 1e18 and dividing by 1e27 cancels to 1e9 in denominator
        if (!roundUp) return amount
            * IRateProviderLike(rateProvider).getConversionRate()
            / 1e9
            / _susdsPrecision;

        return Math.ceilDiv(
            Math.ceilDiv(amount * IRateProviderLike(rateProvider).getConversionRate(), 1e9),
            _susdsPrecision
        );
    }

    /**********************************************************************************************/
    /*** Internal preview functions (swaps)                                                     ***/
    /**********************************************************************************************/

    function _getSwapQuote(address asset, address quoteAsset, uint256 amount, bool roundUp)
        internal view returns (uint256 quoteAmount)
    {
        if (asset == address(usdc)) {
            if      (quoteAsset == address(usds))  return _convertOneToOne(amount, _usdcPrecision, _usdsPrecision, roundUp);
            else if (quoteAsset == address(susds)) return _convertToSUsds(amount, _usdcPrecision, roundUp);
        }

        else if (asset == address(usds)) {
            if      (quoteAsset == address(usdc))  return _convertOneToOne(amount, _usdsPrecision, _usdcPrecision, roundUp);
            else if (quoteAsset == address(susds)) return _convertToSUsds(amount, _usdsPrecision, roundUp);
        }

        else if (asset == address(susds)) {
            if      (quoteAsset == address(usdc)) return _convertFromSUsds(amount, _usdcPrecision, roundUp);
            else if (quoteAsset == address(usds)) return _convertFromSUsds(amount, _usdsPrecision, roundUp);
        }

        revert("PSM3/invalid-asset");
    }

    function _convertToSUsds(uint256 amount, uint256 assetPrecision, bool roundUp)
        internal view returns (uint256)
    {
        uint256 rate = IRateProviderLike(rateProvider).getConversionRate();

        if (!roundUp) return amount * 1e27 / rate * _susdsPrecision / assetPrecision;

        return Math.ceilDiv(
            Math.ceilDiv(amount * 1e27, rate) * _susdsPrecision,
            assetPrecision
        );
    }

    function _convertFromSUsds(uint256 amount, uint256 assetPrecision, bool roundUp)
        internal view returns (uint256)
    {
        uint256 rate = IRateProviderLike(rateProvider).getConversionRate();

        if (!roundUp) return amount * rate / 1e27 * assetPrecision / _susdsPrecision;

        return Math.ceilDiv(
            Math.ceilDiv(amount * rate, 1e27) * assetPrecision,
            _susdsPrecision
        );
    }

    function _convertOneToOne(
        uint256 amount,
        uint256 assetPrecision,
        uint256 convertAssetPrecision,
        bool roundUp
    )
        internal pure returns (uint256)
    {
        if (!roundUp) return amount * convertAssetPrecision / assetPrecision;

        return Math.ceilDiv(amount * convertAssetPrecision, assetPrecision);
    }

    /**********************************************************************************************/
    /*** Internal helper functions                                                              ***/
    /**********************************************************************************************/

    function _convertToSharesRoundUp(uint256 assetValue) internal view returns (uint256) {
        uint256 totalValue = totalAssets();
        if (totalValue != 0) {
            return Math.ceilDiv(assetValue * totalShares, totalValue);
        }
        return assetValue;
    }

    function _isValidAsset(address asset) internal view returns (bool) {
        return asset == address(usdc) || asset == address(usds) || asset == address(susds);
    }

    function _getAssetCustodian(address asset) internal view returns (address custodian) {
        custodian = asset == address(usdc) ? pocket : address(this);
    }

    function _pullAsset(address asset, uint256 amount) internal {
        IERC20(asset).safeTransferFrom(msg.sender, _getAssetCustodian(asset), amount);
    }

    function _pushAsset(address asset, address receiver, uint256 amount) internal {
        if (asset == address(usdc) && pocket != address(this)) {
            usdc.safeTransferFrom(pocket, receiver, amount);
        } else {
            IERC20(asset).safeTransfer(receiver, amount);
        }
    }

}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/*
 *  @title Interface of the ERC20 standard as defined in the EIP,
 *         including EIP-2612 permit functionality.
 */
interface IERC20 {

    /**********************************************************************************************/
    /*** Events                                                                                 ***/
    /**********************************************************************************************/

    /**
     *  @dev   Emitted when one account has set the allowance of another account over their tokens.
     *  @param owner   Account that tokens are approved from.
     *  @param spender Account that tokens are approved for.
     *  @param amount  Amount of tokens that have been approved.
     */
    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /**
     *  @dev   Emitted when tokens have moved from one account to another.
     *  @param owner     Account that tokens have moved from.
     *  @param recipient Account that tokens have moved to.
     *  @param amount    Amount of tokens that have been transferred.
     */
    event Transfer(address indexed owner, address indexed recipient, uint256 amount);

    /**********************************************************************************************/
    /*** External Functions                                                                     ***/
    /**********************************************************************************************/

    /**
     *  @dev    Function that allows one account to set the allowance of another account
     *          over their tokens.
     *          Emits an {Approval} event.
     *  @param  spender Account that tokens are approved for.
     *  @param  amount  Amount of tokens that have been approved.
     *  @return success Boolean indicating whether the operation succeeded.
     */
    function approve(address spender, uint256 amount) external returns (bool success);

    /**
     *  @dev    Function that allows one account to decrease the allowance of another
     *          account over their tokens.
     *          Emits an {Approval} event.
     *  @param  spender          Account that tokens are approved for.
     *  @param  subtractedAmount Amount to decrease approval by.
     *  @return success          Boolean indicating whether the operation succeeded.
     */
    function decreaseAllowance(address spender, uint256 subtractedAmount)
        external returns (bool success);

    /**
     *  @dev    Function that allows one account to increase the allowance of another
     *          account over their tokens.
     *          Emits an {Approval} event.
     *  @param  spender     Account that tokens are approved for.
     *  @param  addedAmount Amount to increase approval by.
     *  @return success     Boolean indicating whether the operation succeeded.
     */
    function increaseAllowance(address spender, uint256 addedAmount)
        external returns (bool success);

    /**
     *  @dev   Approve by signature.
     *  @param owner    Owner address that signed the permit.
     *  @param spender  Spender of the permit.
     *  @param amount   Permit approval spend limit.
     *  @param deadline Deadline after which the permit is invalid.
     *  @param v        ECDSA signature v component.
     *  @param r        ECDSA signature r component.
     *  @param s        ECDSA signature s component.
     */
    function permit(
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8   v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     *  @dev    Moves an amount of tokens from `msg.sender` to a specified account.
     *          Emits a {Transfer} event.
     *  @param  recipient Account that receives tokens.
     *  @param  amount    Amount of tokens that are transferred.
     *  @return success   Boolean indicating whether the operation succeeded.
     */
    function transfer(address recipient, uint256 amount) external returns (bool success);

    /**
     *  @dev    Moves a pre-approved amount of tokens from a sender to a specified account.
     *          Emits a {Transfer} event.
     *          Emits an {Approval} event.
     *  @param  owner     Account that tokens are moving from.
     *  @param  recipient Account that receives tokens.
     *  @param  amount    Amount of tokens that are transferred.
     *  @return success   Boolean indicating whether the operation succeeded.
     */
    function transferFrom(address owner, address recipient, uint256 amount)
        external returns (bool success);

    /**********************************************************************************************/
    /*** View Functions                                                                         ***/
    /**********************************************************************************************/

    /**
     *  @dev    Returns the allowance that one account has given another over their tokens.
     *  @param  owner     Account that tokens are approved from.
     *  @param  spender   Account that tokens are approved for.
     *  @return allowance Allowance that one account has given another over their tokens.
     */
    function allowance(address owner, address spender) external view returns (uint256 allowance);

    /**
     *  @dev    Returns the amount of tokens owned by a given account.
     *  @param  account Account that owns the tokens.
     *  @return balance Amount of tokens owned by a given account.
     */
    function balanceOf(address account) external view returns (uint256 balance);

    /**
     *  @dev    Returns the decimal precision used by the token.
     *  @return decimals The decimal precision used by the token.
     */
    function decimals() external view returns (uint8 decimals);

    /**
     *  @dev    Returns the signature domain separator.
     *  @return domainSeparator The signature domain separator.
     */
    function DOMAIN_SEPARATOR() external view returns (bytes32 domainSeparator);

    /**
     *  @dev    Returns the name of the token.
     *  @return name The name of the token.
     */
    function name() external view returns (string memory name);

    /**
      *  @dev    Returns the nonce for the given owner.
      *  @param  owner  The address of the owner account.
      *  @return nonce The nonce for the given owner.
     */
    function nonces(address owner) external view returns (uint256 nonce);

    /**
     *  @dev    Returns the permit type hash.
     *  @return permitTypehash The permit type hash.
     */
    function PERMIT_TYPEHASH() external view returns (bytes32 permitTypehash);

    /**
     *  @dev    Returns the symbol of the token.
     *  @return symbol The symbol of the token.
     */
    function symbol() external view returns (string memory symbol);

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

}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

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

/**
 *  @title Small library to standardize erc20 token interactions.
 */
library SafeERC20 {

    /**********************************************************************************************/
    /*** Internal Functions                                                                     ***/
    /**********************************************************************************************/

    function safeTransfer(address token, address to, uint256 amount) internal {
        require(
            _call(token, abi.encodeCall(IERC20.transfer, (to, amount))),
            "SafeERC20/transfer-failed"
        );
    }

    function safeTransfer(IERC20 token, address to, uint256 amount) internal {
        safeTransfer(address(token), to, amount);
    }

    function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
        require(
            _call(token, abi.encodeCall(IERC20.transferFrom, (from, to, amount))),
            "SafeERC20/transfer-from-failed"
        );
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint256 amount) internal {
        safeTransferFrom(address(token), from, to, amount);
    }

    function safeApprove(address token, address spender, uint256 amount) internal {
        bytes memory approvalCall
            = abi.encodeCall(IERC20.approve, (spender, amount));

        // Try to call approve with amount, if that doesn't work, set to 0 and then to amount.
        if (!_call(token, approvalCall)) {
            require(
                _call(token, abi.encodeCall(IERC20.approve, (spender, 0))),
                "SafeERC20/approve-zero-failed"
            );
            require(
                _call(token, approvalCall),
                "SafeERC20/approve-amount-failed"
            );
        }
    }

    function safeApprove(IERC20 token, address spender, uint256 amount) internal {
        safeApprove(address(token), spender, amount);
    }

    /**********************************************************************************************/
    /*** Private Functions                                                                      ***/
    /**********************************************************************************************/

    function _call(address token, bytes memory data_) private returns (bool success) {
        if (token.code.length == 0) return false;

        bytes memory returnData;
        ( success, returnData ) = token.call(data_);

        return success && (returnData.length == 0 || abi.decode(returnData, (bool)));
    }

}

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

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

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // 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.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            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.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @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.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            return a / b;
        }

        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
     * Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
            // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.

            uint256 twos = denominator & (0 - denominator);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
            // works in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
        }
    }

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}

// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.13;

import { IERC20 } from "erc20-helpers/interfaces/IERC20.sol";

interface IPSM3 {

    /**********************************************************************************************/
    /*** Events                                                                                 ***/
    /**********************************************************************************************/

    /**
     *  @dev   Emitted when a new pocket is set in the PSM, transferring the balance of USDC.
     *         of the old pocket to the new pocket.
     *  @param oldPocket         Address of the old `pocket`.
     *  @param newPocket         Address of the new `pocket`.
     *  @param amountTransferred Amount of USDC transferred from the old pocket to the new pocket.
     */
    event PocketSet(
        address indexed oldPocket,
        address indexed newPocket,
        uint256 amountTransferred
    );

    /**
     *  @dev   Emitted when an asset is swapped in the PSM.
     *  @param assetIn       Address of the asset swapped in.
     *  @param assetOut      Address of the asset swapped out.
     *  @param sender        Address of the sender of the swap.
     *  @param receiver      Address of the receiver of the swap.
     *  @param amountIn      Amount of the asset swapped in.
     *  @param amountOut     Amount of the asset swapped out.
     *  @param referralCode  Referral code for the swap.
     */
    event Swap(
        address indexed assetIn,
        address indexed assetOut,
        address sender,
        address indexed receiver,
        uint256 amountIn,
        uint256 amountOut,
        uint256 referralCode
    );

    /**
     *  @dev   Emitted when an asset is deposited into the PSM.
     *  @param asset           Address of the asset deposited.
     *  @param user            Address of the user that deposited the asset.
     *  @param receiver        Address of the receiver of the resulting shares from the deposit.
     *  @param assetsDeposited Amount of the asset deposited.
     *  @param sharesMinted    Number of shares minted to the user.
     */
    event Deposit(
        address indexed asset,
        address indexed user,
        address indexed receiver,
        uint256 assetsDeposited,
        uint256 sharesMinted
    );

    /**
     *  @dev   Emitted when an asset is withdrawn from the PSM.
     *  @param asset           Address of the asset withdrawn.
     *  @param user            Address of the user that withdrew the asset.
     *  @param receiver        Address of the receiver of the withdrawn assets.
     *  @param assetsWithdrawn Amount of the asset withdrawn.
     *  @param sharesBurned    Number of shares burned from the user.
     */
    event Withdraw(
        address indexed asset,
        address indexed user,
        address indexed receiver,
        uint256 assetsWithdrawn,
        uint256 sharesBurned
    );

    /**********************************************************************************************/
    /*** State variables and immutables                                                         ***/
    /**********************************************************************************************/

    /**
     *  @dev    Returns the IERC20 interface representing USDC.
     *  @return The IERC20 interface of USDC.
     */
    function usdc() external view returns (IERC20);

    /**
     *  @dev    Returns the IERC20 interface representing USDS.
     *  @return The IERC20 interface of USDS.
     */
    function usds() external view returns (IERC20);

    /**
     *  @dev    Returns the IERC20 interface representing sUSDS. This asset is the yield-bearing
     *          asset in the PSM. The value of this asset is queried from the rate provider.
     *  @return The IERC20 interface of sUSDS.
     */
    function susds() external view returns (IERC20);

    /**
     *  @dev    Returns the address of the pocket, an address that holds custody of USDC in the
     *          PSM and can deploy it to yield-bearing strategies. Settable by the owner.
     *  @return The address of the pocket.
     */
    function pocket() external view returns (address);

    /**
     *  @dev    Returns the address of the rate provider, a contract that provides the conversion
     *          rate between sUSDS and the other two assets in the PSM (e.g., sUSDS to USD).
     *  @return The address of the rate provider.
     */
    function rateProvider() external view returns (address);

    /**
     *  @dev    Returns the total number of shares in the PSM. Shares represent ownership of the
     *          assets in the PSM and can be converted to assets at any time.
     *  @return The total number of shares.
     */
    function totalShares() external view returns (uint256);

    /**
     *  @dev    Returns the number of shares held by a specific user.
     *  @param  user The address of the user.
     *  @return The number of shares held by the user.
     */
    function shares(address user) external view returns (uint256);

    /**********************************************************************************************/
    /*** Owner functions                                                                        ***/
    /**********************************************************************************************/

    /**
     *  @dev    Sets the address of the pocket, an address that holds custody of USDC in the PSM
     *          and can deploy it to yield-bearing strategies. This function will transfer the
     *          balance of USDC in the PSM to the new pocket. Callable only by the owner.
     *  @param  newPocket Address of the new pocket.
     */
    function setPocket(address newPocket) external;

    /**********************************************************************************************/
    /*** Swap functions                                                                         ***/
    /**********************************************************************************************/

    /**
     *  @dev    Swaps a specified amount of assetIn for assetOut in the PSM. The amount swapped is
     *          converted based on the current value of the two assets used in the swap. This
     *          function will revert if there is not enough balance in the PSM to facilitate the
     *          swap. Both assets must be supported in the PSM in order to succeed.
     *  @param  assetIn      Address of the ERC-20 asset to swap in.
     *  @param  assetOut     Address of the ERC-20 asset to swap out.
     *  @param  amountIn     Amount of the asset to swap in.
     *  @param  minAmountOut Minimum amount of the asset to receive.
     *  @param  receiver     Address of the receiver of the swapped assets.
     *  @param  referralCode Referral code for the swap.
     *  @return amountOut    Resulting amount of the asset that will be received in the swap.
     */
    function swapExactIn(
        address assetIn,
        address assetOut,
        uint256 amountIn,
        uint256 minAmountOut,
        address receiver,
        uint256 referralCode
    ) external returns (uint256 amountOut);

    /**
     *  @dev    Swaps a derived amount of assetIn for a specific amount of assetOut in the PSM. The
     *          amount swapped is converted based on the current value of the two assets used in
     *          the swap. This function will revert if there is not enough balance in the PSM to
     *          facilitate the swap. Both assets must be supported in the PSM in order to succeed.
     *  @param  assetIn      Address of the ERC-20 asset to swap in.
     *  @param  assetOut     Address of the ERC-20 asset to swap out.
     *  @param  amountOut    Amount of the asset to receive from the swap.
     *  @param  maxAmountIn  Max amount of the asset to use for the swap.
     *  @param  receiver     Address of the receiver of the swapped assets.
     *  @param  referralCode Referral code for the swap.
     *  @return amountIn     Resulting amount of the asset swapped in.
     */
    function swapExactOut(
        address assetIn,
        address assetOut,
        uint256 amountOut,
        uint256 maxAmountIn,
        address receiver,
        uint256 referralCode
    ) external returns (uint256 amountIn);

    /**********************************************************************************************/
    /*** Liquidity provision functions                                                          ***/
    /**********************************************************************************************/

    /**
     *  @dev    Deposits an amount of a given asset into the PSM. Must be one of the supported
     *          assets in order to succeed. The amount deposited is converted to shares based on
     *          the current exchange rate.
     *  @param  asset           Address of the ERC-20 asset to deposit.
     *  @param  receiver        Address of the receiver of the resulting shares from the deposit.
     *  @param  assetsToDeposit Amount of the asset to deposit into the PSM.
     *  @return newShares       Number of shares minted to the user.
     */
    function deposit(address asset, address receiver, uint256 assetsToDeposit)
        external returns (uint256 newShares);

    /**
     *  @dev    Withdraws an amount of a given asset from the PSM up to `maxAssetsToWithdraw`.
     *          Must be one of the supported assets in order to succeed. The amount withdrawn is
     *          the minimum of the balance of the PSM, the max amount, and the max amount of assets
     *          that the user's shares can be converted to.
     *  @param  asset               Address of the ERC-20 asset to withdraw.
     *  @param  receiver            Address of the receiver of the withdrawn assets.
     *  @param  maxAssetsToWithdraw Max amount that the user is willing to withdraw.
     *  @return assetsWithdrawn     Resulting amount of the asset withdrawn from the PSM.
     */
    function withdraw(
        address asset,
        address receiver,
        uint256 maxAssetsToWithdraw
    ) external returns (uint256 assetsWithdrawn);

    /**********************************************************************************************/
    /*** Deposit/withdraw preview functions                                                     ***/
    /**********************************************************************************************/

    /**
     *  @dev    View function that returns the exact number of shares that would be minted for a
     *          given asset and amount to deposit.
     *  @param  asset  Address of the ERC-20 asset to deposit.
     *  @param  assets Amount of the asset to deposit into the PSM.
     *  @return shares Number of shares to be minted to the user.
     */
    function previewDeposit(address asset, uint256 assets) external view returns (uint256 shares);

    /**
     *  @dev    View function that returns the exact number of assets that would be withdrawn and
     *          corresponding shares that would be burned in a withdrawal for a given asset and max
     *          withdraw amount. The amount returned is the minimum of the balance of the PSM,
     *          the max amount, and the max amount of assets that the user's shares
     *          can be converted to.
     *  @param  asset               Address of the ERC-20 asset to withdraw.
     *  @param  maxAssetsToWithdraw Max amount that the user is willing to withdraw.
     *  @return sharesToBurn        Number of shares that would be burned in the withdrawal.
     *  @return assetsWithdrawn     Resulting amount of the asset withdrawn from the PSM.
     */
    function previewWithdraw(address asset, uint256 maxAssetsToWithdraw)
        external view returns (uint256 sharesToBurn, uint256 assetsWithdrawn);

    /**********************************************************************************************/
    /*** Swap preview functions                                                                 ***/
    /**********************************************************************************************/

    /**
     * @dev    View function that returns the exact amount of assetOut that would be received for a
     *         given amount of assetIn in a swap. The amount returned is converted based on the
     *         current value of the two assets used in the swap.
     * @param  assetIn   Address of the ERC-20 asset to swap in.
     * @param  assetOut  Address of the ERC-20 asset to swap out.
     * @param  amountIn  Amount of the asset to swap in.
     * @return amountOut Amount of the asset that will be received in the swap.
     */
    function previewSwapExactIn(address assetIn, address assetOut, uint256 amountIn)
        external view returns (uint256 amountOut);

    /**
     * @dev    View function that returns the exact amount of assetIn that would be required to
     *         receive a given amount of assetOut in a swap. The amount returned is
     *         converted based on the current value of the two assets used in the swap.
     * @param  assetIn   Address of the ERC-20 asset to swap in.
     * @param  assetOut  Address of the ERC-20 asset to swap out.
     * @param  amountOut Amount of the asset to receive from the swap.
     * @return amountIn  Amount of the asset that is required to receive amountOut.
     */
    function previewSwapExactOut(address assetIn, address assetOut, uint256 amountOut)
        external view returns (uint256 amountIn);

    /**********************************************************************************************/
    /*** Conversion functions                                                                   ***/
    /**********************************************************************************************/

    /**
     *  @dev    View function that converts an amount of a given shares to the equivalent amount of
     *          assets for a specified asset.
     *  @param  asset     Address of the asset to use to convert.
     *  @param  numShares Number of shares to convert to assets.
     *  @return assets    Value of assets in asset-native units.
     */
    function convertToAssets(address asset, uint256 numShares) external view returns (uint256);

    /**
     *  @dev    View function that converts an amount of a given shares to the equivalent
     *          amount of assetValue.
     *  @param  numShares  Number of shares to convert to assetValue.
     *  @return assetValue Value of assets in USDC denominated in 18 decimals.
     */
    function convertToAssetValue(uint256 numShares) external view returns (uint256);

    /**
     *  @dev    View function that converts an amount of assetValue (18 decimal value denominated in
     *          USDC and USDS) to shares in the PSM based on the current exchange rate.
     *          Note that this rounds down on calculation so is intended to be used for quoting the
     *          current exchange rate.
     *  @param  assetValue 18 decimal value denominated in USDC (e.g., 1e6 USDC = 1e18)
     *  @return shares     Number of shares that the assetValue is equivalent to.
     */
    function convertToShares(uint256 assetValue) external view returns (uint256);

    /**
     *  @dev    View function that converts an amount of a given asset to shares in the PSM based
     *          on the current exchange rate. Note that this rounds down on calculation so is
     *          intended to be used for quoting the current exchange rate.
     *  @param  asset  Address of the ERC-20 asset to convert to shares.
     *  @param  assets Amount of assets in asset-native units.
     *  @return shares Number of shares that the assetValue is equivalent to.
     */
    function convertToShares(address asset, uint256 assets) external view returns (uint256);

    /**********************************************************************************************/
    /*** Asset value functions                                                                  ***/
    /**********************************************************************************************/

    /**
     *  @dev View function that returns the total value of the balance of all assets in the PSM
     *       converted to USDC/USDS terms denominated in 18 decimal precision.
     */
    function totalAssets() external view returns (uint256);

}

// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.13;

interface IRateProviderLike {
    function getConversionRate() external view returns (uint256);
}

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

Settings
{
  "remappings": [
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "ds-test/=lib/xchain-ssr-oracle/lib/forge-std/lib/ds-test/src/",
    "erc20-helpers/=lib/erc20-helpers/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "spark-address-registry/=lib/spark-address-registry/src/",
    "sparklend-address-registry/=lib/xchain-ssr-oracle/lib/sparklend-address-registry/",
    "xchain-helpers/=lib/xchain-ssr-oracle/lib/xchain-helpers/src/",
    "xchain-ssr-oracle/=lib/xchain-ssr-oracle/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "shanghai",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"usdc_","type":"address"},{"internalType":"address","name":"usds_","type":"address"},{"internalType":"address","name":"susds_","type":"address"},{"internalType":"address","name":"rateProvider_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"assetsDeposited","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sharesMinted","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldPocket","type":"address"},{"indexed":true,"internalType":"address","name":"newPocket","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountTransferred","type":"uint256"}],"name":"PocketSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"assetIn","type":"address"},{"indexed":true,"internalType":"address","name":"assetOut","type":"address"},{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"referralCode","type":"uint256"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"assetsWithdrawn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sharesBurned","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[{"internalType":"uint256","name":"numShares","type":"uint256"}],"name":"convertToAssetValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"numShares","type":"uint256"}],"name":"convertToAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"convertToShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetValue","type":"uint256"}],"name":"convertToShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"assetsToDeposit","type":"uint256"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"newShares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pocket","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"assetsToDeposit","type":"uint256"}],"name":"previewDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"assetIn","type":"address"},{"internalType":"address","name":"assetOut","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"}],"name":"previewSwapExactIn","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"assetIn","type":"address"},{"internalType":"address","name":"assetOut","type":"address"},{"internalType":"uint256","name":"amountOut","type":"uint256"}],"name":"previewSwapExactOut","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"maxAssetsToWithdraw","type":"uint256"}],"name":"previewWithdraw","outputs":[{"internalType":"uint256","name":"sharesToBurn","type":"uint256"},{"internalType":"uint256","name":"assetsWithdrawn","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rateProvider","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newPocket","type":"address"}],"name":"setPocket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"shares","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"susds","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"assetIn","type":"address"},{"internalType":"address","name":"assetOut","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"referralCode","type":"uint256"}],"name":"swapExactIn","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"assetIn","type":"address"},{"internalType":"address","name":"assetOut","type":"address"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"maxAmountIn","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"referralCode","type":"uint256"}],"name":"swapExactOut","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"usdc","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"usds","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"maxAssetsToWithdraw","type":"uint256"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"assetsWithdrawn","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]

61016060405234801562000011575f80fd5b506040516200296e3803806200296e83398101604081905262000034916200064b565b846001600160a01b0381166200006457604051631e4fbdf760e01b81525f60048201526024015b60405180910390fd5b6200006f81620005e0565b506001600160a01b038416620000bc5760405162461bcd60e51b815260206004820152601160248201527050534d332f696e76616c69642d7573646360781b60448201526064016200005b565b6001600160a01b038316620001085760405162461bcd60e51b815260206004820152601160248201527050534d332f696e76616c69642d7573647360781b60448201526064016200005b565b6001600160a01b038216620001555760405162461bcd60e51b815260206004820152601260248201527150534d332f696e76616c69642d737573647360701b60448201526064016200005b565b6001600160a01b038116620001ad5760405162461bcd60e51b815260206004820152601960248201527f50534d332f696e76616c69642d7261746550726f76696465720000000000000060448201526064016200005b565b826001600160a01b0316846001600160a01b031603620002105760405162461bcd60e51b815260206004820152601360248201527f50534d332f757364632d757364732d73616d650000000000000000000000000060448201526064016200005b565b816001600160a01b0316846001600160a01b031603620002735760405162461bcd60e51b815260206004820152601460248201527f50534d332f757364632d73757364732d73616d6500000000000000000000000060448201526064016200005b565b816001600160a01b0316836001600160a01b031603620002d65760405162461bcd60e51b815260206004820152601460248201527f50534d332f757364732d73757364732d73616d6500000000000000000000000060448201526064016200005b565b6001600160a01b0380851660e05283811661010052828116610120528116610140819052600180546001600160a01b0319163017905560408051633cd8227b60e21b8152905163f36089ec916004808201926020929091908290030181865afa15801562000346573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906200036c9190620006b7565b5f03620003bc5760405162461bcd60e51b815260206004820152601f60248201527f50534d332f726174652d70726f76696465722d72657475726e732d7a65726f0060448201526064016200005b565b836001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015620003f9573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906200041f9190620006cf565b6200042c90600a62000807565b60808181525050826001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000470573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620004969190620006cf565b620004a390600a62000807565b60a08181525050816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015620004e7573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906200050d9190620006cf565b6200051a90600a62000807565b60c052608051670de0b6b3a76400001015620005795760405162461bcd60e51b815260206004820152601c60248201527f50534d332f757364632d707265636973696f6e2d746f6f2d686967680000000060448201526064016200005b565b670de0b6b3a764000060a0511115620005d55760405162461bcd60e51b815260206004820152601c60248201527f50534d332f757364732d707265636973696f6e2d746f6f2d686967680000000060448201526064016200005b565b505050505062000817565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80516001600160a01b038116811462000646575f80fd5b919050565b5f805f805f60a0868803121562000660575f80fd5b6200066b866200062f565b94506200067b602087016200062f565b93506200068b604087016200062f565b92506200069b606087016200062f565b9150620006ab608087016200062f565b90509295509295909350565b5f60208284031215620006c8575f80fd5b5051919050565b5f60208284031215620006e0575f80fd5b815160ff81168114620006f1575f80fd5b9392505050565b634e487b7160e01b5f52601160045260245ffd5b600181815b808511156200074c57815f1904821115620007305762000730620006f8565b808516156200073e57918102915b93841c939080029062000711565b509250929050565b5f82620007645750600162000801565b816200077257505f62000801565b81600181146200078b57600281146200079657620007b6565b600191505062000801565b60ff841115620007aa57620007aa620006f8565b50506001821b62000801565b5060208310610133831016604e8410600b8410161715620007db575081810a62000801565b620007e783836200070c565b805f1904821115620007fd57620007fd620006f8565b0290505b92915050565b5f620006f160ff84168362000754565b60805160a05160c05160e051610100516101205161014051611fc3620009ab5f395f81816102be01528181610be2015281816113900152818161143b015281816119440152611a7901525f818161026c015281816103d1015281816110c0015281816111e70152818161124b015281816116f001526117b701525f81816102320152818161045901528181610b760152818161103401528181611129015281816112e8015281816116b4015261177501525f81816101cd015281816104f501528181610649015281816106d30152818161070c01528181610af801528181610ffb0152818161116201528181611284015281816115d801528181611638015281816116790152818161172c015261189201525f8181610c620152818161136a015281816114cf015281816119cc01528181611a2d0152611b0001525f8181610bbb015281816110920152818161119f015281816112240152818161132501526114f601525f8181610b3d01528181611071015281816110fd015281816111c0015281816112c101526115340152611fc35ff3fe608060405234801561000f575f80fd5b5060043610610152575f3560e01c8063715018a6116100bf578063c6e6f59211610079578063c6e6f5921461031b578063cccef9e21461032e578063ce7c2ac214610341578063d9caed1214610360578063e243741114610373578063f2fde38b14610386575f80fd5b8063715018a61461028e5780638340f549146102965780638da5cb5b146102a9578063949db658146102b9578063b8f82b26146102e0578063bbc6f1dc146102f3575f80fd5b80633e413bee116101105780633e413bee146101c85780633e5541f11461020757806341c094e01461021a5780634cf282fb1461022d57806350603df31461025457806358b8f19c14610267575f80fd5b8062d8088a1461015657806301e1d1141461017c57806303fa0d0b14610184578063051f86b5146101995780631a019e37146101ac5780633a98ef39146101bf575b5f80fd5b610169610164366004611da3565b610399565b6040519081526020015b60405180910390f35b6101696103ae565b610197610192366004611ddc565b61057c565b005b6101696101a7366004611df5565b610790565b6101696101ba366004611df5565b6108f8565b61016960025481565b6101ef7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610173565b610169610215366004611e50565b610a51565b610169610228366004611e78565b610a8e565b6101ef7f000000000000000000000000000000000000000000000000000000000000000081565b610169610262366004611e50565b610ac4565b6101ef7f000000000000000000000000000000000000000000000000000000000000000081565b610197610ca3565b6101696102a4366004611da3565b610cb6565b5f546001600160a01b03166101ef565b6101ef7f000000000000000000000000000000000000000000000000000000000000000081565b6101696102ee366004611e50565b610dac565b610306610301366004611e50565b610dbb565b60408051928352602083019190915201610173565b610169610329366004611e78565b610ebb565b6001546101ef906001600160a01b031681565b61016961034f366004611ddc565b60036020525f908152604090205481565b61016961036e366004611da3565b610edc565b610169610381366004611da3565b610fad565b610197610394366004611ddc565b610fbb565b5f6103a68484845f610ff8565b949350505050565b6040516370a0823160e01b81523060048201525f90610440906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015610416573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061043a9190611e8f565b5f611362565b6040516370a0823160e01b81523060048201526104cf907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156104a6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104ca9190611e8f565b6114f3565b6001546040516370a0823160e01b81526001600160a01b039182166004820152610563917f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa15801561053a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061055e9190611e8f565b611531565b61056d9190611eba565b6105779190611eba565b905090565b610584611565565b6001600160a01b0381166105d55760405162461bcd60e51b81526020600482015260136024820152721414d34ccbda5b9d985b1a590b5c1bd8dad95d606a1b60448201526064015b60405180910390fd5b6001546001600160a01b039081169082168190036106285760405162461bcd60e51b815260206004820152601060248201526f1414d34ccbdcd85b594b5c1bd8dad95d60821b60448201526064016105cc565b6040516370a0823160e01b81526001600160a01b0382811660048301525f917f0000000000000000000000000000000000000000000000000000000000000000909116906370a0823190602401602060405180830381865afa158015610690573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106b49190611e8f565b9050306001600160a01b038316036106ff576106fa6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168483611591565b610734565b6107346001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168385846115a1565b600180546001600160a01b0319166001600160a01b0385811691821790925560405183815290918416907f3fc246ba2ebf4dedf8b9de3cc4b46795e44b56db70960136b44fccbe99daa4789060200160405180910390a3505050565b5f845f036107d95760405162461bcd60e51b81526020600482015260166024820152751414d34ccbda5b9d985b1a590b585b5bdd5b9d13dd5d60521b60448201526064016105cc565b6001600160a01b0383166108275760405162461bcd60e51b81526020600482015260156024820152742829a69997b4b73b30b634b216b932b1b2b4bb32b960591b60448201526064016105cc565b610832878787610fad565b90508381111561087d5760405162461bcd60e51b81526020600482015260166024820152750a0a69a665ec2dadeeadce892dc5ae8dede5ad0d2ced60531b60448201526064016105cc565b61088787826115b3565b6108928684876115d6565b6040805133815260208101839052908101869052606081018390526001600160a01b0380851691888216918a16907fdba43ee9916cb156cc32a5d3406e87341e568126a46815294073ba25c9400246906080015b60405180910390a49695505050505050565b5f845f036109405760405162461bcd60e51b81526020600482015260156024820152742829a69997b4b73b30b634b216b0b6b7bab73a24b760591b60448201526064016105cc565b6001600160a01b03831661098e5760405162461bcd60e51b81526020600482015260156024820152742829a69997b4b73b30b634b216b932b1b2b4bb32b960591b60448201526064016105cc565b610999878787610399565b9050838110156109e45760405162461bcd60e51b815260206004820152601660248201527550534d332f616d6f756e744f75742d746f6f2d6c6f7760501b60448201526064016105cc565b6109ee87866115b3565b6109f98684836115d6565b6040805133815260208101879052908101829052606081018390526001600160a01b0380851691888216918a16907fdba43ee9916cb156cc32a5d3406e87341e568126a46815294073ba25c9400246906080016108e6565b5f610a5b83611676565b610a775760405162461bcd60e51b81526004016105cc90611ecd565b610a8561032984845f611729565b90505b92915050565b6002545f908015610abd5780610aa26103ae565b610aac9085611ef9565b610ab69190611f10565b9392505050565b5090919050565b5f610ace83611676565b610aea5760405162461bcd60e51b81526004016105cc90611ecd565b5f610af483610a8e565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b031603610b7457670de0b6b3a7640000610b627f000000000000000000000000000000000000000000000000000000000000000083611ef9565b610b6c9190611f10565b915050610a88565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b031603610be057670de0b6b3a7640000610b627f000000000000000000000000000000000000000000000000000000000000000083611ef9565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f36089ec6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c3c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c609190611e8f565b7f0000000000000000000000000000000000000000000000000000000000000000610c8f83633b9aca00611ef9565b610c999190611ef9565b6103a69190611f10565b610cab611565565b610cb45f611840565b565b5f815f03610cfc5760405162461bcd60e51b81526020600482015260136024820152721414d34ccbda5b9d985b1a590b585b5bdd5b9d606a1b60448201526064016105cc565b610d068483610dac565b6001600160a01b0384165f90815260036020526040812080549293508392909190610d32908490611eba565b925050819055508060025f828254610d4a9190611eba565b90915550610d5a905084836115b3565b60408051838152602081018390526001600160a01b03808616923392918816917f5fe47ed6d4225326d3303476197d782ded5a4e9c14f479dc9ec4992af4e85d59910160405180910390a49392505050565b5f610a8561032984845f611729565b5f80610dc684611676565b610de25760405162461bcd60e51b81526004016105cc90611ecd565b5f846001600160a01b03166370a08231610dfb8761188f565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015610e3d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e619190611e8f565b9050838110610e705783610e72565b805b9150610e88610e8386846001611729565b6118e0565b335f9081526003602052604090205490935080841115610eb257610eac8682610ac4565b92508093505b50509250929050565b5f80610ec56103ae565b90508015610abd578060025484610aac9190611ef9565b5f815f03610f225760405162461bcd60e51b81526020600482015260136024820152721414d34ccbda5b9d985b1a590b585b5bdd5b9d606a1b60448201526064016105cc565b5f610f2d8584610dbb565b335f9081526003602052604090208054839003905560028054839003905592509050610f5a8585846115d6565b60408051838152602081018390526001600160a01b03808716923392918916917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a4509392505050565b5f6103a68385846001610ff8565b610fc3611565565b6001600160a01b038116610fec57604051631e4fbdf760e01b81525f60048201526024016105cc565b610ff581611840565b50565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316856001600160a01b031603611127577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b0316036110be576110b7837f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000085611909565b90506103a6565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b031603611122576110b7837f000000000000000000000000000000000000000000000000000000000000000084611940565b61134a565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316856001600160a01b031603611249577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b0316036111e5576110b7837f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000085611909565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b031603611122576110b7837f000000000000000000000000000000000000000000000000000000000000000084611940565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316856001600160a01b03160361134a577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b0316036112e6576110b7837f000000000000000000000000000000000000000000000000000000000000000084611a75565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b03160361134a576110b7837f000000000000000000000000000000000000000000000000000000000000000084611a75565b60405162461bcd60e51b81526004016105cc90611ecd565b5f81611433577f0000000000000000000000000000000000000000000000000000000000000000633b9aca007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f36089ec6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113ea573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061140e9190611e8f565b6114189086611ef9565b6114229190611f10565b61142c9190611f10565b9050610a88565b610a856114cd7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f36089ec6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611495573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114b99190611e8f565b6114c39086611ef9565b633b9aca00611b64565b7f0000000000000000000000000000000000000000000000000000000000000000611b64565b5f7f000000000000000000000000000000000000000000000000000000000000000061152783670de0b6b3a7640000611ef9565b610a889190611f10565b5f7f000000000000000000000000000000000000000000000000000000000000000061152783670de0b6b3a7640000611ef9565b5f546001600160a01b03163314610cb45760405163118cdaa760e01b81523360048201526024016105cc565b61159c838383611baa565b505050565b6115ad84848484611c47565b50505050565b6115d2336115c08461188f565b6001600160a01b0385169190846115a1565b5050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b031614801561162257506001546001600160a01b03163014155b156116625760015461159c906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116911684846115a1565b61159c6001600160a01b0384168383611591565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b031614806116e857507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b0316145b80610a8857507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b03161492915050565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b0316036117735761176c83611531565b9050610ab6565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b0316036117b55761176c836114f3565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b0316036117f85761176c8383611362565b60405162461bcd60e51b815260206004820152601c60248201527f50534d332f696e76616c69642d61737365742d666f722d76616c75650000000060448201526064016105cc565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b0316146118cf5730610a88565b50506001546001600160a01b031690565b5f806118ea6103ae565b90508015610abd57610ab6600254846119039190611ef9565b82611b64565b5f81611924578361191a8487611ef9565b6110b79190611f10565b6119376119318487611ef9565b85611b64565b95945050505050565b5f807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f36089ec6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561199e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119c29190611e8f565b905082611a2857837f000000000000000000000000000000000000000000000000000000000000000082611a02886b033b2e3c9fd0803ce8000000611ef9565b611a0c9190611f10565b611a169190611ef9565b611a209190611f10565b915050610ab6565b6119377f0000000000000000000000000000000000000000000000000000000000000000611a6b611a65886b033b2e3c9fd0803ce8000000611ef9565b84611b64565b6119319190611ef9565b5f807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f36089ec6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ad3573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611af79190611e8f565b905082611b37577f0000000000000000000000000000000000000000000000000000000000000000846b033b2e3c9fd0803ce8000000611a028489611ef9565b61193784611b5a611b488489611ef9565b6b033b2e3c9fd0803ce8000000611b64565b6114cd9190611ef9565b5f815f03611b765761142c8284611f10565b8215611ba25781611b88600185611f2f565b611b929190611f10565b611b9d906001611eba565b610a85565b505f92915050565b6040516001600160a01b038316602482015260448101829052611bfb90849060640160408051601f198184030181529190526020810180516001600160e01b031663a9059cbb60e01b179052611cec565b61159c5760405162461bcd60e51b815260206004820152601960248201527f5361666545524332302f7472616e736665722d6661696c65640000000000000060448201526064016105cc565b6040516001600160a01b0380851660248301528316604482015260648101829052611ca090859060840160408051601f198184030181529190526020810180516001600160e01b03166323b872dd60e01b179052611cec565b6115ad5760405162461bcd60e51b815260206004820152601e60248201527f5361666545524332302f7472616e736665722d66726f6d2d6661696c6564000060448201526064016105cc565b5f826001600160a01b03163b5f03611d0557505f610a88565b6060836001600160a01b031683604051611d1f9190611f42565b5f604051808303815f865af19150503d805f8114611d58576040519150601f19603f3d011682016040523d82523d5f602084013e611d5d565b606091505b5090925090508180156103a65750805115806103a65750808060200190518101906103a69190611f6e565b80356001600160a01b0381168114611d9e575f80fd5b919050565b5f805f60608486031215611db5575f80fd5b611dbe84611d88565b9250611dcc60208501611d88565b9150604084013590509250925092565b5f60208284031215611dec575f80fd5b610a8582611d88565b5f805f805f8060c08789031215611e0a575f80fd5b611e1387611d88565b9550611e2160208801611d88565b94506040870135935060608701359250611e3d60808801611d88565b915060a087013590509295509295509295565b5f8060408385031215611e61575f80fd5b611e6a83611d88565b946020939093013593505050565b5f60208284031215611e88575f80fd5b5035919050565b5f60208284031215611e9f575f80fd5b5051919050565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610a8857610a88611ea6565b6020808252601290820152711414d34ccbda5b9d985b1a590b585cdcd95d60721b604082015260600190565b8082028115828204841417610a8857610a88611ea6565b5f82611f2a57634e487b7160e01b5f52601260045260245ffd5b500490565b81810381811115610a8857610a88611ea6565b5f82515f5b81811015611f615760208186018101518583015201611f47565b505f920191825250919050565b5f60208284031215611f7e575f80fd5b81518015158114610ab6575f80fdfea2646970667358221220b3c0eb2f3f00945e5bfb3d74bf9e9febbd05516f28eb4d933cd31fb4a98c006c64736f6c63430008140033000000000000000000000000b037c43b433964a2017cd689f535beb6b0531473000000000000000000000000078d782b760474a361dda0af3839290b0ef57ad60000000000000000000000007e10036acc4b56d4dfca3b77810356ce52313f9c000000000000000000000000a06b10db9f390990364a3984c04fadf1c13691b50000000000000000000000001566bfa55d95686a823751298533d42651183988

Deployed Bytecode

0x608060405234801561000f575f80fd5b5060043610610152575f3560e01c8063715018a6116100bf578063c6e6f59211610079578063c6e6f5921461031b578063cccef9e21461032e578063ce7c2ac214610341578063d9caed1214610360578063e243741114610373578063f2fde38b14610386575f80fd5b8063715018a61461028e5780638340f549146102965780638da5cb5b146102a9578063949db658146102b9578063b8f82b26146102e0578063bbc6f1dc146102f3575f80fd5b80633e413bee116101105780633e413bee146101c85780633e5541f11461020757806341c094e01461021a5780634cf282fb1461022d57806350603df31461025457806358b8f19c14610267575f80fd5b8062d8088a1461015657806301e1d1141461017c57806303fa0d0b14610184578063051f86b5146101995780631a019e37146101ac5780633a98ef39146101bf575b5f80fd5b610169610164366004611da3565b610399565b6040519081526020015b60405180910390f35b6101696103ae565b610197610192366004611ddc565b61057c565b005b6101696101a7366004611df5565b610790565b6101696101ba366004611df5565b6108f8565b61016960025481565b6101ef7f000000000000000000000000078d782b760474a361dda0af3839290b0ef57ad681565b6040516001600160a01b039091168152602001610173565b610169610215366004611e50565b610a51565b610169610228366004611e78565b610a8e565b6101ef7f0000000000000000000000007e10036acc4b56d4dfca3b77810356ce52313f9c81565b610169610262366004611e50565b610ac4565b6101ef7f000000000000000000000000a06b10db9f390990364a3984c04fadf1c13691b581565b610197610ca3565b6101696102a4366004611da3565b610cb6565b5f546001600160a01b03166101ef565b6101ef7f0000000000000000000000001566bfa55d95686a823751298533d4265118398881565b6101696102ee366004611e50565b610dac565b610306610301366004611e50565b610dbb565b60408051928352602083019190915201610173565b610169610329366004611e78565b610ebb565b6001546101ef906001600160a01b031681565b61016961034f366004611ddc565b60036020525f908152604090205481565b61016961036e366004611da3565b610edc565b610169610381366004611da3565b610fad565b610197610394366004611ddc565b610fbb565b5f6103a68484845f610ff8565b949350505050565b6040516370a0823160e01b81523060048201525f90610440906001600160a01b037f000000000000000000000000a06b10db9f390990364a3984c04fadf1c13691b516906370a0823190602401602060405180830381865afa158015610416573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061043a9190611e8f565b5f611362565b6040516370a0823160e01b81523060048201526104cf907f0000000000000000000000007e10036acc4b56d4dfca3b77810356ce52313f9c6001600160a01b0316906370a0823190602401602060405180830381865afa1580156104a6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104ca9190611e8f565b6114f3565b6001546040516370a0823160e01b81526001600160a01b039182166004820152610563917f000000000000000000000000078d782b760474a361dda0af3839290b0ef57ad616906370a0823190602401602060405180830381865afa15801561053a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061055e9190611e8f565b611531565b61056d9190611eba565b6105779190611eba565b905090565b610584611565565b6001600160a01b0381166105d55760405162461bcd60e51b81526020600482015260136024820152721414d34ccbda5b9d985b1a590b5c1bd8dad95d606a1b60448201526064015b60405180910390fd5b6001546001600160a01b039081169082168190036106285760405162461bcd60e51b815260206004820152601060248201526f1414d34ccbdcd85b594b5c1bd8dad95d60821b60448201526064016105cc565b6040516370a0823160e01b81526001600160a01b0382811660048301525f917f000000000000000000000000078d782b760474a361dda0af3839290b0ef57ad6909116906370a0823190602401602060405180830381865afa158015610690573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106b49190611e8f565b9050306001600160a01b038316036106ff576106fa6001600160a01b037f000000000000000000000000078d782b760474a361dda0af3839290b0ef57ad6168483611591565b610734565b6107346001600160a01b037f000000000000000000000000078d782b760474a361dda0af3839290b0ef57ad6168385846115a1565b600180546001600160a01b0319166001600160a01b0385811691821790925560405183815290918416907f3fc246ba2ebf4dedf8b9de3cc4b46795e44b56db70960136b44fccbe99daa4789060200160405180910390a3505050565b5f845f036107d95760405162461bcd60e51b81526020600482015260166024820152751414d34ccbda5b9d985b1a590b585b5bdd5b9d13dd5d60521b60448201526064016105cc565b6001600160a01b0383166108275760405162461bcd60e51b81526020600482015260156024820152742829a69997b4b73b30b634b216b932b1b2b4bb32b960591b60448201526064016105cc565b610832878787610fad565b90508381111561087d5760405162461bcd60e51b81526020600482015260166024820152750a0a69a665ec2dadeeadce892dc5ae8dede5ad0d2ced60531b60448201526064016105cc565b61088787826115b3565b6108928684876115d6565b6040805133815260208101839052908101869052606081018390526001600160a01b0380851691888216918a16907fdba43ee9916cb156cc32a5d3406e87341e568126a46815294073ba25c9400246906080015b60405180910390a49695505050505050565b5f845f036109405760405162461bcd60e51b81526020600482015260156024820152742829a69997b4b73b30b634b216b0b6b7bab73a24b760591b60448201526064016105cc565b6001600160a01b03831661098e5760405162461bcd60e51b81526020600482015260156024820152742829a69997b4b73b30b634b216b932b1b2b4bb32b960591b60448201526064016105cc565b610999878787610399565b9050838110156109e45760405162461bcd60e51b815260206004820152601660248201527550534d332f616d6f756e744f75742d746f6f2d6c6f7760501b60448201526064016105cc565b6109ee87866115b3565b6109f98684836115d6565b6040805133815260208101879052908101829052606081018390526001600160a01b0380851691888216918a16907fdba43ee9916cb156cc32a5d3406e87341e568126a46815294073ba25c9400246906080016108e6565b5f610a5b83611676565b610a775760405162461bcd60e51b81526004016105cc90611ecd565b610a8561032984845f611729565b90505b92915050565b6002545f908015610abd5780610aa26103ae565b610aac9085611ef9565b610ab69190611f10565b9392505050565b5090919050565b5f610ace83611676565b610aea5760405162461bcd60e51b81526004016105cc90611ecd565b5f610af483610a8e565b90507f000000000000000000000000078d782b760474a361dda0af3839290b0ef57ad66001600160a01b0316846001600160a01b031603610b7457670de0b6b3a7640000610b627f00000000000000000000000000000000000000000000000000000000000f424083611ef9565b610b6c9190611f10565b915050610a88565b7f0000000000000000000000007e10036acc4b56d4dfca3b77810356ce52313f9c6001600160a01b0316846001600160a01b031603610be057670de0b6b3a7640000610b627f0000000000000000000000000000000000000000000000000de0b6b3a764000083611ef9565b7f0000000000000000000000001566bfa55d95686a823751298533d426511839886001600160a01b031663f36089ec6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c3c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c609190611e8f565b7f0000000000000000000000000000000000000000000000000de0b6b3a7640000610c8f83633b9aca00611ef9565b610c999190611ef9565b6103a69190611f10565b610cab611565565b610cb45f611840565b565b5f815f03610cfc5760405162461bcd60e51b81526020600482015260136024820152721414d34ccbda5b9d985b1a590b585b5bdd5b9d606a1b60448201526064016105cc565b610d068483610dac565b6001600160a01b0384165f90815260036020526040812080549293508392909190610d32908490611eba565b925050819055508060025f828254610d4a9190611eba565b90915550610d5a905084836115b3565b60408051838152602081018390526001600160a01b03808616923392918816917f5fe47ed6d4225326d3303476197d782ded5a4e9c14f479dc9ec4992af4e85d59910160405180910390a49392505050565b5f610a8561032984845f611729565b5f80610dc684611676565b610de25760405162461bcd60e51b81526004016105cc90611ecd565b5f846001600160a01b03166370a08231610dfb8761188f565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015610e3d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e619190611e8f565b9050838110610e705783610e72565b805b9150610e88610e8386846001611729565b6118e0565b335f9081526003602052604090205490935080841115610eb257610eac8682610ac4565b92508093505b50509250929050565b5f80610ec56103ae565b90508015610abd578060025484610aac9190611ef9565b5f815f03610f225760405162461bcd60e51b81526020600482015260136024820152721414d34ccbda5b9d985b1a590b585b5bdd5b9d606a1b60448201526064016105cc565b5f610f2d8584610dbb565b335f9081526003602052604090208054839003905560028054839003905592509050610f5a8585846115d6565b60408051838152602081018390526001600160a01b03808716923392918916917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a4509392505050565b5f6103a68385846001610ff8565b610fc3611565565b6001600160a01b038116610fec57604051631e4fbdf760e01b81525f60048201526024016105cc565b610ff581611840565b50565b5f7f000000000000000000000000078d782b760474a361dda0af3839290b0ef57ad66001600160a01b0316856001600160a01b031603611127577f0000000000000000000000007e10036acc4b56d4dfca3b77810356ce52313f9c6001600160a01b0316846001600160a01b0316036110be576110b7837f00000000000000000000000000000000000000000000000000000000000f42407f0000000000000000000000000000000000000000000000000de0b6b3a764000085611909565b90506103a6565b7f000000000000000000000000a06b10db9f390990364a3984c04fadf1c13691b56001600160a01b0316846001600160a01b031603611122576110b7837f00000000000000000000000000000000000000000000000000000000000f424084611940565b61134a565b7f0000000000000000000000007e10036acc4b56d4dfca3b77810356ce52313f9c6001600160a01b0316856001600160a01b031603611249577f000000000000000000000000078d782b760474a361dda0af3839290b0ef57ad66001600160a01b0316846001600160a01b0316036111e5576110b7837f0000000000000000000000000000000000000000000000000de0b6b3a76400007f00000000000000000000000000000000000000000000000000000000000f424085611909565b7f000000000000000000000000a06b10db9f390990364a3984c04fadf1c13691b56001600160a01b0316846001600160a01b031603611122576110b7837f0000000000000000000000000000000000000000000000000de0b6b3a764000084611940565b7f000000000000000000000000a06b10db9f390990364a3984c04fadf1c13691b56001600160a01b0316856001600160a01b03160361134a577f000000000000000000000000078d782b760474a361dda0af3839290b0ef57ad66001600160a01b0316846001600160a01b0316036112e6576110b7837f00000000000000000000000000000000000000000000000000000000000f424084611a75565b7f0000000000000000000000007e10036acc4b56d4dfca3b77810356ce52313f9c6001600160a01b0316846001600160a01b03160361134a576110b7837f0000000000000000000000000000000000000000000000000de0b6b3a764000084611a75565b60405162461bcd60e51b81526004016105cc90611ecd565b5f81611433577f0000000000000000000000000000000000000000000000000de0b6b3a7640000633b9aca007f0000000000000000000000001566bfa55d95686a823751298533d426511839886001600160a01b031663f36089ec6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113ea573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061140e9190611e8f565b6114189086611ef9565b6114229190611f10565b61142c9190611f10565b9050610a88565b610a856114cd7f0000000000000000000000001566bfa55d95686a823751298533d426511839886001600160a01b031663f36089ec6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611495573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114b99190611e8f565b6114c39086611ef9565b633b9aca00611b64565b7f0000000000000000000000000000000000000000000000000de0b6b3a7640000611b64565b5f7f0000000000000000000000000000000000000000000000000de0b6b3a764000061152783670de0b6b3a7640000611ef9565b610a889190611f10565b5f7f00000000000000000000000000000000000000000000000000000000000f424061152783670de0b6b3a7640000611ef9565b5f546001600160a01b03163314610cb45760405163118cdaa760e01b81523360048201526024016105cc565b61159c838383611baa565b505050565b6115ad84848484611c47565b50505050565b6115d2336115c08461188f565b6001600160a01b0385169190846115a1565b5050565b7f000000000000000000000000078d782b760474a361dda0af3839290b0ef57ad66001600160a01b0316836001600160a01b031614801561162257506001546001600160a01b03163014155b156116625760015461159c906001600160a01b037f000000000000000000000000078d782b760474a361dda0af3839290b0ef57ad68116911684846115a1565b61159c6001600160a01b0384168383611591565b5f7f000000000000000000000000078d782b760474a361dda0af3839290b0ef57ad66001600160a01b0316826001600160a01b031614806116e857507f0000000000000000000000007e10036acc4b56d4dfca3b77810356ce52313f9c6001600160a01b0316826001600160a01b0316145b80610a8857507f000000000000000000000000a06b10db9f390990364a3984c04fadf1c13691b56001600160a01b0316826001600160a01b03161492915050565b5f7f000000000000000000000000078d782b760474a361dda0af3839290b0ef57ad66001600160a01b0316846001600160a01b0316036117735761176c83611531565b9050610ab6565b7f0000000000000000000000007e10036acc4b56d4dfca3b77810356ce52313f9c6001600160a01b0316846001600160a01b0316036117b55761176c836114f3565b7f000000000000000000000000a06b10db9f390990364a3984c04fadf1c13691b56001600160a01b0316846001600160a01b0316036117f85761176c8383611362565b60405162461bcd60e51b815260206004820152601c60248201527f50534d332f696e76616c69642d61737365742d666f722d76616c75650000000060448201526064016105cc565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b5f7f000000000000000000000000078d782b760474a361dda0af3839290b0ef57ad66001600160a01b0316826001600160a01b0316146118cf5730610a88565b50506001546001600160a01b031690565b5f806118ea6103ae565b90508015610abd57610ab6600254846119039190611ef9565b82611b64565b5f81611924578361191a8487611ef9565b6110b79190611f10565b6119376119318487611ef9565b85611b64565b95945050505050565b5f807f0000000000000000000000001566bfa55d95686a823751298533d426511839886001600160a01b031663f36089ec6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561199e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119c29190611e8f565b905082611a2857837f0000000000000000000000000000000000000000000000000de0b6b3a764000082611a02886b033b2e3c9fd0803ce8000000611ef9565b611a0c9190611f10565b611a169190611ef9565b611a209190611f10565b915050610ab6565b6119377f0000000000000000000000000000000000000000000000000de0b6b3a7640000611a6b611a65886b033b2e3c9fd0803ce8000000611ef9565b84611b64565b6119319190611ef9565b5f807f0000000000000000000000001566bfa55d95686a823751298533d426511839886001600160a01b031663f36089ec6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ad3573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611af79190611e8f565b905082611b37577f0000000000000000000000000000000000000000000000000de0b6b3a7640000846b033b2e3c9fd0803ce8000000611a028489611ef9565b61193784611b5a611b488489611ef9565b6b033b2e3c9fd0803ce8000000611b64565b6114cd9190611ef9565b5f815f03611b765761142c8284611f10565b8215611ba25781611b88600185611f2f565b611b929190611f10565b611b9d906001611eba565b610a85565b505f92915050565b6040516001600160a01b038316602482015260448101829052611bfb90849060640160408051601f198184030181529190526020810180516001600160e01b031663a9059cbb60e01b179052611cec565b61159c5760405162461bcd60e51b815260206004820152601960248201527f5361666545524332302f7472616e736665722d6661696c65640000000000000060448201526064016105cc565b6040516001600160a01b0380851660248301528316604482015260648101829052611ca090859060840160408051601f198184030181529190526020810180516001600160e01b03166323b872dd60e01b179052611cec565b6115ad5760405162461bcd60e51b815260206004820152601e60248201527f5361666545524332302f7472616e736665722d66726f6d2d6661696c6564000060448201526064016105cc565b5f826001600160a01b03163b5f03611d0557505f610a88565b6060836001600160a01b031683604051611d1f9190611f42565b5f604051808303815f865af19150503d805f8114611d58576040519150601f19603f3d011682016040523d82523d5f602084013e611d5d565b606091505b5090925090508180156103a65750805115806103a65750808060200190518101906103a69190611f6e565b80356001600160a01b0381168114611d9e575f80fd5b919050565b5f805f60608486031215611db5575f80fd5b611dbe84611d88565b9250611dcc60208501611d88565b9150604084013590509250925092565b5f60208284031215611dec575f80fd5b610a8582611d88565b5f805f805f8060c08789031215611e0a575f80fd5b611e1387611d88565b9550611e2160208801611d88565b94506040870135935060608701359250611e3d60808801611d88565b915060a087013590509295509295509295565b5f8060408385031215611e61575f80fd5b611e6a83611d88565b946020939093013593505050565b5f60208284031215611e88575f80fd5b5035919050565b5f60208284031215611e9f575f80fd5b5051919050565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610a8857610a88611ea6565b6020808252601290820152711414d34ccbda5b9d985b1a590b585cdcd95d60721b604082015260600190565b8082028115828204841417610a8857610a88611ea6565b5f82611f2a57634e487b7160e01b5f52601260045260245ffd5b500490565b81810381811115610a8857610a88611ea6565b5f82515f5b81811015611f615760208186018101518583015201611f47565b505f920191825250919050565b5f60208284031215611f7e575f80fd5b81518015158114610ab6575f80fdfea2646970667358221220b3c0eb2f3f00945e5bfb3d74bf9e9febbd05516f28eb4d933cd31fb4a98c006c64736f6c63430008140033

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

000000000000000000000000b037c43b433964a2017cd689f535beb6b0531473000000000000000000000000078d782b760474a361dda0af3839290b0ef57ad60000000000000000000000007e10036acc4b56d4dfca3b77810356ce52313f9c000000000000000000000000a06b10db9f390990364a3984c04fadf1c13691b50000000000000000000000001566bfa55d95686a823751298533d42651183988

-----Decoded View---------------
Arg [0] : owner_ (address): 0xb037C43b433964A2017cd689f535BEb6B0531473
Arg [1] : usdc_ (address): 0x078D782b760474a361dDA0AF3839290b0EF57AD6
Arg [2] : usds_ (address): 0x7E10036Acc4B56d4dFCa3b77810356CE52313F9C
Arg [3] : susds_ (address): 0xA06b10Db9F390990364A3984C04FaDf1c13691b5
Arg [4] : rateProvider_ (address): 0x1566BFA55D95686a823751298533D42651183988

-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 000000000000000000000000b037c43b433964a2017cd689f535beb6b0531473
Arg [1] : 000000000000000000000000078d782b760474a361dda0af3839290b0ef57ad6
Arg [2] : 0000000000000000000000007e10036acc4b56d4dfca3b77810356ce52313f9c
Arg [3] : 000000000000000000000000a06b10db9f390990364a3984c04fadf1c13691b5
Arg [4] : 0000000000000000000000001566bfa55d95686a823751298533d42651183988


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

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.