Source Code
Latest 25 from a total of 760 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Swap Exact In | 37905258 | 8 days ago | IN | 0 ETH | 0 | ||||
| Swap Exact In | 37877897 | 9 days ago | IN | 0 ETH | 0 | ||||
| Swap Exact In | 37877855 | 9 days ago | IN | 0 ETH | 0 | ||||
| Swap Exact In | 37790602 | 10 days ago | IN | 0 ETH | 0 | ||||
| Swap Exact In | 37790562 | 10 days ago | IN | 0 ETH | 0 | ||||
| Swap Exact In | 37776723 | 10 days ago | IN | 0 ETH | 0 | ||||
| Swap Exact In | 37776677 | 10 days ago | IN | 0 ETH | 0 | ||||
| Swap Exact In | 37775924 | 10 days ago | IN | 0 ETH | 0 | ||||
| Swap Exact In | 37767258 | 10 days ago | IN | 0 ETH | 0 | ||||
| Swap Exact In | 37738598 | 10 days ago | IN | 0 ETH | 0.00000001 | ||||
| Swap Exact In | 37738444 | 10 days ago | IN | 0 ETH | 0.00000001 | ||||
| Swap Exact In | 37519896 | 13 days ago | IN | 0 ETH | 0 | ||||
| Swap Exact In | 37519772 | 13 days ago | IN | 0 ETH | 0 | ||||
| Swap Exact In | 37459054 | 13 days ago | IN | 0 ETH | 0.00000001 | ||||
| Swap Exact In | 37458877 | 13 days ago | IN | 0 ETH | 0 | ||||
| Swap Exact In | 37446450 | 14 days ago | IN | 0 ETH | 0.00000011 | ||||
| Swap Exact In | 37446427 | 14 days ago | IN | 0 ETH | 0.00000011 | ||||
| Swap Exact In | 37445018 | 14 days ago | IN | 0 ETH | 0 | ||||
| Swap Exact In | 37444994 | 14 days ago | IN | 0 ETH | 0 | ||||
| Swap Exact In | 37360225 | 15 days ago | IN | 0 ETH | 0 | ||||
| Swap Exact In | 37360200 | 15 days ago | IN | 0 ETH | 0 | ||||
| Swap Exact In | 37286422 | 15 days ago | IN | 0 ETH | 0.00000001 | ||||
| Swap Exact In | 37232785 | 16 days ago | IN | 0 ETH | 0.00000001 | ||||
| Swap Exact In | 37183864 | 17 days ago | IN | 0 ETH | 0 | ||||
| Swap Exact In | 37183839 | 17 days ago | IN | 0 ETH | 0 |
Advanced mode: Intended for advanced users or developers and will display all Internal Transactions including zero value transfers.
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Block | From | To | ||||
|---|---|---|---|---|---|---|---|
| 38658655 | 15 mins ago | 0 ETH | |||||
| 38658655 | 15 mins ago | 0 ETH | |||||
| 38578663 | 22 hrs ago | 0 ETH | |||||
| 38578663 | 22 hrs ago | 0 ETH | |||||
| 38578663 | 22 hrs ago | 0 ETH | |||||
| 38578663 | 22 hrs ago | 0 ETH | |||||
| 38578663 | 22 hrs ago | 0 ETH | |||||
| 38578663 | 22 hrs ago | 0 ETH | |||||
| 38578663 | 22 hrs ago | 0 ETH | |||||
| 38578508 | 22 hrs ago | 0 ETH | |||||
| 38578508 | 22 hrs ago | 0 ETH | |||||
| 38578508 | 22 hrs ago | 0 ETH | |||||
| 38578508 | 22 hrs ago | 0 ETH | |||||
| 38572193 | 24 hrs ago | 0 ETH | |||||
| 38572193 | 24 hrs ago | 0 ETH | |||||
| 38572193 | 24 hrs ago | 0 ETH | |||||
| 38572193 | 24 hrs ago | 0 ETH | |||||
| 38572193 | 24 hrs ago | 0 ETH | |||||
| 38572193 | 24 hrs ago | 0 ETH | |||||
| 38572034 | 24 hrs ago | 0 ETH | |||||
| 38572034 | 24 hrs ago | 0 ETH | |||||
| 38572034 | 24 hrs ago | 0 ETH | |||||
| 38572034 | 24 hrs ago | 0 ETH | |||||
| 38553352 | 29 hrs ago | 0 ETH | |||||
| 38553352 | 29 hrs ago | 0 ETH |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
PSM3
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 200 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// 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;
}
}{
"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
- No Contract Security Audit Submitted- Submit Audit Here
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"}]Contract Creation Code
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
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$118,952,878.75
Net Worth in ETH
41,444.267245
Token Allocations
SUSDS
83.11%
USDS
8.49%
USDC
8.40%
Multichain Portfolio | 35 Chains
Loading...
Loading
Loading...
Loading
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.