// 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;
}
}
// 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-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.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: 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: 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;
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)));
}
}
{
"compilationTarget": {
"src/PSM3.sol": "PSM3"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"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/"
]
}
[{"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"}]