Source Code
Latest 25 from a total of 45 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Redeem | 18996337 | 791 days ago | IN | 0 ETH | 0.00127969 | ||||
| Redeem | 18926446 | 801 days ago | IN | 0 ETH | 0.00205626 | ||||
| Redeem | 18918379 | 802 days ago | IN | 0 ETH | 0.00165207 | ||||
| Redeem | 18918260 | 802 days ago | IN | 0 ETH | 0.00156649 | ||||
| Redeem | 18918210 | 802 days ago | IN | 0 ETH | 0.00151661 | ||||
| Redeem | 18918200 | 802 days ago | IN | 0 ETH | 0.00132765 | ||||
| Redeem | 18918143 | 802 days ago | IN | 0 ETH | 0.00129132 | ||||
| Redeem | 18917782 | 802 days ago | IN | 0 ETH | 0.00109217 | ||||
| Redeem | 18368156 | 879 days ago | IN | 0 ETH | 0.00053852 | ||||
| Redeem | 18150515 | 910 days ago | IN | 0 ETH | 0.00118838 | ||||
| Redeem | 18143657 | 911 days ago | IN | 0 ETH | 0.00222118 | ||||
| Redeem | 17826226 | 955 days ago | IN | 0 ETH | 0.00136895 | ||||
| Redeem | 17776887 | 962 days ago | IN | 0 ETH | 0.00198837 | ||||
| Redeem | 17774640 | 962 days ago | IN | 0 ETH | 0.00190114 | ||||
| Redeem | 17769813 | 963 days ago | IN | 0 ETH | 0.00263232 | ||||
| Redeem | 17739737 | 967 days ago | IN | 0 ETH | 0.00189687 | ||||
| Redeem | 17725707 | 969 days ago | IN | 0 ETH | 0.00188657 | ||||
| Redeem | 17723261 | 969 days ago | IN | 0 ETH | 0.00246288 | ||||
| Redeem | 17723252 | 969 days ago | IN | 0 ETH | 0.00244687 | ||||
| Redeem | 17716705 | 970 days ago | IN | 0 ETH | 0.00152286 | ||||
| Redeem | 17697938 | 973 days ago | IN | 0 ETH | 0.00129886 | ||||
| Redeem | 17697921 | 973 days ago | IN | 0 ETH | 0.00176689 | ||||
| Redeem | 17680550 | 975 days ago | IN | 0 ETH | 0.00156751 | ||||
| Redeem | 17680481 | 975 days ago | IN | 0 ETH | 0.00169739 | ||||
| Redeem | 17680418 | 975 days ago | IN | 0 ETH | 0.00188416 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
Redeemer
Compiler Version
v0.8.16+commit.07a7930e
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
import 'src/Marketplace.sol';
import 'src/lib/Safe.sol';
import 'src/lib/Maturities.sol';
import 'src/errors/Exception.sol';
import 'src/interfaces/IERC5095.sol';
import 'src/interfaces/IERC20.sol';
import 'src/interfaces/ITempus.sol';
import 'src/interfaces/ITempusToken.sol';
import 'src/interfaces/IAPWineController.sol';
import 'src/interfaces/IAPWineFutureVault.sol';
import 'src/interfaces/IAPWineToken.sol';
import 'src/interfaces/ISwivel.sol';
import 'src/interfaces/ISwivelToken.sol';
import 'src/interfaces/IElementToken.sol';
import 'src/interfaces/IYieldToken.sol';
import 'src/interfaces/INotional.sol';
import 'src/interfaces/IPendleToken.sol';
import 'src/interfaces/IPendleYieldToken.sol';
import 'src/interfaces/IPendleSYToken.sol';
import 'src/interfaces/ISensePeriphery.sol';
import 'src/interfaces/ISenseDivider.sol';
import 'src/interfaces/ISenseAdapter.sol';
import 'src/interfaces/IConverter.sol';
/// @title Redeemer
/// @author Sourabh Marathe, Julian Traversa, Rob Robbins
/// @notice The Redeemer contract is used to redeem the underlying lent capital of a loan.
/// @notice Users may redeem their ERC-5095 tokens for the underlying asset represented by that token after maturity.
contract Redeemer {
/// @notice minimum wait before the admin may withdraw funds or change the fee rate
uint256 public constant HOLD = 3 days;
/// @notice address that is allowed to set fees and contracts, etc. It is commonly used in the authorized modifier.
address public admin;
/// @notice address of the MarketPlace contract, used to access the markets mapping
address public marketPlace;
/// @notice address that custodies principal tokens for all markets
address public lender;
/// @notice address that converts compounding tokens to their underlying
address public converter;
/// @notice third party contract needed to redeem Swivel PTs
address public immutable swivelAddr;
/// @notice third party contract needed to redeem Tempus PTs
address public immutable tempusAddr;
/// @notice this value determines the amount of fees paid on auto redemptions
uint256 public feenominator;
/// @notice represents a point in time where the feenominator may change
uint256 public feeChange;
/// @notice represents a minimum that the feenominator must exceed
uint256 public MIN_FEENOMINATOR = 500;
/// @notice mapping that indicates how much underlying has been redeemed by a market
mapping(address => mapping(uint256 => uint256)) public holdings;
/// @notice mapping that determines if a market's iPT can be redeemed
mapping(address => mapping(uint256 => bool)) public paused;
/// @notice emitted upon redemption of a loan
event Redeem(
uint8 principal,
address indexed underlying,
uint256 indexed maturity,
uint256 amount,
uint256 burned,
address sender
);
/// @notice emitted upon changing the admin
event SetAdmin(address indexed admin);
/// @notice emitted upon changing the converter
event SetConverter(address indexed converter);
/// @notice emitted upon setting the fee rate
event SetFee(uint256 indexed fee);
/// @notice emitted upon scheduling a fee change
event ScheduleFeeChange(uint256 when);
/// @notice emitted upon pausing of Illuminate PTs
event PauseRedemptions(
address indexed underlying,
uint256 maturity,
bool state
);
/// @notice ensures that only a certain address can call the function
/// @param a address that msg.sender must be to be authorized
modifier authorized(address a) {
if (msg.sender != a) {
revert Exception(0, 0, 0, msg.sender, a);
}
_;
}
/// @notice reverts on all markets where the paused mapping returns true
/// @param u address of an underlying asset
/// @param m maturity (timestamp) of the market
modifier unpaused(address u, uint256 m) {
if (paused[u][m] || ILender(lender).halted()) {
revert Exception(17, m, 0, u, address(0));
}
_;
}
/// @notice Initializes the Redeemer contract
/// @param l the lender contract
/// @param s the Swivel contract
/// @param t the Tempus contract
constructor(address l, address s, address t) {
admin = msg.sender;
lender = l;
swivelAddr = s;
tempusAddr = t;
feenominator = 4000;
}
/// @notice sets the admin address
/// @param a Address of a new admin
/// @return bool true if successful
function setAdmin(address a) external authorized(admin) returns (bool) {
admin = a;
emit SetAdmin(a);
return true;
}
/// @notice sets the address of the marketplace contract which contains the addresses of all the fixed rate markets
/// @param m the address of the marketplace contract
/// @return bool true if the address was set
function setMarketPlace(
address m
) external authorized(admin) returns (bool) {
// MarketPlace may only be set once
if (marketPlace != address(0)) {
revert Exception(5, 0, 0, marketPlace, address(0));
}
marketPlace = m;
return true;
}
/// @notice sets the converter address
/// @param c address of the new converter
/// @param i a list of interest bearing tokens the redeemer will approve
/// @return bool true if successful
function setConverter(
address c,
address[] memory i
) external authorized(admin) returns (bool) {
// Set the new converter
converter = c;
// Have the redeemer approve the new converter
for (uint256 x; x != i.length; ) {
// Approve the new converter to transfer the relevant tokens
Safe.approve(IERC20(i[x]), c, type(uint256).max);
unchecked {
x++;
}
}
emit SetConverter(c);
return true;
}
/// @notice sets the address of the lender contract which contains the addresses of all the fixed rate markets
/// @param l the address of the lender contract
/// @return bool true if the address was set
function setLender(address l) external authorized(admin) returns (bool) {
// Lender may only be set once
if (lender != address(0)) {
revert Exception(8, 0, 0, address(lender), address(0));
}
lender = l;
return true;
}
/// @notice sets the feenominator to the given value
/// @param f the new value of the feenominator, fees are not collected when the feenominator is 0
/// @return bool true if successful
function setFee(uint256 f) external authorized(admin) returns (bool) {
// Cache the minimum timestamp for executing a fee rate change
uint256 feeTime = feeChange;
// Check that a fee rate change has been scheduled
if (feeTime == 0) {
revert Exception(23, 0, 0, address(0), address(0));
// Check that the scheduled fee rate change time has been passed
} else if (block.timestamp < feeTime) {
revert Exception(
24,
block.timestamp,
feeTime,
address(0),
address(0)
);
// Check the the new fee rate is not too high
} else if (f < MIN_FEENOMINATOR) {
revert Exception(25, 0, 0, address(0), address(0));
}
// Set the new fee rate
feenominator = f;
// Unschedule the fee rate change
delete feeChange;
emit SetFee(f);
return true;
}
/// @notice allows the admin to schedule a change to the fee denominators
function scheduleFeeChange() external authorized(admin) returns (bool) {
// Calculate the timestamp that must be passed prior to setting thew new fee
uint256 when = block.timestamp + HOLD;
// Store the timestamp that must be passed to update the fee rate
feeChange = when;
emit ScheduleFeeChange(when);
return true;
}
/// @notice allows admin to stop redemptions of Illuminate PTs for a given market
/// @param u address of an underlying asset
/// @param m maturity (timestamp) of the market
/// @param b true to pause, false to unpause
function pauseRedemptions(
address u,
uint256 m,
bool b
) external authorized(admin) {
paused[u][m] = b;
emit PauseRedemptions(u, m, b);
}
/// @notice approves the converter to spend the compounding asset
/// @param i an interest bearing token that must be approved for conversion
function approve(address i) external authorized(marketPlace) {
if (i != address(0)) {
Safe.approve(IERC20(i), address(converter), type(uint256).max);
}
}
/// @notice redeem method for Yield, Element, Pendle, APWine, Tempus and Notional protocols
/// @param p principal value according to the MarketPlace's Principals Enum
/// @param u address of an underlying asset
/// @param m maturity (timestamp) of the market
/// @return bool true if the redemption was successful
function redeem(
uint8 p,
address u,
uint256 m
) external unpaused(u, m) returns (bool) {
// Get the principal token that is being redeemed by the user
address principal = IMarketPlace(marketPlace).markets(u, m, p);
// Get the maturity for the given principal token
uint256 maturity;
if (p == uint8(MarketPlace.Principals.Yield)) {
maturity = Maturities.yield(principal);
} else if (p == uint8(MarketPlace.Principals.Element)) {
maturity = Maturities.element(principal);
} else if (p == uint8(MarketPlace.Principals.Pendle)) {
maturity = Maturities.pendle(principal);
} else if (p == uint8(MarketPlace.Principals.Tempus)) {
maturity = Maturities.tempus(principal);
} else if (p == uint8(MarketPlace.Principals.Apwine)) {
maturity = Maturities.apwine(principal);
} else if (p == uint8(MarketPlace.Principals.Notional)) {
maturity = Maturities.notional(principal);
} else {
revert Exception(6, p, 0, address(0), address(0));
}
// Verify that the token has matured
if (maturity > block.timestamp) {
revert Exception(7, maturity, 0, address(0), address(0));
}
// Cache the lender to save gas on sload
address cachedLender = lender;
// Get the amount of principal tokens held by the lender
uint256 amount = IERC20(principal).balanceOf(cachedLender);
// For Pendle, we can transfer directly to the YT
address destination = address(this);
if (p == uint8(MarketPlace.Principals.Pendle)) {
destination = IPendleToken(principal).YT();
}
// Receive the principal token from the lender contract
Safe.transferFrom(IERC20(principal), cachedLender, destination, amount);
// Get the starting balance of the underlying held by the redeemer
uint256 starting = IERC20(u).balanceOf(address(this));
if (p == uint8(MarketPlace.Principals.Yield)) {
// Redeems principal tokens from Yield
IYieldToken(principal).redeem(address(this), amount);
} else if (p == uint8(MarketPlace.Principals.Element)) {
// Redeems principal tokens from Element
IElementToken(principal).withdrawPrincipal(amount, address(this));
} else if (p == uint8(MarketPlace.Principals.Pendle)) {
// Retrieve the YT for the PT
address yt = IPendleToken(principal).YT();
// Redeem the PTs to the SY token
uint256 syRedeemed = IPendleYieldToken(yt).redeemPY(address(this));
// Retreive the SY token from the PT
address sy = IPendleToken(principal).SY();
// Redeem the underlying by unwrapping the SY token
IPendleSYToken(sy).redeem(address(this), syRedeemed, u, 0, false);
} else if (p == uint8(MarketPlace.Principals.Tempus)) {
// Retrieve the pool for the principal token
address pool = ITempusToken(principal).pool();
// Redeems principal tokens from Tempus
ITempus(tempusAddr).redeemToBacking(pool, amount, 0, address(this));
} else if (p == uint8(MarketPlace.Principals.Apwine)) {
apwineWithdraw(principal, u, amount);
} else if (p == uint8(MarketPlace.Principals.Notional)) {
// Redeems principal tokens from Notional
INotional(principal).redeem(
IERC20(principal).balanceOf(address(this)),
address(this),
address(this)
);
}
// Calculate how much underlying was redeemed
uint256 redeemed = IERC20(u).balanceOf(address(this)) - starting;
// Update the holding for this market
holdings[u][m] = holdings[u][m] + redeemed;
emit Redeem(p, u, m, redeemed, amount, msg.sender);
return true;
}
/// @notice redeem method signature for Swivel
/// @param p principal value according to the MarketPlace's Principals Enum
/// @param u address of an underlying asset
/// @param m maturity (timestamp) of the market
/// @return bool true if the redemption was successful
function redeem(
uint8 p,
address u,
uint256 m,
uint8 protocol
) external unpaused(u, m) returns (bool) {
// Check the principal is Swivel
if (p != uint8(MarketPlace.Principals.Swivel)) {
revert Exception(6, p, 0, address(0), address(0));
}
// Get Swivel's principal token for this market
address token = IMarketPlace(marketPlace).markets(u, m, p);
// Get the maturity of the token
uint256 maturity = ISwivelToken(token).maturity();
// Verify that the token has matured
if (maturity > block.timestamp) {
revert Exception(7, maturity, 0, address(0), address(0));
}
// Cache the lender to save on SLOAD operations
address cachedLender = lender;
// Get the balance of tokens to be redeemed by the lenders
uint256 amount = IERC20(token).balanceOf(cachedLender);
// Transfer the lenders' tokens to the redeem contract
Safe.transferFrom(IERC20(token), cachedLender, address(this), amount);
// Get the starting balance to verify the amount received afterwards
uint256 starting = IERC20(u).balanceOf(address(this));
// Redeem principal tokens from Swivel
if (!ISwivel(swivelAddr).redeemZcToken(protocol, u, maturity, amount)) {
revert Exception(15, 0, 0, address(0), address(0));
}
// Retrieve unswapped premium from the Lender contract
ILender(cachedLender).transferPremium(u, m);
// Calculate how much underlying was redeemed
uint256 redeemed = IERC20(u).balanceOf(address(this)) - starting;
// Update the holding for this market
holdings[u][m] = holdings[u][m] + redeemed;
emit Redeem(p, u, m, redeemed, amount, msg.sender);
return true;
}
/// @notice redeem method signature for Sense
/// @param p principal value according to the MarketPlace's Principals Enum
/// @param u address of an underlying asset
/// @param m maturity (timestamp) of the market
/// @param s Sense's maturity is needed to extract the pt address
/// @param a Sense's adapter index
/// @param periphery Sense's periphery contract, used to get the verified adapter
/// @return bool true if the redemption was successful
function redeem(
uint8 p,
address u,
uint256 m,
uint256 s,
uint256 a,
address periphery
) external unpaused(u, m) returns (bool) {
// Get Sense's principal token for this market
IERC20 token = IERC20(
IMarketPlace(marketPlace).markets(
u,
m,
uint8(MarketPlace.Principals.Sense)
)
);
// Confirm the periphery is verified by the lender
if (IERC20(u).allowance(lender, periphery) == 0) {
revert Exception(29, 0, 0, address(0), address(0));
}
// Cache the lender to save on SLOAD operations
address cachedLender = lender;
// Get the balance of tokens to be redeemed by the user
uint256 amount = token.balanceOf(cachedLender);
// Transfer the user's tokens to the redeem contract
Safe.transferFrom(token, cachedLender, address(this), amount);
// Calculate the balance of the redeemer contract
uint256 redeemable = token.balanceOf(address(this));
// Get the starting balance to verify the amount received afterwards
uint256 starting = IERC20(u).balanceOf(address(this));
// Get the existing balance of Sense PTs
uint256 senseBalance = token.balanceOf(address(this));
// Get the divider from the periphery
ISenseDivider divider = ISenseDivider(
ISensePeriphery(periphery).divider()
);
// Get the adapter from the divider
address adapter = divider.adapterAddresses(a);
// Redeem the tokens from the Sense contract
ISenseDivider(divider).redeem(adapter, s, senseBalance);
// Get the compounding token that is redeemed by Sense
address compounding = ISenseAdapter(adapter).target();
// Redeem the compounding token back to the underlying
IConverter(converter).convert(
compounding,
u,
IERC20(compounding).balanceOf(address(this))
);
// Get the amount received
uint256 redeemed = IERC20(u).balanceOf(address(this)) - starting;
// Update the holdings for this market
holdings[u][m] = holdings[u][m] + redeemed;
emit Redeem(p, u, m, redeemed, redeemable, msg.sender);
return true;
}
/// @notice burns Illuminate principal tokens and sends underlying to user
/// @param u address of an underlying asset
/// @param m maturity (timestamp) of the market
function redeem(address u, uint256 m) external unpaused(u, m) {
// Get Illuminate's principal token for this market
IERC5095 token = IERC5095(
IMarketPlace(marketPlace).markets(
u,
m,
uint8(MarketPlace.Principals.Illuminate)
)
);
// Verify the token has matured
if (block.timestamp < token.maturity()) {
revert Exception(7, block.timestamp, m, address(0), address(0));
}
// Get the amount of tokens to be redeemed from the sender
uint256 amount = token.balanceOf(msg.sender);
// Calculate how many tokens the user should receive
uint256 redeemed = (amount * holdings[u][m]) / token.totalSupply();
// Update holdings of underlying
holdings[u][m] = holdings[u][m] - redeemed;
// Burn the user's principal tokens
token.authBurn(msg.sender, amount);
// Transfer the original underlying token back to the user
Safe.transfer(IERC20(u), msg.sender, redeemed);
emit Redeem(0, u, m, redeemed, amount, msg.sender);
}
/// @notice implements the redeem method for the contract to fulfill the ERC-5095 interface
/// @param u address of an underlying asset
/// @param m maturity (timestamp) of the market
/// @param f address from where the underlying asset will be burned
/// @param t address to where the underlying asset will be transferred
/// @param a amount of the Illuminate PT to be burned and redeemed
/// @return uint256 amount of the underlying asset that was burned
function authRedeem(
address u,
uint256 m,
address f,
address t,
uint256 a
)
external
authorized(IMarketPlace(marketPlace).markets(u, m, 0))
unpaused(u, m)
returns (uint256)
{
// Get the principal token for the given market
IERC5095 pt = IERC5095(IMarketPlace(marketPlace).markets(u, m, 0));
// Make sure the market has matured
uint256 maturity = pt.maturity();
if (block.timestamp < maturity) {
revert Exception(7, maturity, 0, address(0), address(0));
}
// Calculate the amount redeemed
uint256 redeemed = (a * holdings[u][m]) / pt.totalSupply();
// Update holdings of underlying
holdings[u][m] = holdings[u][m] - redeemed;
// Burn the user's principal tokens
pt.authBurn(f, a);
// Transfer the original underlying token back to the user
Safe.transfer(IERC20(u), t, redeemed);
emit Redeem(0, u, m, redeemed, a, msg.sender);
return a;
}
/// @notice implements a redeem method to enable third-party redemptions
/// @dev expects approvals from owners to redeemer
/// @param u address of the underlying asset
/// @param m maturity of the market
/// @param f address from where the principal token will be burned
/// @return uint256 amount of underlying yielded as a fee
function autoRedeem(
address u,
uint256 m,
address[] calldata f
) external unpaused(u, m) returns (uint256) {
// Get the principal token for the given market
IERC5095 pt = IERC5095(IMarketPlace(marketPlace).markets(u, m, 0));
// Make sure the market has matured
if (block.timestamp < pt.maturity()) {
revert Exception(7, pt.maturity(), 0, address(0), address(0));
}
// Sum up the fees received by the caller
uint256 incentiveFee;
// Loop through the provided arrays and mature each individual position
for (uint256 i; i != f.length; ) {
// Fetch the allowance set by the holder of the principal tokens
uint256 allowance = pt.allowance(f[i], address(this));
// Get the amount of tokens held by the owner
uint256 amount = pt.balanceOf(f[i]);
// Calculate how many tokens the user should receive
uint256 redeemed = (amount * holdings[u][m]) / pt.totalSupply();
// Calculate the fees to be received
uint256 fee = redeemed / feenominator;
// Verify allowance
if (allowance < amount) {
revert Exception(20, allowance, amount, address(0), address(0));
}
// Burn the tokens from the user
pt.authBurn(f[i], amount);
// Reduce the allowance of the burned tokens
pt.authApprove(f[i], address(this), 0);
// Update the holdings for this market
holdings[u][m] = holdings[u][m] - redeemed;
// Transfer the underlying to the user
Safe.transfer(IERC20(u), f[i], redeemed - fee);
unchecked {
// Track the fees gained by the caller
incentiveFee += fee;
++i;
}
}
// Transfer the fee to the caller
Safe.transfer(IERC20(u), msg.sender, incentiveFee);
return incentiveFee;
}
/// @notice Allows for external deposit of underlying for a market
/// @notice This is to be used in emergency situations where the redeem method is not functioning for a market
/// @param u address of the underlying asset
/// @param m maturity of the market
/// @param a amount of underlying to be deposited
function depositHoldings(address u, uint256 m, uint256 a) external {
// Receive the underlying asset from the admin
Safe.transferFrom(IERC20(u), msg.sender, address(this), a);
// Update the holdings
holdings[u][m] += a;
}
/// @notice Execute the business logic for conducting an APWine redemption
function apwineWithdraw(address p, address u, uint256 a) internal {
// Retrieve the vault which executes the redemption in APWine
address futureVault = IAPWineToken(p).futureVault();
// Retrieve the controller that will execute the withdrawal
address controller = IAPWineFutureVault(futureVault)
.getControllerAddress();
// Retrieve the next period index
uint256 index = IAPWineFutureVault(futureVault).getCurrentPeriodIndex();
// Get the FYT address for the current period
address fyt = IAPWineFutureVault(futureVault).getFYTofPeriod(index);
// Ensure there are sufficient FYTs to execute the redemption
uint256 amount = IERC20(fyt).balanceOf(address(lender));
// Get the minimum between the FYT and PT balance to redeem
if (amount > a) {
amount = a;
}
// Trigger claim to FYTs by executing transfer
ILender(lender).transferFYTs(fyt, amount);
// Redeem the underlying token from APWine to Illuminate
IAPWineController(controller).withdraw(futureVault, amount);
// Retrieve the interest bearing token
address ibt = IAPWineFutureVault(futureVault).getIBTAddress();
// Convert the interest bearing token to underlying
IConverter(converter).convert(
IAPWineFutureVault(futureVault).getIBTAddress(),
u,
IERC20(ibt).balanceOf(address(this))
);
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
import 'src/tokens/ERC5095.sol';
import 'src/lib/Safe.sol';
import 'src/lib/RevertMsgExtractor.sol';
import 'src/errors/Exception.sol';
import 'src/interfaces/ILender.sol';
import 'src/interfaces/ICreator.sol';
import 'src/interfaces/IPool.sol';
import 'src/interfaces/IPendleToken.sol';
import 'src/interfaces/IAPWineToken.sol';
import 'src/interfaces/IAPWineFutureVault.sol';
/// @title MarketPlace
/// @author Sourabh Marathe, Julian Traversa, Rob Robbins
/// @notice This contract is in charge of managing the available principals for each loan market.
/// @notice In addition, this contract routes swap orders between Illuminate PTs and their respective underlying to YieldSpace pools.
contract MarketPlace {
/// @notice the available principals
/// @dev the order of this enum is used to select principals from the markets
/// mapping (e.g. Illuminate => 0, Swivel => 1, and so on)
enum Principals {
Illuminate, // 0
Swivel, // 1
Yield, // 2
Element, // 3
Pendle, // 4
Tempus, // 5
Sense, // 6
Apwine, // 7
Notional // 8
}
/// @notice markets are defined by a tuple that points to a fixed length array of principal token addresses.
mapping(address => mapping(uint256 => address[9])) public markets;
/// @notice pools map markets to their respective YieldSpace pools for the MetaPrincipal token
mapping(address => mapping(uint256 => address)) public pools;
/// @notice address that is allowed to create markets, set pools, etc. It is commonly used in the authorized modifier.
address public admin;
/// @notice address of the deployed redeemer contract
address public immutable redeemer;
/// @notice address of the deployed lender contract
address public immutable lender;
/// @notice address of the deployed creator contract
address public immutable creator;
/// @notice emitted upon the creation of a new market
event CreateMarket(
address indexed underlying,
uint256 indexed maturity,
address[9] tokens,
address element,
address apwine
);
/// @notice emitted upon setting a principal token
event SetPrincipal(
address indexed underlying,
uint256 indexed maturity,
address indexed principal,
uint8 protocol
);
/// @notice emitted upon swapping with the pool
event Swap(
address indexed underlying,
uint256 indexed maturity,
address sold,
address bought,
uint256 received,
uint256 spent,
address spender
);
/// @notice emitted upon minting tokens with the pool
event Mint(
address indexed underlying,
uint256 indexed maturity,
uint256 underlyingIn,
uint256 principalTokensIn,
uint256 minted,
address minter
);
/// @notice emitted upon burning tokens with the pool
event Burn(
address indexed underlying,
uint256 indexed maturity,
uint256 tokensBurned,
uint256 underlyingReceived,
uint256 principalTokensReceived,
address burner
);
/// @notice emitted upon changing the admin
event SetAdmin(address indexed admin);
/// @notice emitted upon setting a pool
event SetPool(
address indexed underlying,
uint256 indexed maturity,
address indexed pool
);
/// @notice ensures that only a certain address can call the function
/// @param a address that msg.sender must be to be authorized
modifier authorized(address a) {
if (msg.sender != a) {
revert Exception(0, 0, 0, msg.sender, a);
}
_;
}
/// @notice initializes the MarketPlace contract
/// @param r address of the deployed redeemer contract
/// @param l address of the deployed lender contract
/// @param c address of the deployed creator contract
constructor(address r, address l, address c) {
admin = msg.sender;
redeemer = r;
lender = l;
creator = c;
}
/// @notice creates a new market for the given underlying token and maturity
/// @param u address of an underlying asset
/// @param m maturity (timestamp) of the market
/// @param t principal token addresses for this market
/// @param n name for the Illuminate token
/// @param s symbol for the Illuminate token
/// @param a address of the APWine router that corresponds to this market
/// @param e address of the Element vault that corresponds to this market
/// @param h address of a helper contract, used for Sense approvals if active in the market
/// @param sensePeriphery address of the Sense periphery contract that must be approved by the lender
/// @return bool true if successful
function createMarket(
address u,
uint256 m,
address[8] calldata t,
string calldata n,
string calldata s,
address a,
address e,
address h,
address sensePeriphery
) external authorized(admin) returns (bool) {
{
// Get the Illuminate principal token for this market (if one exists)
address illuminate = markets[u][m][0];
// If illuminate PT already exists, a new market cannot be created
if (illuminate != address(0)) {
revert Exception(9, 0, 0, illuminate, address(0));
}
}
// Create an Illuminate principal token for the new market
address illuminateToken = ICreator(creator).create(
u,
m,
redeemer,
lender,
address(this),
n,
s
);
{
// create the principal tokens array
address[9] memory market = [
illuminateToken, // Illuminate
t[0], // Swivel
t[1], // Yield
t[2], // Element
t[3], // Pendle
t[4], // Tempus
t[5], // Sense
t[6], // APWine
t[7] // Notional
];
// Set the market
markets[u][m] = market;
// Have the lender contract approve the several contracts
ILender(lender).approve(u, a, e, t[7], sensePeriphery);
// Allow converter to spend interest bearing asset
if (t[5] != address(0)) {
IRedeemer(redeemer).approve(h);
}
// Approve interest bearing token conversion to underlying for APWine
if (t[6] != address(0)) {
address futureVault = IAPWineToken(t[6]).futureVault();
address interestBearingToken = IAPWineFutureVault(futureVault)
.getIBTAddress();
IRedeemer(redeemer).approve(interestBearingToken);
}
emit CreateMarket(u, m, market, e, a);
}
return true;
}
/// @notice allows the admin to set an individual market
/// @param p principal value according to the MarketPlace's Principals Enum
/// @param u address of an underlying asset
/// @param m maturity (timestamp) of the market
/// @param a address of the new principal token
/// @param h a supplementary address (apwine needs a router, element needs a vault, sense needs interest bearing asset)
/// @param sensePeriphery address of the Sense periphery contract that must be approved by the lender
/// @return bool true if the principal set, false otherwise
function setPrincipal(
uint8 p,
address u,
uint256 m,
address a,
address h,
address sensePeriphery
) external authorized(admin) returns (bool) {
// Set the principal token in the markets mapping
markets[u][m][p] = a;
if (p == uint8(Principals.Element)) {
// Approve Element vault if setting Element's principal token
ILender(lender).approve(u, address(0), h, address(0), address(0));
} else if (p == uint8(Principals.Sense)) {
// Approve converter to transfer yield token for Sense's redeem
IRedeemer(redeemer).approve(h);
// Approve Periphery to be used from Lender
ILender(lender).approve(
u,
address(0),
address(0),
address(0),
sensePeriphery
);
} else if (p == uint8(Principals.Apwine)) {
// Approve converter to transfer yield token for APWine's redeem
address futureVault = IAPWineToken(a).futureVault();
address interestBearingToken = IAPWineFutureVault(futureVault)
.getIBTAddress();
IRedeemer(redeemer).approve(interestBearingToken);
// Approve APWine's router if setting APWine's principal token
ILender(lender).approve(u, h, address(0), address(0), address(0));
} else if (p == uint8(Principals.Notional)) {
// Principal token must be approved for Notional's lend
ILender(lender).approve(u, address(0), address(0), a, address(0));
}
emit SetPrincipal(u, m, a, p);
return true;
}
/// @notice sets the admin address
/// @param a Address of a new admin
/// @return bool true if the admin set, false otherwise
function setAdmin(address a) external authorized(admin) returns (bool) {
admin = a;
emit SetAdmin(a);
return true;
}
/// @notice sets the address for a pool
/// @param u address of an underlying asset
/// @param m maturity (timestamp) of the market
/// @param a address of the pool
/// @return bool true if the pool set, false otherwise
function setPool(
address u,
uint256 m,
address a
) external authorized(admin) returns (bool) {
// Set the pool
pools[u][m] = a;
// Get the principal token
ERC5095 pt = ERC5095(markets[u][m][uint8(Principals.Illuminate)]);
// Set the pool for the principal token
pt.setPool(a);
// Approve the marketplace to spend the principal and underlying tokens
pt.approveMarketPlace();
emit SetPool(u, m, a);
return true;
}
/// @notice sells the PT for the underlying via the pool
/// @param u address of an underlying asset
/// @param m maturity (timestamp) of the market
/// @param a amount of PTs to sell
/// @param s slippage cap, minimum amount of underlying that must be received
/// @return uint128 amount of underlying bought
function sellPrincipalToken(
address u,
uint256 m,
uint128 a,
uint128 s
) external returns (uint128) {
// Get the pool for the market
IPool pool = IPool(pools[u][m]);
// Preview amount of underlying received by selling `a` PTs
uint256 expected = pool.sellFYTokenPreview(a);
// Verify that the amount needed does not exceed the slippage parameter
if (expected < s) {
revert Exception(16, expected, s, address(0), address(0));
}
// Transfer the principal tokens to the pool
Safe.transferFrom(
IERC20(address(pool.fyToken())),
msg.sender,
address(pool),
a
);
// Execute the swap
uint128 received = pool.sellFYToken(msg.sender, s);
emit Swap(u, m, address(pool.fyToken()), u, received, a, msg.sender);
return received;
}
/// @notice buys the PT for the underlying via the pool
/// @notice determines how many underlying to sell by using the preview
/// @param u address of an underlying asset
/// @param m maturity (timestamp) of the market
/// @param a amount of PTs to be purchased
/// @param s slippage cap, maximum number of underlying that can be sold
/// @return uint128 amount of underlying sold
function buyPrincipalToken(
address u,
uint256 m,
uint128 a,
uint128 s
) external returns (uint128) {
// Get the pool for the market
IPool pool = IPool(pools[u][m]);
// Get the amount of base hypothetically required to purchase `a` PTs
uint128 expected = pool.buyFYTokenPreview(a);
// Verify that the amount needed does not exceed the slippage parameter
if (expected > s) {
revert Exception(16, expected, 0, address(0), address(0));
}
// Transfer the underlying tokens to the pool
Safe.transferFrom(
IERC20(pool.base()),
msg.sender,
address(pool),
expected
);
// Execute the swap to purchase `a` base tokens
uint128 spent = pool.buyFYToken(msg.sender, a, s);
emit Swap(u, m, u, address(pool.fyToken()), a, spent, msg.sender);
return spent;
}
/// @notice sells the underlying for the PT via the pool
/// @param u address of an underlying asset
/// @param m maturity (timestamp) of the market
/// @param a amount of underlying to sell
/// @param s slippage cap, minimum number of PTs that must be received
/// @return uint128 amount of PT purchased
function sellUnderlying(
address u,
uint256 m,
uint128 a,
uint128 s
) external returns (uint128) {
// Get the pool for the market
IPool pool = IPool(pools[u][m]);
// Get the number of PTs received for selling `a` underlying tokens
uint128 expected = pool.sellBasePreview(a);
// Verify slippage does not exceed the one set by the user
if (expected < s) {
revert Exception(16, expected, 0, address(0), address(0));
}
// Transfer the underlying tokens to the pool
Safe.transferFrom(IERC20(pool.base()), msg.sender, address(pool), a);
// Execute the swap
uint128 received = pool.sellBase(msg.sender, s);
emit Swap(u, m, u, address(pool.fyToken()), received, a, msg.sender);
return received;
}
/// @notice buys the underlying for the PT via the pool
/// @notice determines how many PTs to sell by using the preview
/// @param u address of an underlying asset
/// @param m maturity (timestamp) of the market
/// @param a amount of underlying to be purchased
/// @param s slippage cap, maximum number of PTs that can be sold
/// @return uint128 amount of PTs sold
function buyUnderlying(
address u,
uint256 m,
uint128 a,
uint128 s
) external returns (uint128) {
// Get the pool for the market
IPool pool = IPool(pools[u][m]);
// Get the amount of PTs hypothetically required to purchase `a` underlying
uint256 expected = pool.buyBasePreview(a);
// Verify that the amount needed does not exceed the slippage parameter
if (expected > s) {
revert Exception(16, expected, 0, address(0), address(0));
}
// Transfer the principal tokens to the pool
Safe.transferFrom(
IERC20(address(pool.fyToken())),
msg.sender,
address(pool),
expected
);
// Execute the swap to purchase `a` underlying tokens
uint128 spent = pool.buyBase(msg.sender, a, s);
emit Swap(u, m, address(pool.fyToken()), u, a, spent, msg.sender);
return spent;
}
/// @notice mint liquidity tokens in exchange for adding underlying and PT
/// @dev amount of liquidity tokens to mint is calculated from the amount of unaccounted for PT in this contract.
/// @dev A proportional amount of underlying tokens need to be present in this contract, also unaccounted for.
/// @param u the address of the underlying token
/// @param m the maturity of the principal token
/// @param b number of base tokens
/// @param p the principal token amount being sent
/// @param minRatio minimum ratio of LP tokens to PT in the pool.
/// @param maxRatio maximum ratio of LP tokens to PT in the pool.
/// @return uint256 number of base tokens passed to the method
/// @return uint256 number of yield tokens passed to the method
/// @return uint256 the amount of tokens minted.
function mint(
address u,
uint256 m,
uint256 b,
uint256 p,
uint256 minRatio,
uint256 maxRatio
) external returns (uint256, uint256, uint256) {
// Get the pool for the market
IPool pool = IPool(pools[u][m]);
// Transfer the underlying tokens to the pool
Safe.transferFrom(IERC20(pool.base()), msg.sender, address(pool), b);
// Transfer the principal tokens to the pool
Safe.transferFrom(
IERC20(address(pool.fyToken())),
msg.sender,
address(pool),
p
);
// Mint the tokens and return the leftover assets to the caller
(uint256 underlyingIn, uint256 principalTokensIn, uint256 minted) = pool
.mint(msg.sender, msg.sender, minRatio, maxRatio);
emit Mint(u, m, underlyingIn, principalTokensIn, minted, msg.sender);
return (underlyingIn, principalTokensIn, minted);
}
/// @notice Mint liquidity tokens in exchange for adding only underlying
/// @dev amount of liquidity tokens is calculated from the amount of PT to buy from the pool,
/// plus the amount of unaccounted for PT in this contract.
/// @param u the address of the underlying token
/// @param m the maturity of the principal token
/// @param a the underlying amount being sent
/// @param p amount of `PT` being bought in the Pool, from this we calculate how much underlying it will be taken in.
/// @param minRatio minimum ratio of LP tokens to PT in the pool.
/// @param maxRatio maximum ratio of LP tokens to PT in the pool.
/// @return uint256 number of base tokens passed to the method
/// @return uint256 number of yield tokens passed to the method
/// @return uint256 the amount of tokens minted.
function mintWithUnderlying(
address u,
uint256 m,
uint256 a,
uint256 p,
uint256 minRatio,
uint256 maxRatio
) external returns (uint256, uint256, uint256) {
// Get the pool for the market
IPool pool = IPool(pools[u][m]);
// Transfer the underlying tokens to the pool
Safe.transferFrom(IERC20(pool.base()), msg.sender, address(pool), a);
// Mint the tokens to the user
(uint256 underlyingIn, , uint256 minted) = pool.mintWithBase(
msg.sender,
msg.sender,
p,
minRatio,
maxRatio
);
emit Mint(u, m, underlyingIn, 0, minted, msg.sender);
return (underlyingIn, 0, minted);
}
/// @notice burn liquidity tokens in exchange for underlying and PT.
/// @param u the address of the underlying token
/// @param m the maturity of the principal token
/// @param a the amount of liquidity tokens to burn
/// @param minRatio minimum ratio of LP tokens to PT in the pool
/// @param maxRatio maximum ratio of LP tokens to PT in the pool
/// @return uint256 amount of LP tokens burned
/// @return uint256 amount of base tokens received
/// @return uint256 amount of fyTokens received
function burn(
address u,
uint256 m,
uint256 a,
uint256 minRatio,
uint256 maxRatio
) external returns (uint256, uint256, uint256) {
// Get the pool for the market
IPool pool = IPool(pools[u][m]);
// Transfer the underlying tokens to the pool
Safe.transferFrom(IERC20(address(pool)), msg.sender, address(pool), a);
// Burn the tokens
(
uint256 tokensBurned,
uint256 underlyingReceived,
uint256 principalTokensReceived
) = pool.burn(msg.sender, msg.sender, minRatio, maxRatio);
emit Burn(
u,
m,
tokensBurned,
underlyingReceived,
principalTokensReceived,
msg.sender
);
return (tokensBurned, underlyingReceived, principalTokensReceived);
}
/// @notice burn liquidity tokens in exchange for underlying.
/// @param u the address of the underlying token
/// @param m the maturity of the principal token
/// @param a the amount of liquidity tokens to burn
/// @param minRatio minimum ratio of LP tokens to PT in the pool.
/// @param maxRatio minimum ratio of LP tokens to PT in the pool.
/// @return uint256 amount of PT tokens sent to the pool
/// @return uint256 amount of underlying tokens returned
function burnForUnderlying(
address u,
uint256 m,
uint256 a,
uint256 minRatio,
uint256 maxRatio
) external returns (uint256, uint256) {
// Get the pool for the market
IPool pool = IPool(pools[u][m]);
// Transfer the underlying tokens to the pool
Safe.transferFrom(IERC20(address(pool)), msg.sender, address(pool), a);
// Burn the tokens in exchange for underlying tokens
(uint256 tokensBurned, uint256 underlyingReceived) = pool.burnForBase(
msg.sender,
minRatio,
maxRatio
);
emit Burn(u, m, tokensBurned, underlyingReceived, 0, msg.sender);
return (tokensBurned, underlyingReceived);
}
/// @notice Allows batched call to self (this contract).
/// @param c An array of inputs for each call.
function batch(
bytes[] calldata c
) external payable returns (bytes[] memory results) {
results = new bytes[](c.length);
for (uint256 i; i < c.length; i++) {
(bool success, bytes memory result) = address(this).delegatecall(
c[i]
);
if (!success) revert(RevertMsgExtractor.getRevertMsg(result));
results[i] = result;
}
}
}// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; /// @dev A single custom error capable of indicating a wide range of detected errors by providing /// an error code value whose string representation is documented in errors.txt, and any possible other values /// that are pertinent to the error. error Exception(uint8, uint256, uint256, address, address);
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
interface IAPWineController {
function getNextPeriodStart(uint256) external view returns (uint256);
function withdraw(address, uint256) external;
function createFYTDelegationTo(
address,
address,
uint256
) external;
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
interface IAPWineFutureVault {
function PERIOD_DURATION() external view returns (uint256);
function getControllerAddress() external view returns (address);
function getCurrentPeriodIndex() external view returns (uint256);
function getFYTofPeriod(uint256) external view returns (address);
function getIBTAddress() external view returns (address);
function startNewPeriod() external;
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
interface IAPWineToken {
function futureVault() external view returns (address);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
interface IAny {}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
interface IConverter {
function convert(
address,
address,
uint256
) external;
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
interface ICreator {
function create(
address,
uint256,
address,
address,
address,
string calldata,
string calldata
) external returns (address);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount)
external
returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender)
external
view
returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
/**
* @dev Returns the number of decimals the token uses - e.g. 8, means to
* divide the token amount by 100000000 to get its user representation.
*/
function decimals() external view returns (uint8);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
import 'src/interfaces/IERC20.sol';
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
import 'src/interfaces/IERC20Metadata.sol';
/**
* @dev Interface of the ERC2612 standard as defined in the EIP.
*
* Adds the {permit} method, which can be used to change one's
* {IERC20-allowance} without having to send a transaction, by signing a
* message. This allows users to spend tokens without having to hold Ether.
*
* See https://eips.ethereum.org/EIPS/eip-2612.
*/
interface IERC2612 is IERC20Metadata {
/**
* @dev Sets `amount` as the allowance of `spender` over `owner`'s tokens,
* given `owner`'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*/
function permit(
address owner,
address spender,
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current ERC2612 nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
import 'src/interfaces/IERC2612.sol';
interface IERC5095 is IERC2612 {
function maturity() external view returns (uint256);
function underlying() external view returns (address);
function convertToUnderlying(uint256) external view returns (uint256);
function convertToShares(uint256) external view returns (uint256);
function maxRedeem(address) external view returns (uint256);
function previewRedeem(uint256) external view returns (uint256);
function maxWithdraw(address) external view returns (uint256);
function previewWithdraw(uint256) external view returns (uint256);
function previewDeposit(uint256) external view returns (uint256);
function withdraw(
uint256,
address,
address
) external returns (uint256);
function redeem(
uint256,
address,
address
) external returns (uint256);
function deposit(uint256, address) external returns (uint256);
function mint(uint256, address) external returns (uint256);
function authMint(address, uint256) external returns (bool);
function authBurn(address, uint256) external returns (bool);
function authApprove(
address,
address,
uint256
) external returns (bool);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
interface IElementToken {
function unlockTimestamp() external view returns (uint256);
function underlying() external returns (address);
function withdrawPrincipal(uint256 amount, address destination)
external
returns (uint256);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
interface ILender {
function approve(
address,
address,
address,
address,
address
) external;
function transferFYTs(address, uint256) external;
function transferPremium(address, uint256) external;
function paused(uint8) external returns (bool);
function halted() external returns (bool);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
interface IMarketPlace {
function markets(
address,
uint256,
uint256
) external returns (address);
function pools(address, uint256) external view returns (address);
function sellPrincipalToken(
address,
uint256,
uint128,
uint128
) external returns (uint128);
function buyPrincipalToken(
address,
uint256,
uint128,
uint128
) external returns (uint128);
function sellUnderlying(
address,
uint256,
uint128,
uint128
) external returns (uint128);
function buyUnderlying(
address,
uint256,
uint128,
uint128
) external returns (uint128);
function redeemer() external view returns (address);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
import 'src/interfaces/IERC20.sol';
interface INotional {
function getUnderlyingToken() external view returns (IERC20, int256);
function getMaturity() external view returns (uint40);
function deposit(uint256, address) external returns (uint256);
function maxRedeem(address) external returns (uint256);
function redeem(
uint256,
address,
address
) external returns (uint256);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
interface IPendleSYToken {
function redeem(
address,
uint256,
address,
uint256,
bool
) external returns (uint256);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
interface IPendleToken {
function SY() external view returns (address);
function YT() external view returns (address);
function expiry() external view returns (uint256);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
interface IPendleYieldToken {
function redeemPY(address) external returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import 'src/interfaces/IERC20.sol';
import 'src/interfaces/IERC5095.sol';
interface IPool {
function ts() external view returns (int128);
function g1() external view returns (int128);
function g2() external view returns (int128);
function maturity() external view returns (uint32);
function scaleFactor() external view returns (uint96);
function getCache()
external
view
returns (
uint112,
uint112,
uint32
);
// NOTE This will be deprecated
function base() external view returns (IERC20);
function baseToken() external view returns (address);
function fyToken() external view returns (IERC5095);
function getBaseBalance() external view returns (uint112);
function getFYTokenBalance() external view returns (uint112);
function retrieveBase(address) external returns (uint128 retrieved);
function retrieveFYToken(address) external returns (uint128 retrieved);
function sellBase(address, uint128) external returns (uint128);
function buyBase(
address,
uint128,
uint128
) external returns (uint128);
function sellFYToken(address, uint128) external returns (uint128);
function buyFYToken(
address,
uint128,
uint128
) external returns (uint128);
function sellBasePreview(uint128) external view returns (uint128);
function buyBasePreview(uint128) external view returns (uint128);
function sellFYTokenPreview(uint128) external view returns (uint128);
function buyFYTokenPreview(uint128) external view returns (uint128);
function mint(
address,
address,
uint256,
uint256
)
external
returns (
uint256,
uint256,
uint256
);
function mintWithBase(
address,
address,
uint256,
uint256,
uint256
)
external
returns (
uint256,
uint256,
uint256
);
function burn(
address,
address,
uint256,
uint256
)
external
returns (
uint256,
uint256,
uint256
);
function burnForBase(
address,
uint256,
uint256
) external returns (uint256, uint256);
function cumulativeBalancesRatio() external view returns (uint256);
function sync() external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
interface IRedeemer {
function authRedeem(
address underlying,
uint256 maturity,
address from,
address to,
uint256 amount
) external returns (uint256);
function approve(address p) external;
function holdings(address u, uint256 m) external view returns (uint256);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
interface ISenseAdapter {
function underlying() external view returns (address);
function divider() external view returns (address);
function target() external view returns (address);
function maxm() external view returns (uint256);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
interface ISenseDivider {
function redeem(
address,
uint256,
uint256
) external returns (uint256);
function pt(address, uint256) external view returns (address);
// only used by integration tests
function settleSeries(address, uint256) external;
function adapterAddresses(uint256) external view returns (address);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
interface ISensePeriphery {
function divider() external view returns (address);
function swapUnderlyingForPTs(
address,
uint256,
uint256,
uint256
) external returns (uint256);
function verified(address) external view returns (bool);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
import 'src/lib/Swivel.sol';
interface ISwivel {
function initiate(
Swivel.Order[] calldata,
uint256[] calldata,
Swivel.Components[] calldata
) external returns (bool);
function redeemZcToken(
uint8 p,
address u,
uint256 m,
uint256 a
) external returns (bool);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
interface ISwivelToken {
function maturity() external view returns (uint256);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
import 'src/interfaces/IERC20Metadata.sol';
import 'src/interfaces/IAny.sol';
interface ITempus {
function depositAndFix(
address,
uint256,
bool,
uint256,
uint256
) external;
function redeemToBacking(
address,
uint256,
uint256,
address
) external;
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
import 'src/interfaces/IERC20Metadata.sol';
interface ITempusPool {
function maturityTime() external view returns (uint256);
function backingToken() external view returns (address);
function controller() external view returns (address);
// Used for integration testing
function principalShare() external view returns (address);
function currentInterestRate() external view returns (uint256);
function initialInterestRate() external view returns (uint256);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
interface ITempusToken {
function balanceOf(address) external returns (uint256);
function pool() external view returns (address);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
import 'src/interfaces/IERC20.sol';
interface IYield {
function maturity() external view returns (uint32);
function base() external view returns (IERC20);
function sellBase(address, uint128) external returns (uint128);
function sellBasePreview(uint128) external view returns (uint128);
function fyToken() external returns (address);
function sellFYToken(address, uint128) external returns (uint128);
function sellFYTokenPreview(uint128) external view returns (uint128);
function buyBase(
address,
uint128,
uint128
) external returns (uint128);
function buyBasePreview(uint128) external view returns (uint128);
function buyFYToken(
address,
uint128,
uint128
) external returns (uint128);
function buyFYTokenPreview(uint128) external view returns (uint128);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
interface IYieldToken {
function redeem(address, uint256) external returns (uint256);
function underlying() external returns (address);
function maturity() external view returns (uint256);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
library Cast {
/// @dev Safely cast an uint256 to an uint128
/// @param n the u256 to cast to u128
function u128(uint256 n) internal pure returns (uint128) {
if (n > type(uint128).max) {
revert();
}
return uint128(n);
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
import 'src/interfaces/IERC5095.sol';
import 'src/interfaces/ISwivelToken.sol';
import 'src/interfaces/IYieldToken.sol';
import 'src/interfaces/IElementToken.sol';
import 'src/interfaces/IPendleToken.sol';
import 'src/interfaces/ITempusToken.sol';
import 'src/interfaces/ITempusPool.sol';
import 'src/interfaces/IAPWineToken.sol';
import 'src/interfaces/IAPWineFutureVault.sol';
import 'src/interfaces/IAPWineController.sol';
import 'src/interfaces/INotional.sol';
library Maturities {
/// @notice returns the maturity for an Illumiante principal token
/// @param p address of the principal token contract
/// @return uint256 maturity of the principal token
function illuminate(address p) internal view returns (uint256) {
return IERC5095(p).maturity();
}
/// @notice returns the maturity for a Swivel principal token
/// @param p address of the principal token contract
/// @return uint256 maturity of the principal token
function swivel(address p) internal view returns (uint256) {
return ISwivelToken(p).maturity();
}
function yield(address p) internal view returns (uint256) {
return IYieldToken(p).maturity();
}
/// @notice returns the maturity for an Element principal token
/// @param p address of the principal token contract
/// @return uint256 maturity of the principal token
function element(address p) internal view returns (uint256) {
return IElementToken(p).unlockTimestamp();
}
/// @notice returns the maturity for a Pendle principal token
/// @param p address of the principal token contract
/// @return uint256 maturity of the principal token
function pendle(address p) internal view returns (uint256) {
return IPendleToken(p).expiry();
}
/// @notice returns the maturity for a Tempus principal token
/// @param p address of the principal token contract
/// @return uint256 maturity of the principal token
function tempus(address p) internal view returns (uint256) {
return ITempusPool(ITempusToken(p).pool()).maturityTime();
}
/// @notice returns the maturity for a APWine principal token
/// @param p address of the principal token contract
/// @return uint256 maturity of the principal token
function apwine(address p) internal view returns (uint256) {
address futureVault = IAPWineToken(p).futureVault();
address controller = IAPWineFutureVault(futureVault)
.getControllerAddress();
uint256 duration = IAPWineFutureVault(futureVault).PERIOD_DURATION();
return IAPWineController(controller).getNextPeriodStart(duration);
}
/// @notice returns the maturity for a Notional principal token
/// @param p address of the principal token contract
/// @return uint256 maturity of the principal token
function notional(address p) internal view returns (uint256) {
return INotional(p).getMaturity();
}
}// SPDX-License-Identifier: MIT
// Taken from https://github.com/sushiswap/BoringSolidity/blob/441e51c0544cf2451e6116fe00515e71d7c42e2c/contracts/BoringBatchable.sol
pragma solidity >=0.6.0;
library RevertMsgExtractor {
/// @dev Helper function to extract a useful revert message from a failed call.
/// If the returned data is malformed or not correctly abi encoded then this call can fail itself.
function getRevertMsg(bytes memory returnData)
internal
pure
returns (string memory)
{
// If the _res length is less than 68, then the transaction failed silently (without a revert message)
if (returnData.length < 68) return 'Transaction reverted silently';
assembly {
// Slice the sighash.
returnData := add(returnData, 0x04)
}
return abi.decode(returnData, (string)); // All that remains is the revert string
}
}// SPDX-License-Identifier: UNLICENSED
// Adapted from: https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol
pragma solidity ^0.8.13;
import 'src/interfaces/IERC20.sol';
/**
@notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
@author Modified from Gnosis (https://github.com/gnosis/gp-v2-contracts/blob/main/src/contracts/libraries/GPv2SafeERC20.sol)
@dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
*/
library Safe {
/// @param e Erc20 token to execute the call with
/// @param t To address
/// @param a Amount being transferred
function transfer(
IERC20 e,
address t,
uint256 a
) internal {
bool result;
assembly {
// Get a pointer to some free memory.
let pointer := mload(0x40)
// Write the abi-encoded calldata to memory piece by piece:
mstore(
pointer,
0xa9059cbb00000000000000000000000000000000000000000000000000000000
) // Begin with the function selector.
mstore(
add(pointer, 4),
and(t, 0xffffffffffffffffffffffffffffffffffffffff)
) // Mask and append the "to" argument.
mstore(add(pointer, 36), a) // Finally append the "amount" argument. No mask as it's a full 32 byte value.
// Call the token and store if it succeeded or not.
// We use 68 because the calldata length is 4 + 32 * 2.
result := call(gas(), e, 0, pointer, 68, 0, 0)
}
require(success(result), 'transfer failed');
}
/// @param e Erc20 token to execute the call with
/// @param f From address
/// @param t To address
/// @param a Amount being transferred
function transferFrom(
IERC20 e,
address f,
address t,
uint256 a
) internal {
bool result;
assembly {
// Get a pointer to some free memory.
let pointer := mload(0x40)
// Write the abi-encoded calldata to memory piece by piece:
mstore(
pointer,
0x23b872dd00000000000000000000000000000000000000000000000000000000
) // Begin with the function selector.
mstore(
add(pointer, 4),
and(f, 0xffffffffffffffffffffffffffffffffffffffff)
) // Mask and append the "from" argument.
mstore(
add(pointer, 36),
and(t, 0xffffffffffffffffffffffffffffffffffffffff)
) // Mask and append the "to" argument.
mstore(add(pointer, 68), a) // Finally append the "amount" argument. No mask as it's a full 32 byte value.
// Call the token and store if it succeeded or not.
// We use 100 because the calldata length is 4 + 32 * 3.
result := call(gas(), e, 0, pointer, 100, 0, 0)
}
require(success(result), 'transfer from failed');
}
/// @notice normalize the acceptable values of true or null vs the unacceptable value of false (or something malformed)
/// @param r Return value from the assembly `call()` to Erc20['selector']
function success(bool r) private pure returns (bool) {
bool result;
assembly {
// Get how many bytes the call returned.
let returnDataSize := returndatasize()
// If the call reverted:
if iszero(r) {
// Copy the revert message into memory.
returndatacopy(0, 0, returnDataSize)
// Revert with the same message.
revert(0, returnDataSize)
}
switch returnDataSize
case 32 {
// Copy the return data into memory.
returndatacopy(0, 0, returnDataSize)
// Set success to whether it returned true.
result := iszero(iszero(mload(0)))
}
case 0 {
// There was no return data.
result := 1
}
default {
// It returned some malformed input.
result := 0
}
}
return result;
}
function approve(
IERC20 token,
address to,
uint256 amount
) internal {
bool callStatus;
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata to memory piece by piece:
mstore(
freeMemoryPointer,
0x095ea7b300000000000000000000000000000000000000000000000000000000
) // Begin with the function selector.
mstore(
add(freeMemoryPointer, 4),
and(to, 0xffffffffffffffffffffffffffffffffffffffff)
) // Mask and append the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.
// Call the token and store if it succeeded or not.
// We use 68 because the calldata length is 4 + 32 * 2.
callStatus := call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)
}
require(didLastOptionalReturnCallSucceed(callStatus), 'APPROVE_FAILED');
}
/*///////////////////////////////////////////////////////////////
INTERNAL HELPER LOGIC
//////////////////////////////////////////////////////////////*/
function didLastOptionalReturnCallSucceed(bool callStatus)
private
pure
returns (bool)
{
bool result;
assembly {
// Get how many bytes the call returned.
let returnDataSize := returndatasize()
// If the call reverted:
if iszero(callStatus) {
// Copy the revert message into memory.
returndatacopy(0, 0, returnDataSize)
// Revert with the same message.
revert(0, returnDataSize)
}
switch returnDataSize
case 32 {
// Copy the return data into memory.
returndatacopy(0, 0, returnDataSize)
// Set success to whether it returned true.
result := iszero(iszero(mload(0)))
}
case 0 {
// There was no return data.
result := 1
}
default {
// It returned some malformed input.
result := 0
}
}
return result;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
library Swivel {
// the components of a ECDSA signature
struct Components {
uint8 v;
bytes32 r;
bytes32 s;
}
struct Order {
bytes32 key;
uint8 protocol;
address maker;
address underlying;
bool vault;
bool exit;
uint256 principal;
uint256 premium;
uint256 maturity;
uint256 expiry;
}
}// SPDX-License-Identifier: MIT
// Inspired on token.sol from DappHub. Natspec adpated from OpenZeppelin.
pragma solidity 0.8.16;
import 'src/interfaces/IERC20Metadata.sol';
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* We have followed general OpenZeppelin guidelines: functions revert instead
* of returning `false` on failure. This behavior is nonetheless conventional
* and does not conflict with the expectations of ERC20 applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Calls to {transferFrom} do not check for allowance if the caller is the owner
* of the funds. This allows to reduce the number of approvals that are necessary.
*
* Finally, {transferFrom} does not decrease the allowance if it is set to
* type(uint256).max. This reduces the gas costs without any likely impact.
*/
contract ERC20 is IERC20Metadata {
uint256 internal _totalSupply;
mapping(address => uint256) internal _balanceOf;
mapping(address => mapping(address => uint256)) internal _allowance;
string public override name = '???';
string public override symbol = '???';
uint8 public override decimals = 18;
/**
* @dev Sets the values for {name}, {symbol} and {decimals}.
*/
constructor(
string memory name_,
string memory symbol_,
uint8 decimals_
) {
name = name_;
symbol = symbol_;
decimals = decimals_;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() external view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address guy)
external
view
virtual
override
returns (uint256)
{
return _balanceOf[guy];
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender)
external
view
virtual
override
returns (uint256)
{
return _allowance[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*/
function approve(address spender, uint256 wad)
external
virtual
override
returns (bool)
{
return _setAllowance(msg.sender, spender, wad);
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - the caller must have a balance of at least `wad`.
*/
function transfer(address dst, uint256 wad)
external
virtual
override
returns (bool)
{
return _transfer(msg.sender, dst, wad);
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* Requirements:
*
* - `src` must have a balance of at least `wad`.
* - the caller is not `src`, it must have allowance for ``src``'s tokens of at least
* `wad`.
*/
/// if_succeeds {:msg "TransferFrom - decrease allowance"} msg.sender != src ==> old(_allowance[src][msg.sender]) >= wad;
function transferFrom(
address src,
address dst,
uint256 wad
) external virtual override returns (bool) {
_decreaseAllowance(src, wad);
return _transfer(src, dst, wad);
}
/**
* @dev Moves tokens `wad` from `src` to `dst`.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `src` must have a balance of at least `amount`.
*/
/// if_succeeds {:msg "Transfer - src decrease"} old(_balanceOf[src]) >= _balanceOf[src];
/// if_succeeds {:msg "Transfer - dst increase"} _balanceOf[dst] >= old(_balanceOf[dst]);
/// if_succeeds {:msg "Transfer - supply"} old(_balanceOf[src]) + old(_balanceOf[dst]) == _balanceOf[src] + _balanceOf[dst];
function _transfer(
address src,
address dst,
uint256 wad
) internal virtual returns (bool) {
require(_balanceOf[src] >= wad, 'ERC20: Insufficient balance');
unchecked {
_balanceOf[src] = _balanceOf[src] - wad;
}
_balanceOf[dst] = _balanceOf[dst] + wad;
emit Transfer(src, dst, wad);
return true;
}
/**
* @dev Sets the allowance granted to `spender` by `owner`.
*
* Emits an {Approval} event indicating the updated allowance.
*/
function _setAllowance(
address owner,
address spender,
uint256 wad
) internal virtual returns (bool) {
_allowance[owner][spender] = wad;
emit Approval(owner, spender, wad);
return true;
}
/**
* @dev Decreases the allowance granted to the caller by `src`, unless src == msg.sender or _allowance[src][msg.sender] == MAX
*
* Emits an {Approval} event indicating the updated allowance, if the allowance is updated.
*
* Requirements:
*
* - `spender` must have allowance for the caller of at least
* `wad`, unless src == msg.sender
*/
/// if_succeeds {:msg "Decrease allowance - underflow"} old(_allowance[src][msg.sender]) <= _allowance[src][msg.sender];
function _decreaseAllowance(address src, uint256 wad)
internal
virtual
returns (bool)
{
if (src != msg.sender) {
uint256 allowed = _allowance[src][msg.sender];
if (allowed != type(uint256).max) {
require(allowed >= wad, 'ERC20: Insufficient approval');
unchecked {
_setAllowance(src, msg.sender, allowed - wad);
}
}
}
return true;
}
/** @dev Creates `wad` tokens and assigns them to `dst`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*/
/// if_succeeds {:msg "Mint - balance overflow"} old(_balanceOf[dst]) >= _balanceOf[dst];
/// if_succeeds {:msg "Mint - supply overflow"} old(_totalSupply) >= _totalSupply;
function _mint(address dst, uint256 wad) internal virtual returns (bool) {
_balanceOf[dst] = _balanceOf[dst] + wad;
_totalSupply = _totalSupply + wad;
emit Transfer(address(0), dst, wad);
return true;
}
/**
* @dev Destroys `wad` tokens from `src`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `src` must have at least `wad` tokens.
*/
/// if_succeeds {:msg "Burn - balance underflow"} old(_balanceOf[src]) <= _balanceOf[src];
/// if_succeeds {:msg "Burn - supply underflow"} old(_totalSupply) <= _totalSupply;
function _burn(address src, uint256 wad) internal virtual returns (bool) {
unchecked {
require(_balanceOf[src] >= wad, 'ERC20: Insufficient balance');
_balanceOf[src] = _balanceOf[src] - wad;
_totalSupply = _totalSupply - wad;
emit Transfer(src, address(0), wad);
}
return true;
}
}// SPDX-License-Identifier: MIT
// Adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/53516bc555a454862470e7860a9b5254db4d00f5/contracts/token/ERC20/ERC20Permit.sol
pragma solidity 0.8.16;
import 'src/tokens/ERC20.sol';
import 'src/interfaces/IERC2612.sol';
/**
* @dev Extension of {ERC20} that allows token holders to use their tokens
* without sending any transactions by setting {IERC20-allowance} with a
* signature using the {permit} method, and then spend them via
* {IERC20-transferFrom}.
*
* The {permit} signature mechanism conforms to the {IERC2612} interface.
*/
abstract contract ERC20Permit is ERC20, IERC2612 {
mapping(address => uint256) public override nonces;
bytes32 public immutable PERMIT_TYPEHASH =
keccak256(
'Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)'
);
bytes32 private immutable _DOMAIN_SEPARATOR;
uint256 public immutable deploymentChainId;
constructor(
string memory name_,
string memory symbol_,
uint8 decimals_
) ERC20(name_, symbol_, decimals_) {
deploymentChainId = block.chainid;
_DOMAIN_SEPARATOR = _calculateDomainSeparator(block.chainid);
}
/// @dev Calculate the DOMAIN_SEPARATOR.
function _calculateDomainSeparator(uint256 chainId)
private
view
returns (bytes32)
{
return
keccak256(
abi.encode(
keccak256(
'EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'
),
keccak256(bytes(name)),
keccak256(bytes(version())),
chainId,
address(this)
)
);
}
/// @dev Return the DOMAIN_SEPARATOR.
function DOMAIN_SEPARATOR() external view returns (bytes32) {
return
block.chainid == deploymentChainId
? _DOMAIN_SEPARATOR
: _calculateDomainSeparator(block.chainid);
}
/// @dev Setting the version as a function so that it can be overriden
function version() public pure virtual returns (string memory) {
return '1';
}
/**
* @dev See {IERC2612-permit}.
*
* In cases where the free option is not a concern, deadline can simply be
* set to uint(-1), so it should be seen as an optional parameter
*/
function permit(
address owner,
address spender,
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external virtual override {
require(deadline >= block.timestamp, 'ERC20Permit: expired deadline');
bytes32 hashStruct = keccak256(
abi.encode(
PERMIT_TYPEHASH,
owner,
spender,
amount,
nonces[owner]++,
deadline
)
);
bytes32 hash = keccak256(
abi.encodePacked(
'\x19\x01',
block.chainid == deploymentChainId
? _DOMAIN_SEPARATOR
: _calculateDomainSeparator(block.chainid),
hashStruct
)
);
address signer = ecrecover(hash, v, r, s);
require(
signer != address(0) && signer == owner,
'ERC20Permit: invalid signature'
);
_setAllowance(owner, spender, amount);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import 'src/tokens/ERC20Permit.sol';
import 'src/interfaces/IERC5095.sol';
import 'src/interfaces/IRedeemer.sol';
import 'src/interfaces/IMarketPlace.sol';
import 'src/interfaces/IYield.sol';
import 'src/errors/Exception.sol';
import 'src/lib/Cast.sol';
import 'src/lib/Safe.sol';
contract ERC5095 is ERC20Permit, IERC5095 {
/// @dev unix timestamp when the ERC5095 token can be redeemed
uint256 public immutable override maturity;
/// @dev address of the ERC20 token that is returned on ERC5095 redemption
address public immutable override underlying;
/// @dev address of the minting authority
address public immutable lender;
/// @dev address of the "marketplace" YieldSpace AMM router
address public immutable marketplace;
///@dev Interface to interact with the pool
address public pool;
/// @dev address and interface for an external custody contract (necessary for some project's backwards compatability)
address public immutable redeemer;
/// @notice ensures that only a certain address can call the function
/// @param a address that msg.sender must be to be authorized
modifier authorized(address a) {
if (msg.sender != a) {
revert Exception(0, 0, 0, msg.sender, a);
}
_;
}
constructor(
address _underlying,
uint256 _maturity,
address _redeemer,
address _lender,
address _marketplace,
string memory name_,
string memory symbol_,
uint8 decimals_
) ERC20Permit(name_, symbol_, decimals_) {
underlying = _underlying;
maturity = _maturity;
redeemer = _redeemer;
lender = _lender;
marketplace = _marketplace;
pool = address(0);
}
/// @notice Allows the marketplace to set the pool
/// @param p Address of the pool
/// @return bool True if successful
function setPool(address p)
external
authorized(marketplace)
returns (bool)
{
pool = p;
return true;
}
/// @notice Allows the marketplace to spend underlying, principal tokens held by the token
/// @dev This is necessary when MarketPlace calls pool methods to swap tokens
/// @return True if successful
function approveMarketPlace()
external
authorized(marketplace)
returns (bool)
{
// Approve the marketplace to spend the token's underlying
Safe.approve(IERC20(underlying), marketplace, type(uint256).max);
// Approve the marketplace to spend illuminate PTs
Safe.approve(IERC20(address(this)), marketplace, type(uint256).max);
return true;
}
/// @notice Post or at maturity, converts an amount of principal tokens to an amount of underlying that would be returned.
/// @param s The amount of principal tokens to convert
/// @return uint256 The amount of underlying tokens returned by the conversion
function convertToUnderlying(uint256 s)
external
view
override
returns (uint256)
{
if (block.timestamp < maturity) {
return previewRedeem(s);
}
return s;
}
/// @notice Post or at maturity, converts a desired amount of underlying tokens returned to principal tokens needed.
/// @param a The amount of underlying tokens to convert
/// @return uint256 The amount of principal tokens returned by the conversion
function convertToShares(uint256 a)
external
view
override
returns (uint256)
{
if (block.timestamp < maturity) {
return previewWithdraw(a);
}
return a;
}
/// @notice Returns user's PT balance
/// @param o The address of the owner for which redemption is calculated
/// @return uint256 The maximum amount of principal tokens that `owner` can redeem.
function maxRedeem(address o) external view override returns (uint256) {
return _balanceOf[o];
}
/// @notice Post or at maturity, returns user's PT balance. Prior to maturity, returns a previewRedeem for owner's PT balance.
/// @param o The address of the owner for which withdrawal is calculated
/// @return uint256 maximum amount of underlying tokens that `owner` can withdraw.
function maxWithdraw(address o) external view override returns (uint256) {
if (block.timestamp < maturity) {
return previewRedeem(_balanceOf[o]);
}
return _balanceOf[o];
}
/// @notice After maturity, returns 0. Prior to maturity, returns the amount of `shares` when spending `a` in underlying on a YieldSpace AMM.
/// @param a The amount of underlying spent
/// @return uint256 The amount of PT purchased by spending `a` of underlying
function previewDeposit(uint256 a) public view returns (uint256) {
if (block.timestamp < maturity) {
return IYield(pool).sellBasePreview(Cast.u128(a));
}
return 0;
}
/// @notice After maturity, returns 0. Prior to maturity, returns the amount of `assets` in underlying spent on a purchase of `s` in PT on a YieldSpace AMM.
/// @param s The amount of principal tokens bought in the simulation
/// @return uint256 The amount of underlying required to purchase `s` of PT
function previewMint(uint256 s) public view returns (uint256) {
if (block.timestamp < maturity) {
return IYield(pool).buyFYTokenPreview(Cast.u128(s));
}
return 0;
}
/// @notice Post or at maturity, simulates the effects of redemption. Prior to maturity, returns the amount of `assets` from a sale of `s` PTs on a YieldSpace AMM.
/// @param s The amount of principal tokens redeemed in the simulation
/// @return uint256 The amount of underlying returned by `s` of PT redemption
function previewRedeem(uint256 s) public view override returns (uint256) {
if (block.timestamp >= maturity) {
// After maturity, the amount redeemed is based on the Redeemer contract's holdings of the underlying
return
Cast.u128(
s *
Cast.u128(
IRedeemer(redeemer).holdings(underlying, maturity)
)
) / _totalSupply;
}
// Prior to maturity, return a a preview of a swap on the pool
return IYield(pool).sellFYTokenPreview(Cast.u128(s));
}
/// @notice Post or at maturity, simulates the effects of withdrawal at the current block. Prior to maturity, simulates the amount of PTs necessary to receive `a` in underlying from the sale of PTs on a YieldSpace AMM.
/// @param a The amount of underlying tokens withdrawn in the simulation
/// @return uint256 The amount of principal tokens required for the withdrawal of `a`
function previewWithdraw(uint256 a) public view override returns (uint256) {
if (block.timestamp >= maturity) {
// After maturity, the amount redeemed is based on the Redeemer contract's holdings of the underlying
return
(a * _totalSupply) /
IRedeemer(redeemer).holdings(underlying, maturity);
}
// Prior to maturity, return a a preview of a swap on the pool
return IYield(pool).buyBasePreview(Cast.u128(a));
}
/// @notice Before maturity spends `a` of underlying, and sends PTs to `r`. Post or at maturity, reverts.
/// @param a The amount of underlying tokens deposited
/// @param r The receiver of the principal tokens
/// @param m Minimum number of shares that the user will receive
/// @return uint256 The amount of principal tokens purchased
function deposit(
uint256 a,
address r,
uint256 m
) external returns (uint256) {
// Execute the deposit
return _deposit(r, a, m);
}
/// @notice Before maturity spends `assets` of underlying, and sends `shares` of PTs to `receiver`. Post or at maturity, reverts.
/// @param a The amount of underlying tokens deposited
/// @param r The receiver of the principal tokens
/// @return uint256 The amount of principal tokens burnt by the withdrawal
function deposit(uint256 a, address r) external override returns (uint256) {
// Execute the deposit
return _deposit(r, a, 0);
}
/// @notice Before maturity mints `s` of PTs to `r` by spending underlying. Post or at maturity, reverts.
/// @param s The amount of shares being minted
/// @param r The receiver of the underlying tokens being withdrawn
/// @param m Maximum amount of underlying that the user will spend
/// @return uint256 The amount of principal tokens purchased
function mint(
uint256 s,
address r,
uint256 m
) external returns (uint256) {
// Execute the mint
return _mint(r, s, m);
}
/// @notice Before maturity mints `shares` of PTs to `receiver` by spending underlying. Post or at maturity, reverts.
/// @param s The amount of shares being minted
/// @param r The receiver of the underlying tokens being withdrawn
/// @return uint256 The amount of principal tokens purchased
function mint(uint256 s, address r) external override returns (uint256) {
// Execute the mint
return _mint(r, s, type(uint128).max);
}
/// @notice At or after maturity, burns PTs from owner and sends `a` underlying to `r`. Before maturity, sends `a` by selling shares of PT on a YieldSpace AMM.
/// @param a The amount of underlying tokens withdrawn
/// @param r The receiver of the underlying tokens being withdrawn
/// @param o The owner of the underlying tokens
/// @param m Maximum amount of PTs to be sold
/// @return uint256 The amount of principal tokens burnt by the withdrawal
function withdraw(
uint256 a,
address r,
address o,
uint256 m
) external returns (uint256) {
// Execute the withdrawal
return _withdraw(a, r, o, m);
}
/// @notice At or after maturity, burns PTs from owner and sends `a` underlying to `r`. Before maturity, sends `a` by selling shares of PT on a YieldSpace AMM.
/// @param a The amount of underlying tokens withdrawn
/// @param r The receiver of the underlying tokens being withdrawn
/// @param o The owner of the underlying tokens
/// @return uint256 The amount of principal tokens burnt by the withdrawal
function withdraw(
uint256 a,
address r,
address o
) external override returns (uint256) {
// Execute the withdrawal
return _withdraw(a, r, o, type(uint128).max);
}
/// @notice At or after maturity, burns exactly `s` of Principal Tokens from `o` and sends underlying tokens to `r`. Before maturity, sends underlying by selling `s` of PT on a YieldSpace AMM.
/// @param s The number of shares to be burned in exchange for the underlying asset
/// @param r The receiver of the underlying tokens being withdrawn
/// @param o Address of the owner of the shares being burned
/// @param m Minimum amount of underlying that must be received
/// @return uint256 The amount of underlying tokens distributed by the redemption
function redeem(
uint256 s,
address r,
address o,
uint256 m
) external returns (uint256) {
// Execute the redemption
return _redeem(s, r, o, m);
}
/// @notice At or after maturity, burns exactly `shares` of Principal Tokens from `owner` and sends `assets` of underlying tokens to `receiver`. Before maturity, sells `s` of PT on a YieldSpace AMM.
/// @param s The number of shares to be burned in exchange for the underlying asset
/// @param r The receiver of the underlying tokens being withdrawn
/// @param o Address of the owner of the shares being burned
/// @return uint256 The amount of underlying tokens distributed by the redemption
function redeem(
uint256 s,
address r,
address o
) external override returns (uint256) {
// Execute the redemption
return _redeem(s, r, o, 0);
}
/// @param f Address to burn from
/// @param a Amount to burn
/// @return bool true if successful
function authBurn(address f, uint256 a)
external
authorized(redeemer)
returns (bool)
{
_burn(f, a);
return true;
}
/// @param t Address recieving the minted amount
/// @param a The amount to mint
/// @return bool True if successful
function authMint(address t, uint256 a)
external
authorized(lender)
returns (bool)
{
_mint(t, a);
return true;
}
/// @param o Address of the owner of the tokens
/// @param s Address of the spender
/// @param a Amount to be approved
function authApprove(
address o,
address s,
uint256 a
) external authorized(redeemer) returns (bool) {
_allowance[o][s] = a;
return true;
}
function _deposit(
address r,
uint256 a,
uint256 m
) internal returns (uint256) {
// Revert if called at or after maturity
if (block.timestamp >= maturity) {
revert Exception(
21,
block.timestamp,
maturity,
address(0),
address(0)
);
}
// Receive the funds from the sender
Safe.transferFrom(IERC20(underlying), msg.sender, address(this), a);
// Sell the underlying assets for PTs
uint128 returned = IMarketPlace(marketplace).sellUnderlying(
underlying,
maturity,
Cast.u128(a),
Cast.u128(m)
);
// Pass the received shares onto the intended receiver
_transfer(address(this), r, returned);
return returned;
}
function _mint(
address r,
uint256 s,
uint256 m
) internal returns (uint256) {
// Revert if called at or after maturity
if (block.timestamp >= maturity) {
revert Exception(
21,
block.timestamp,
maturity,
address(0),
address(0)
);
}
// Determine how many underlying tokens are needed to mint the shares
uint256 required = IYield(pool).buyFYTokenPreview(Cast.u128(s));
// Transfer the underlying to the token
Safe.transferFrom(
IERC20(underlying),
msg.sender,
address(this),
required
);
// Swap the underlying for principal tokens via the pool
uint128 sold = IMarketPlace(marketplace).buyPrincipalToken(
underlying,
maturity,
Cast.u128(s),
Cast.u128(m)
);
// Transfer the principal tokens to the desired receiver
_transfer(address(this), r, s);
return sold;
}
function _withdraw(
uint256 a,
address r,
address o,
uint256 m
) internal returns (uint256) {
// Determine how many principal tokens are needed to purchase the underlying
uint256 needed = previewWithdraw(a);
// Pre maturity
if (block.timestamp < maturity) {
// Receive the shares from the caller
_transfer(o, address(this), needed);
// If owner is the sender, sell PT without allowance check
if (o == msg.sender) {
uint128 returned = IMarketPlace(marketplace).buyUnderlying(
underlying,
maturity,
Cast.u128(a),
Cast.u128(m)
);
// Transfer the underlying to the desired receiver
Safe.transfer(IERC20(underlying), r, a);
return returned;
} else {
// Else, sell PT with allowance check
// Get the allowance of the user spending the tokens
uint256 allowance = _allowance[o][msg.sender];
// Check for sufficient allowance
if (allowance < needed) {
revert Exception(20, allowance, a, address(0), address(0));
}
// Update the caller's allowance
_allowance[o][msg.sender] = allowance - needed;
// Sell the principal tokens for underlying
uint128 returned = IMarketPlace(marketplace).buyUnderlying(
underlying,
maturity,
Cast.u128(a),
Cast.u128(m)
);
// Transfer the underlying to the desired receiver
Safe.transfer(IERC20(underlying), r, returned);
return returned;
}
}
// Post maturity
else {
// If owner is the sender, redeem PT without allowance check
if (o == msg.sender) {
// Execute the redemption to the desired receiver
return
IRedeemer(redeemer).authRedeem(
underlying,
maturity,
msg.sender,
r,
needed
);
} else {
// Get the allowance of the user spending the tokens
uint256 allowance = _allowance[o][msg.sender];
// Check for sufficient allowance
if (allowance < needed) {
revert Exception(
20,
allowance,
needed,
address(0),
address(0)
);
}
// Update the callers's allowance
_allowance[o][msg.sender] = allowance - needed;
// Execute the redemption to the desired receiver
return
IRedeemer(redeemer).authRedeem(
underlying,
maturity,
o,
r,
needed
);
}
}
}
function _redeem(
uint256 s,
address r,
address o,
uint256 m
) internal returns (uint256) {
// Pre-maturity
if (block.timestamp < maturity) {
// Receive the funds from the user
_transfer(o, address(this), s);
// If owner is the sender, sell PT without allowance check
if (o == msg.sender) {
// Swap principal tokens for the underlying asset
uint128 returned = IMarketPlace(marketplace).sellPrincipalToken(
underlying,
maturity,
Cast.u128(s),
Cast.u128(m)
);
// Transfer underlying to the desired receiver
Safe.transfer(IERC20(underlying), r, returned);
return returned;
// Else, sell PT with allowance check
} else {
// Get the allowance of the user spending the tokens
uint256 allowance = _allowance[o][msg.sender];
// Check for sufficient allowance
if (allowance < s) {
revert Exception(20, allowance, s, address(0), address(0));
}
// Update the caller's allowance
_allowance[o][msg.sender] = allowance - s;
// Sell the principal tokens for the underlying
uint128 returned = IMarketPlace(marketplace).sellPrincipalToken(
underlying,
maturity,
Cast.u128(s),
Cast.u128(m)
);
// Transfer the underlying to the desired receiver
Safe.transfer(IERC20(underlying), r, returned);
return returned;
}
// Post-maturity
} else {
// If owner is the sender, redeem PT without allowance check
if (o == msg.sender) {
// Execute the redemption to the desired receiver
return
IRedeemer(redeemer).authRedeem(
underlying,
maturity,
msg.sender,
r,
s
);
} else {
// Get the allowance of the user spending the tokens
uint256 allowance = _allowance[o][msg.sender];
// Check for sufficient allowance
if (allowance < s) {
revert Exception(20, allowance, s, address(0), address(0));
}
// Update the caller's allowance
_allowance[o][msg.sender] = allowance - s;
// Execute the redemption to the desired receiver
return
IRedeemer(redeemer).authRedeem(
underlying,
maturity,
o,
r,
s
);
}
}
}
}{
"remappings": [
"ds-test/=lib/forge-std/lib/ds-test/src/",
"forge-std/=lib/forge-std/src/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"bytecodeHash": "ipfs"
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "london",
"viaIR": true,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"l","type":"address"},{"internalType":"address","name":"s","type":"address"},{"internalType":"address","name":"t","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint8","name":"","type":"uint8"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"Exception","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"underlying","type":"address"},{"indexed":false,"internalType":"uint256","name":"maturity","type":"uint256"},{"indexed":false,"internalType":"bool","name":"state","type":"bool"}],"name":"PauseRedemptions","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"principal","type":"uint8"},{"indexed":true,"internalType":"address","name":"underlying","type":"address"},{"indexed":true,"internalType":"uint256","name":"maturity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"burned","type":"uint256"},{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"Redeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"when","type":"uint256"}],"name":"ScheduleFeeChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"}],"name":"SetAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"converter","type":"address"}],"name":"SetConverter","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"SetFee","type":"event"},{"inputs":[],"name":"HOLD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_FEENOMINATOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"i","type":"address"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"u","type":"address"},{"internalType":"uint256","name":"m","type":"uint256"},{"internalType":"address","name":"f","type":"address"},{"internalType":"address","name":"t","type":"address"},{"internalType":"uint256","name":"a","type":"uint256"}],"name":"authRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"u","type":"address"},{"internalType":"uint256","name":"m","type":"uint256"},{"internalType":"address[]","name":"f","type":"address[]"}],"name":"autoRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"converter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"u","type":"address"},{"internalType":"uint256","name":"m","type":"uint256"},{"internalType":"uint256","name":"a","type":"uint256"}],"name":"depositHoldings","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeChange","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feenominator","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"holdings","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lender","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"marketPlace","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"u","type":"address"},{"internalType":"uint256","name":"m","type":"uint256"},{"internalType":"bool","name":"b","type":"bool"}],"name":"pauseRedemptions","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"u","type":"address"},{"internalType":"uint256","name":"m","type":"uint256"}],"name":"redeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"p","type":"uint8"},{"internalType":"address","name":"u","type":"address"},{"internalType":"uint256","name":"m","type":"uint256"},{"internalType":"uint8","name":"protocol","type":"uint8"}],"name":"redeem","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"p","type":"uint8"},{"internalType":"address","name":"u","type":"address"},{"internalType":"uint256","name":"m","type":"uint256"},{"internalType":"uint256","name":"s","type":"uint256"},{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"address","name":"periphery","type":"address"}],"name":"redeem","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"p","type":"uint8"},{"internalType":"address","name":"u","type":"address"},{"internalType":"uint256","name":"m","type":"uint256"}],"name":"redeem","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"scheduleFeeChange","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"a","type":"address"}],"name":"setAdmin","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"c","type":"address"},{"internalType":"address[]","name":"i","type":"address[]"}],"name":"setConverter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"f","type":"uint256"}],"name":"setFee","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"l","type":"address"}],"name":"setLender","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"m","type":"address"}],"name":"setMarketPlace","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swivelAddr","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tempusAddr","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60c034620000d157601f62003e2d38819003918201601f19168301916001600160401b03831184841017620000d657808492606094604052833981010312620000d1576200004d81620000ec565b906200006a60406200006260208401620000ec565b9201620000ec565b916101f460065560018060a01b0319903382600054161760005560018060a01b031690600254161760025560805260a052610fa0600455604051613d2b90816200010282396080518181816110760152611d4a015260a05181818161103001526117da0152f35b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b0382168203620000d15756fe60806040526004361015610013575b600080fd5b60003560e01c80630d3f53521461022f5780631177ec30146102265780631e9a69501461021d5780632e25d2a61461021457806330568a8d1461020b57806335197f9e1461020257806346e368d4146101f957806369fe0e2d146101f05780636a97d9ce146101e7578063704b6c02146101de57806370a03ced146101d5578063769065b9146101cc57806380252724146101c357806391b46a91146101ba5780639e6b5173146101b1578063a1b1138c146101a8578063a4ad51151461019f578063ad86b83e14610196578063bcead63e1461018d578063bd38837b14610184578063c01199f41461017b578063d0886f9714610172578063daea85c514610169578063de1d3cb514610160578063ea08c03114610157578063f38961311461014e5763f851a4401461014657600080fd5b61000e6110f6565b5061000e6110a5565b5061000e61105f565b5061000e611019565b5061000e610fb1565b5061000e610f92565b5061000e610f26565b5061000e610efc565b5061000e610ed2565b5061000e610e66565b5061000e610dc2565b5061000e610c99565b5061000e610c7a565b5061000e610c2e565b5061000e610b00565b5061000e6109db565b5061000e6108b2565b5061000e610816565b5061000e610750565b5061000e6105cc565b5061000e61053a565b5061000e61051b565b5061000e610481565b5061000e610457565b5061000e610314565b5061000e610262565b5061000e610243565b600091031261000e57565b503461000e57600036600319011261000e576020600654604051908152f35b503461000e57600036600319011261000e576000546001600160a01b0316338190036102e1577ff339d7864b1b8839e8a8870c012fc6eb9a89844861a87a26ce35979018603a1b60206203f48042018042116102d4575b80600555604051908152a160405160018152602090f35b0390f35b6102dc61121a565b6102b9565b604051636d4c6c8960e01b81529081906102ff903360048401611120565b0390fd5b6001600160a01b0381160361000e57565b503461000e57604036600319011261000e5760043561033281610303565b60243561036e610367826103588560018060a01b03166000526008602052604060002090565b90600052602052604060002090565b5460ff1690565b80156103c7575b61038457610382916125bc565b005b604051636d4c6c8960e01b81526011600482015260248101919091526000604482018190526001600160a01b03929092166064820152608481019190915260a490fd5b506002546000906020906103f1906103e5906001600160a01b031681565b6001600160a01b031690565b60046040518094819363b9b8af0b60e01b83525af190811561044a575b60009161041c575b50610375565b61043d915060203d8111610443575b610435818361072e565b810190611248565b38610416565b503d61042b565b610452611260565b61040e565b503461000e57600036600319011261000e576001546040516001600160a01b039091168152602090f35b503461000e57602036600319011261000e5760043561049f81610303565b6000546001600160a01b03908116338190036102e1575060015416806104e757600180546001600160a01b0319166001600160a01b0384161790555b60405160018152602090f35b60a49060405190636d4c6c8960e01b8252600560048301526000602483015260006044830152606482015260006084820152fd5b503461000e57600036600319011261000e576020600554604051908152f35b503461000e57602036600319011261000e5760043561055881610303565b6000546001600160a01b03908116338190036102e15750600254168061059857600280546001600160a01b0319166001600160a01b0384161790556104db565b60a49060405190636d4c6c8960e01b8252600860048301526000602483015260006044830152606482015260006084820152fd5b503461000e57602036600319011261000e57600054600435906001600160a01b0316338190036102e157506005548061063457604051636d4c6c8960e01b8152601760048201526000602482018190526044820181905260648201819052608482015260a490fd5b80421060001461067357604051636d4c6c8960e01b8152601860048201524260248201526044810191909152600060648201819052608482015260a490fd5b5060065481106106c15761068681600455565b6106906000600555565b604051907e172ddfc5ae88d08b3de01a5a187667c37a5a53989e8c175055cb6c993792a7600083a260018152602090f35b604051636d4c6c8960e01b8152601960048201526000602482018190526044820181905260648201819052608482015260a490fd5b50634e487b7160e01b600052604160045260246000fd5b67ffffffffffffffff811161072157604052565b6107296106f6565b604052565b90601f8019910116810190811067ffffffffffffffff82111761072157604052565b503461000e57604036600319011261000e5760043561076e81610303565b6024359067ffffffffffffffff80831161000e573660238401121561000e578260040135908111610809575b8060051b92604051936020926107b28483018761072e565b85526024838601918301019136831161000e57602401905b8282106107f0576102d06107de878761114e565b60405190151581529081906020820190565b83809183356107fe81610303565b8152019101906107ca565b6108116106f6565b61079a565b503461000e57602036600319011261000e5760043561083481610303565b6000546001600160a01b03808216923384900361089657602093501680916bffffffffffffffffffffffff60a01b1617600055604051907f5a272403b402d892977df56625f4164ccaf70ca3863991c43ecfe76a6905b0a1600083a260018152f35b604051636d4c6c8960e01b8152806102ff863360048401611120565b503461000e5760a036600319011261000e576004356108d081610303565b6024356044356108df81610303565b606435916108ec83610303565b600154610903906103e5906001600160a01b031681565b60405163125cf47f60e01b81526001600160a01b0386166004820152602481018390526000604482018190529091602091839160649183915af19081156109ae575b600091610980575b506001600160a01b03811633036102e1576102d06109706084358686868a6128da565b6040519081529081906020820190565b6109a1915060203d81116109a7575b610999818361072e565b81019061126d565b3861094d565b503d61098f565b6109b6611260565b610945565b6004359060ff8216820361000e57565b6064359060ff8216820361000e57565b503461000e57608036600319011261000e576109f56109bb565b602435610a0181610303565b60443590610a0d6109cb565b92610a31610367846103588560018060a01b03166000526008602052604060002090565b8015610a8c575b610a4b57916102d093916107de93611bb1565b50604051636d4c6c8960e01b81526011600482015260248101929092526000604483018190526001600160a01b03919091166064830152608482015260a490fd5b50600254600090602090610aaa906103e5906001600160a01b031681565b60046040518094819363b9b8af0b60e01b83525af1908115610af3575b600091610ad5575b50610a38565b610aed915060203d811161044357610435818361072e565b38610acf565b610afb611260565b610ac7565b503461000e5760c036600319011261000e57610b1a6109bb565b60243590610b2782610303565b60a43590604435610b3783610303565b610b5a610367826103588760018060a01b03166000526008602052604060002090565b8015610bba575b610b7c57926107de92916102d0946084359260643592611fc5565b604051636d4c6c8960e01b81526011600482015260248101919091526000604482018190526001600160a01b0385166064830152608482015260a490fd5b50600254600090602090610bd8906103e5906001600160a01b031681565b60046040518094819363b9b8af0b60e01b83525af1908115610c21575b600091610c03575b50610b61565b610c1b915060203d811161044357610435818361072e565b38610bfd565b610c29611260565b610bf5565b503461000e57604036600319011261000e57600435610c4c81610303565b60018060a01b0316600052600760205260406000206024356000526020526020604060002054604051908152f35b503461000e57600036600319011261000e576020600454604051908152f35b503461000e57606036600319011261000e57610cb36109bb565b60243590610cc082610303565b60443590610ce7610367836103588660018060a01b03166000526008602052604060002090565b8015610d44575b610d0057906102d0926107de9261129e565b50604051636d4c6c8960e01b81526011600482015260248101919091526000604482018190526001600160a01b03929092166064820152608481019190915260a490fd5b50600254600090602090610d62906103e5906001600160a01b031681565b60046040518094819363b9b8af0b60e01b83525af1908115610dab575b600091610d8d575b50610cee565b610da5915060203d811161044357610435818361072e565b38610d87565b610db3611260565b610d7f565b8015150361000e57565b503461000e57606036600319011261000e57600435610de081610303565b604435602435610def82610db8565b6000805490936001600160a01b03918216338190036102e15750916040917f1258883257f202b4bfb5c92d9effcc9b5e7775dcbb52c2c31f39f2dff27d3c2d93169384865260086020528286208287526020528286209015159060ff1981541660ff831617905582519182526020820152a2604051f35b503461000e57606036600319011261000e57600435610e8481610303565b60443567ffffffffffffffff80821161000e573660238301121561000e57816004013590811161000e573660248260051b8401011161000e576102d092602461097093019060243590612c9d565b503461000e57600036600319011261000e576002546040516001600160a01b039091168152602090f35b503461000e57600036600319011261000e576003546040516001600160a01b039091168152602090f35b503461000e57606036600319011261000e57600435610f4481610303565b604435906001600160a01b0316610f5d82303384613ba8565b6000526007602052604060002060243560005260205260406000208054918201809211610f875755005b610f8f61121a565b55005b503461000e57600036600319011261000e5760206040516203f4808152f35b503461000e57602036600319011261000e57600435610fcf81610303565b6001546001600160a01b039081169133839003610ffd57811680610fef57005b610382916003541690613c5e565b604051636d4c6c8960e01b8152806102ff853360048401611120565b503461000e57600036600319011261000e576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461000e57600036600319011261000e576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461000e57604036600319011261000e576004356110c381610303565b60018060a01b031660005260086020526040600020602435600052602052602060ff604060002054166040519015158152f35b503461000e57600036600319011261000e576000546040516001600160a01b039091168152602090f35b60008082526020820181905260408201526001600160a01b0391821660608201529116608082015260a00190565b6000549092916001600160a01b03918216338190036102e15750818416806bffffffffffffffffffffffff60a01b600354161760035560005b82518082146111d057906001918110156111c3575b6020600582901b850101516111bd90889087166001600160a01b0316613c5e565b01611187565b6111cb611203565b61119c565b505092935050507fc06343c9448e37c4ed257861469b4b35c140991c9cf08a4c38d335a1bc6a75d36000604051a2600190565b50634e487b7160e01b600052603260045260246000fd5b50634e487b7160e01b600052601160045260246000fd5b9190820180921161123e57565b61124661121a565b565b9081602091031261000e575161125d81610db8565b90565b506040513d6000823e3d90fd5b9081602091031261000e575161125d81610303565b9081602091031261000e575190565b9190820391821161123e57565b6001546112b5906103e5906001600160a01b031681565b6040805163125cf47f60e01b81526001600160a01b0385166004808301919091526024820187905260ff8516604483015260209591949193919291868680606481010381600080955af1958615611ba4575b8196611b85575b5060ff84169660028814928315611af057611328886137e3565b428111611ab557506002546001600160a01b031685516370a0823160e01b8082526001600160a01b03838116858401908152929b919282821692908e9088908f9081906020010381875afa9d8e15611aa8575b899e611a85575b508d61139991883091149788611a2f575b86613ba8565b818c169d8e958b519a868c52898c806113c3308d8301919091602081019260018060a01b03169052565b03818b5afa9b8c15611a22575b8b9c611a03575b501561155e57505089516301e9a69560e41b815230818801908152602081018f9052600080516020613cd68339815191529e9d9c6114f29c909b909a61147b9a919990986114629892978897509195509391928691839182908c90829060400103925af18015611551575b611534575b505b8951908152309281019283529485928391829160200190565b03915afa928315611527575b926114fa575b5050611291565b936114c9896103586114ad886114a7846103588860018060a01b03166000526007602052604060002090565b54611231565b6001600160a01b03909416600090815260076020526040902090565b555160ff9091168152602081019290925260408201929092523360608201529081906080820190565b0390a3600190565b6115199250803d10611520575b611511818361072e565b810190611282565b3880611474565b503d611507565b61152f611260565b61146e565b61154a90853d871161152057611511818361072e565b5038611447565b611559611260565b611442565b600382036115ff575050895163884e17f360e01b81528087018e8152306020820152600080516020613cd68339815191529e9d9c6114f29c909b909a61147b9a919990986114629892978897509195509391928691839182908c90829060400103925af180156115f2575b6115d5575b505b611449565b6115eb90853d871161152057611511818361072e565b50386115ce565b6115fa611260565b6115c9565b9293921561179f5750509484879461146294611708600080516020613cd68339815191529f9e9d9b988e8697916114f29f9d9288938f61147b9f9086849281611688845163781c18db60e01b815282818b81875afa908115611792575b8891611775575b50855163bcb7ea5d60e01b815230818c0190815290998a9384928b9291849160200190565b0393165af1958615611768575b8596611749575b50825163afd27bf560e01b815296879182905afa94851561173c575b839561171d575b505163769f8e5d60e01b8152308b820190815260208101949094526001600160a01b03909516604084015260006060840181905260808401529395869485939091849160a00190565b0393165af180156115f2576115d55750611449565b611735919550873d89116109a757610999818361072e565b93386116bf565b611744611260565b6116b8565b611761919650823d841161152057611511818361072e565b943861169c565b611770611260565b611695565b61178c9150833d85116109a757610999818361072e565b38611663565b61179a611260565b61165c565b6005819693949596146000146118d957505050908484928951938480926316f0115b60e01b82525afa9182156118cc575b86926118ad575b507f000000000000000000000000000000000000000000000000000000000000000016803b156118a95793808b9a9997948d889561186e600080516020613cd68339815191529f9a6114629761147b9b6114f29f9d838f51809681958294636c8d4fa160e01b845230918d850191939290606091608084019560018060a01b03809316855260208501526000604085015216910152565b03925af1801561189c575b6118835750611449565b806118906118969261070d565b80610238565b386115ce565b6118a4611260565b611879565b8580fd5b6118c5919250853d87116109a757610999818361072e565b90386117d7565b6118d4611260565b6117d0565b61146294509188956114f29b99600080516020613cd68339815191529f9e9d9b98938f908f908c80998961147b9f9a60071460001461191f575050506115d0935061332e565b91945092506008915014611936575b505050611449565b8c51848152308782019081526119959291908f908590839081906020010381865afa9182156119f6575b86926119d7575b5051635d043b2960e11b81528881019182523060208301819052604083015294859384928391606090910190565b03925af180156119ca575b6119ad575b84889161192e565b6119c390853d871161152057611511818361072e565b50386119a5565b6119d2611260565b6119a0565b6119ef919250853d871161152057611511818361072e565b9038611967565b6119fe611260565b611960565b611a1b919c508a3d8c1161152057611511818361072e565b9a386113d7565b611a2a611260565b6113d0565b8d5163781c18db60e01b81529091508a818b818a5afa908115611a78575b8c91611a5b575b5090611393565b611a7291508b3d8d116109a757610999818361072e565b38611a54565b611a80611260565b611a4d565b611399919e50611aa190893d8b1161152057611511818361072e565b9d90611382565b611ab0611260565b61137b565b8551636d4c6c8960e01b81526007928101928352602083019190915260006040830181905260608301819052608083015290819060a0010390fd5b60038903611b0657611b018861383e565b611328565b888103611b1657611b0188613873565b60058903611b2757611b01886138a8565b60078903611b3857611b0188613958565b60088903611b4957611b0188613aa1565b8451636d4c6c8960e01b8152600691810191825260ff8716602083015260006040830181905260608301819052608083015290819060a0010390fd5b611b9d919650873d89116109a757610999818361072e565b943861130e565b611bac611260565b611307565b939293600160ff821603611f8c57600154611bd6906103e5906001600160a01b031681565b6040805163125cf47f60e01b81526001600160a01b0385166004808301919091526024820187905260ff8516604483015290979093926020808a80606481010381600080965af1998a15611f7f575b829a611f60575b50835163204f83f960e01b81526001600160a01b039a9096908b1682888381845afa978815611f53575b8498611f34575b50428811611efb5760025486516370a0823160e01b8082526001600160a01b039092168482018181529d9e9c9d9b9c919b8d9b9394929391929087908e9081900360200181845afa9c8d15611eee575b889d611ecb575b508c611cc291843091613ba8565b828b169c8d988d888c51809c8982528180611cef8d30908301919091602081019260018060a01b03169052565b03915afa9a8b15611ebe575b8a9b611e95575b508b51632c5a8cf360e21b815260ff9092168883019081526001600160a01b039093166020840152604083019390935260608201929092528690829081906080015b03818a877f0000000000000000000000000000000000000000000000000000000000000000165af1908115611e88575b8791611e6b575b5015611e325716803b15611e2e578361147b948a9997948d8f95611de5600080516020613cd68339815191529f9e9a8098611462986114f29f9d838f51809681958294636b42450d60e01b84528c840160209093929193604081019460018060a01b031681520152565b03925af18015611e21575b611e0e57508951908152309281019283529485928391829160200190565b80611890611e1b9261070d565b38611447565b611e29611260565b611df0565b8480fd5b8751636d4c6c8960e01b8152600f81860190815260006020820181905260408201819052606082018190526080820152819060a0010390fd5b611e829150863d881161044357610435818361072e565b38611d7b565b611e90611260565b611d74565b899392919b50611eb4611d4491853d871161152057611511818361072e565b9b91929350611d02565b611ec6611260565b611cfb565b611cc2919d50611ee790883d8a1161152057611511818361072e565b9c90611cb4565b611ef6611260565b611cad565b8551636d4c6c8960e01b81526007818401908152602081018a9052600060408201819052606082018190526080820152819060a0010390fd5b611f4c919850833d851161152057611511818361072e565b9638611c5d565b611f5b611260565b611c56565b81611f78929b503d8c116109a757610999818361072e565b9838611c2c565b611f87611260565b611c25565b604051636d4c6c8960e01b81526006600482015260ff91909116602482015260006044820181905260648201819052608482015260a490fd5b90919493611fe06103e56103e560015460018060a01b031690565b6040805163125cf47f60e01b81526001600160a01b038616600480830191909152602482018a90526006604483015260209792949092888380606481010381600080995af1928315612570575b8593612551575b5060018060a01b03809316918a856120918c878d169e8f8c61205d60025460018060a01b031690565b9051636eb1769f60e11b81526001600160a01b03808316978201978852909316602087015294919384928391829160400190565b03915afa908115612544575b8991612527575b50156124ee57928b95928795926121018f9a99968c51908a82806120e4846370a0823160e01b9d8e84528301919091602081019260018060a01b03169052565b03818a5afa9182156124e1575b8c926124c2575b50309087613ba8565b8a5186815230888201908152909e908f9081906020010381875afa9d8e156124b5575b899e612494575b508a5186815230888201908152909a8c92918a918d918290819060200103915afa9a8b15612487575b8a9b61243d575b5088936121ed938b8a858a8996878e839d9a519a8b9182528180612190308a8301919091602081019260018060a01b03169052565b03915afa988915612430575b8599612409575b5090808880938686518094819363378efa3760e01b8352165afa9081156123fc575b86916123df575b508351630f922e0760e31b81528581019788529a9116958a91829160200190565b0381875afa9788156123d2575b83986123a9575b5051632b83cccd60e01b81526001600160a01b0388169181019182526020820194909452604081019590955291938492839182906060015b03925af1801561239c575b61237f575b50858a5180948193636a5c1cc960e11b8352165afa908115612372575b8691612355575b50600354612285906103e5906001600160a01b031681565b8851848152308682019081529093919291879185919082908190602001039185165afa928315612348575b8793612329575b50813b1561232557948a9997948d8895611de5600080516020613cd68339815191529f9e9a6114629786976114f29f9d838f9e61147b9f519687958694859363248391ff60e01b85528d850160409194939294606082019560018060a01b0380921683521660208201520152565b8680fd5b612341919350863d881161152057611511818361072e565b91386122b7565b612350611260565b6122b0565b61236c9150853d87116109a757610999818361072e565b3861226d565b61237a611260565b612266565b61239590833d851161152057611511818361072e565b5038612249565b6123a4611260565b612244565b61223998506123c89087949392943d89116109a757610999818361072e565b9792909192612201565b6123da611260565b6121fa565b6123f69150833d85116109a757610999818361072e565b386121cc565b612404611260565b6121c5565b8291995091886124268194823d841161152057611511818361072e565b9a929350506121a3565b612438611260565b61219c565b899b509387938b8a899895898e99968a97883d8a11612480575b612461818361072e565b810161246c91611282565b9f509650509598505050935093989a61215b565b503d612457565b61248f611260565b612154565b8b919e506124ae90893d8b1161152057611511818361072e565b9d9061212b565b6124bd611260565b612124565b6124da9192508b3d8d1161152057611511818361072e565b90386120f8565b6124e9611260565b6120f1565b8851636d4c6c8960e01b8152601d81890190815260006020820181905260408201819052606082018190526080820152819060a0010390fd5b61253e91508d803d1061152057611511818361072e565b386120a4565b61254c611260565b61209d565b612569919350893d8b116109a757610999818361072e565b9138612034565b612578611260565b61202d565b8060001904821181151516612590570290565b61259861121a565b0290565b81156125a6570490565b634e487b7160e01b600052601260045260246000fd5b6001546125d3906103e5906001600160a01b031681565b6040805163125cf47f60e01b81526001600160a01b038416600480830191909152602482018690526000604483015291939190602090818180606481010381600080995af19081156128cd575b85916128b0575b50855163204f83f960e01b81526001600160a01b03918216949083818681895afa9081156128a3575b8791612886575b50421061284e5786516370a0823160e01b8152338582019081529094908490869081906020010381895afa948515612841575b8795612820575b5083906126ea6126c16126ba8c6103588760018060a01b03166000526007602052604060002090565b548861257d565b8a516318160ddd60e01b815290848285818d5afa918215612813575b8b926127f4575b5061259c565b96878b6127098660018060a01b03166000526007602052604060002090565b600091825260205260409020549061272091611291565b6001600160a01b03851660009081526007602052604090208c906000918252602052604090205589516388ba33df60e01b81523392810192835260208301889052988992839190829060400103925af1600080516020613cd6833981519152966127c4949180156127e7575b6127c9575b505016946127a0843388613b37565b51600081526020810193909352604083019190915233606083015281906080820190565b0390a3565b816127df92903d1061044357610435818361072e565b503880612791565b6127ef611260565b61278c565b61280c919250853d871161152057611511818361072e565b90386126e4565b61281b611260565b6126dd565b8491955061283a90823d841161152057611511818361072e565b9490612691565b612849611260565b61268a565b8651636d4c6c8960e01b81526007818601908152426020820152604081018a90526000606082018190526080820152819060a0010390fd5b61289d9150843d861161152057611511818361072e565b38612657565b6128ab611260565b612650565b6128c79150823d84116109a757610999818361072e565b38612627565b6128d5611260565b612620565b6001600160a01b03811660009081526008602052604090209095949392919081600052602052612910604060002060ff90541690565b8015612963575b6129255761125d94956129d7565b604051636d4c6c8960e01b81526011600482015260248101919091526000604482018190526001600160a01b0387166064830152608482015260a490fd5b50600254600090602090612981906103e5906001600160a01b031681565b60046040518094819363b9b8af0b60e01b83525af19081156129ca575b6000916129ac575b50612917565b6129c4915060203d811161044357610435818361072e565b386129a6565b6129d2611260565b61299e565b600154919392916129f2906103e5906001600160a01b031681565b6040805163125cf47f60e01b81526001600160a01b0384166004820152602481018790526000604482018190529193909290916020918291859160649183915af1928315612c90575b600093612c71575b50835163204f83f960e01b81526001600160a01b03938416908281600481855afa908115612c64575b600091612c47575b50804210612c125750600080516020613cd683398151915295938383612b7e612b98958d8d96612af6612acc612ac58a612bbc9f9d6103589060018060a01b03166000526007602052604060002090565b548461257d565b8b516318160ddd60e01b8152908582600481875afa918215612c05575b600092612bed575061259c565b9a88612b46612b268e612b20819d6103588d60018060a01b03166000526007602052604060002090565b54611291565b6001600160a01b038a166000908152600760205260409020909290610358565b558a516388ba33df60e01b81526001600160a01b03909116600482015260248101929092529092839190829060009082906044820190565b03925af18015612be0575b612bc2575b5050169687613b37565b51600081526020810191909152604081018690523360608201529081906080820190565b0390a390565b81612bd892903d1061044357610435818361072e565b503880612b8e565b612be8611260565b612b89565b61280c919250863d881161152057611511818361072e565b612c0d611260565b612ae9565b8551636d4c6c8960e01b815260076004820152602481019190915260006044820181905260648201819052608482015260a490fd5b612c5e9150833d851161152057611511818361072e565b38612a74565b612c6c611260565b612a6c565b81612c899294503d85116109a757610999818361072e565b9138612a43565b612c98611260565b612a3b565b6001600160a01b038116600090815260086020526040902090949392919081600052602052612cd2604060002060ff90541690565b8015612d25575b612ce75761125d9394612dc1565b604051636d4c6c8960e01b81526011600482015260248101919091526000604482018190526001600160a01b0386166064830152608482015260a490fd5b50600254600090602090612d43906103e5906001600160a01b031681565b60046040518094819363b9b8af0b60e01b83525af1908115612d8c575b600091612d6e575b50612cd9565b612d86915060203d811161044357610435818361072e565b38612d68565b612d94611260565b612d60565b9190811015612daa575b60051b0190565b612db2611203565b612da3565b3561125d81610303565b600154909290612ddb906103e5906001600160a01b031681565b6040805163125cf47f60e01b81526001600160a01b0386166004820152602481018590526000604482015291959193909290602085806064810103816000809b5af1948515613321575b8795613300575b50835163204f83f960e01b808252906020816004816001600160a01b038b165afa9081156132f3575b89916132d4575b50421061323f5750869493929190855b838703612e92575061125d968896503395506001600160a01b03169350613b3792505050565b909192939495612ee68199848a6020612eb4612eaf878c8c612d99565b612db7565b8b51636eb1769f60e11b81526001600160a01b0390911660048201523060248201529485908d90829081906044820190565b03916001600160a01b03165afa938415613232575b8694613204575b506020612f18612eaf612f449596978c8c612d99565b8b516370a0823160e01b81526001600160a01b0390911660048201529384908d90829081906024820190565b03916001600160a01b03165afa9283156131f7575b86936131c1575b506001600160a01b03166000908152600760205260409020612fbd91600491612f8c91612ac591610358565b60208c8c51938480926318160ddd60e01b825260018060a01b03165afa9182156131b4575b879261319a575061259c565b90612fca6004548361259c565b9381811061316457508392916130f26001956130ec8f8f908f918f8f8f612eaf958f93613067612eaf886131009f96866130476020809a8c613013612eaf888861309f9f612d99565b8a516388ba33df60e01b81526001600160a01b03909116600482015260248101929092529092839190829081906044820190565b03916001600160a01b038d165af18015613157575b61313a575b50612d99565b90516326ce41a160e21b81526001600160a01b0390911660048201523060248201526000604482015293849283919082906064820190565b03926001600160a01b03165af1801561312d575b61310e575b506130e48d6103586114ad8a612b20846103588860018060a01b03166000526007602052604060002090565b558d8d612d99565b92611291565b90858060a01b038d16613b37565b019801959493929190612e6c565b6131269060203d60201161044357610435818361072e565b50386130b8565b613135611260565b6130b3565b613150908a3d8c1161044357610435818361072e565b5038613061565b61315f611260565b61305c565b8951636d4c6c8960e01b81526014600482015260248101919091526044810191909152600060648201819052608482015260a490fd5b61280c91925060203d60201161152057611511818361072e565b6131bc611260565b612fb1565b6004919350612ac5612fbd936103586131eb612f8c9460203d60201161152057611511818361072e565b96945050935050612f60565b6131ff611260565b612f59565b612f44939450612f18612eaf613228602093843d861161152057611511818361072e565b9695505050612f02565b61323a611260565b612efb565b84519081526102ff90889086906020816004816001600160a01b038d165afa9283156132c7575b926132a6575b5051636d4c6c8960e01b815260076004820152602481019190915260006044820181905260648201819052608482015290819060a4820190565b6132c091925060203d60201161152057611511818361072e565b908361326c565b6132cf611260565b613266565b6132ed915060203d60201161152057611511818361072e565b38612e5c565b6132fb611260565b612e55565b61331a91955060203d6020116109a757610999818361072e565b9338612e2c565b613329611260565b612e25565b60405163247c579160e21b815292600492916020908590859082906001600160a01b03165afa9384156137d6575b6000946137b5575b506040516362b9c05160e11b815260208185816001600160a01b0389165afa9081156137a8575b600091613789575b50604051635a7ad10d60e11b81526133e790602090818188816001600160a01b038c165afa90811561377c575b60009161375f575b50604051809381926371a5d76160e01b83528983019190602083019252565b03816001600160a01b038a165afa908115613752575b600091613733575b506002546001600160a01b03166040516370a0823160e01b81526001600160a01b03821687820190815290949060209086908190830103816001600160a01b0387165afa948515613726575b600095613705575b508085116136fd575b506001600160a01b0316803b1561000e5760408051633ad4d8b960e11b81526001600160a01b0390931687840190815260208101869052600092849290918391859183910103925af180156136f0575b6136dd575b506001600160a01b0316803b1561000e576040805163f3fef3a360e01b81526001600160a01b038716868201908152602081019490945292600092849290918391859183910103925af180156136d0575b6136bd575b5060405163f018ae6960e01b808252909360208585816001600160a01b0385165afa9485156136b0575b60009561368f575b5060035460209061355a906103e5906001600160a01b031681565b60405193845292918290869082906001600160a01b03165afa908115613682575b600091613663575b506040516370a0823160e01b815230858201908152909560209187919082908190850103916001600160a01b03165afa948515613656575b600095613635575b50813b1561000e576040805163248391ff60e01b81526001600160a01b039283169581019586529390911660208501528301939093529160009183919082908490829060600103925af18015613628575b61361b5750565b806118906112469261070d565b613630611260565b613614565b61364f91955060203d60201161152057611511818361072e565b93386135c3565b61365e611260565b6135bb565b61367c915060203d6020116109a757610999818361072e565b38613583565b61368a611260565b61357b565b6136a991955060203d6020116109a757610999818361072e565b933861353f565b6136b8611260565b613537565b806118906136ca9261070d565b3861350d565b6136d8611260565b613508565b806118906136ea9261070d565b386134b7565b6136f8611260565b6134b2565b935038613462565b61371f91955060203d60201161152057611511818361072e565b9338613459565b61372e611260565b613451565b61374c915060203d6020116109a757610999818361072e565b38613405565b61375a611260565b6133fd565b6137769150823d841161152057611511818361072e565b386133c8565b613784611260565b6133c0565b6137a2915060203d6020116109a757610999818361072e565b38613393565b6137b0611260565b61338b565b6137cf91945060203d6020116109a757610999818361072e565b9238613364565b6137de611260565b61335c565b60405163204f83f960e01b815290602090829060049082906001600160a01b03165afa908115613831575b600091613819575090565b61125d915060203d811161152057611511818361072e565b613839611260565b61380e565b60405163aa082a9d60e01b815290602090829060049082906001600160a01b03165afa90811561383157600091613819575090565b6040516370c264df60e11b815290602090829060049082906001600160a01b03165afa90811561383157600091613819575090565b6040516316f0115b60e01b81526020916001600160a01b0391839182908290600490829087165afa90811561394b575b60009161392e575b50600460405180948193632745fed560e11b8352165afa918215613921575b60009261390b57505090565b61125d9250803d1061152057611511818361072e565b613929611260565b6138ff565b6139459150823d84116109a757610999818361072e565b386138e0565b613953611260565b6138d8565b60405163247c579160e21b81526020916001600160a01b03918391613a0891849184908290600490829086165afa908115613a94575b600091613a77575b50166040516362b9c05160e11b8152838082600481865afa918215613a6a575b600092613a4a575b50604051636558954f60e01b81529192829060049082905afa908115613a3d575b600091613a20575b506040519485809481936220e10960e51b8352600483019190602083019252565b0392165afa9182156139215760009261390b57505090565b613a379150843d861161152057611511818361072e565b386139e7565b613a45611260565b6139df565b60049250613a6490823d84116109a757610999818361072e565b916139be565b613a72611260565b6139b6565b613a8e9150843d86116109a757610999818361072e565b38613996565b613a9c611260565b61398e565b60405163e16695b560e01b815290602090829060049082906001600160a01b03165afa908115613b2a575b600091613adf575b5064ffffffffff1690565b6020813d8211613b22575b81613af76020938361072e565b81010312613b1e57519064ffffffffff82168203613b1b575064ffffffffff613ad4565b80fd5b5080fd5b3d9150613aea565b613b32611260565b613acc565b6044600092838093613b6a966040519363a9059cbb60e01b855260018060a01b0316600485015260248401525af1613c27565b15613b7157565b60405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b6044820152606490fd5b60009283606492613be4968295604051946323b872dd60e01b865260018060a01b03809216600487015216602485015260448401525af1613c27565b15613beb57565b60405162461bcd60e51b81526020600482015260146024820152731d1c985b9cd9995c88199c9bdb4819985a5b195960621b6044820152606490fd5b6000903d9015613c57579081602014613c4a575015613c4557600090565b600190565b9050602081803e51151590565b908181803efd5b60405163095ea7b360e01b81526001600160a01b0390921660048301526000196024830152613c9891600091829160449183905af1613c27565b15613c9f57565b60405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b6044820152606490fdfef9820069613a0f200b0791623bec3ed14a17256cf61c3fc2160fdadbbcdda463a26469706673582212207234f4d02766bbc48118cc207dfdebcc367022e3d70cc9a1355fb4874657444a64736f6c63430008100033000000000000000000000000429b47c4aeadd42bbcb118651c8984086bfc4551000000000000000000000000373a06bd3067f8da90239a47f316f09312b7800f000000000000000000000000db5fd0678eed82246b599da6bc36b56157e4bed8
Deployed Bytecode
0x60806040526004361015610013575b600080fd5b60003560e01c80630d3f53521461022f5780631177ec30146102265780631e9a69501461021d5780632e25d2a61461021457806330568a8d1461020b57806335197f9e1461020257806346e368d4146101f957806369fe0e2d146101f05780636a97d9ce146101e7578063704b6c02146101de57806370a03ced146101d5578063769065b9146101cc57806380252724146101c357806391b46a91146101ba5780639e6b5173146101b1578063a1b1138c146101a8578063a4ad51151461019f578063ad86b83e14610196578063bcead63e1461018d578063bd38837b14610184578063c01199f41461017b578063d0886f9714610172578063daea85c514610169578063de1d3cb514610160578063ea08c03114610157578063f38961311461014e5763f851a4401461014657600080fd5b61000e6110f6565b5061000e6110a5565b5061000e61105f565b5061000e611019565b5061000e610fb1565b5061000e610f92565b5061000e610f26565b5061000e610efc565b5061000e610ed2565b5061000e610e66565b5061000e610dc2565b5061000e610c99565b5061000e610c7a565b5061000e610c2e565b5061000e610b00565b5061000e6109db565b5061000e6108b2565b5061000e610816565b5061000e610750565b5061000e6105cc565b5061000e61053a565b5061000e61051b565b5061000e610481565b5061000e610457565b5061000e610314565b5061000e610262565b5061000e610243565b600091031261000e57565b503461000e57600036600319011261000e576020600654604051908152f35b503461000e57600036600319011261000e576000546001600160a01b0316338190036102e1577ff339d7864b1b8839e8a8870c012fc6eb9a89844861a87a26ce35979018603a1b60206203f48042018042116102d4575b80600555604051908152a160405160018152602090f35b0390f35b6102dc61121a565b6102b9565b604051636d4c6c8960e01b81529081906102ff903360048401611120565b0390fd5b6001600160a01b0381160361000e57565b503461000e57604036600319011261000e5760043561033281610303565b60243561036e610367826103588560018060a01b03166000526008602052604060002090565b90600052602052604060002090565b5460ff1690565b80156103c7575b61038457610382916125bc565b005b604051636d4c6c8960e01b81526011600482015260248101919091526000604482018190526001600160a01b03929092166064820152608481019190915260a490fd5b506002546000906020906103f1906103e5906001600160a01b031681565b6001600160a01b031690565b60046040518094819363b9b8af0b60e01b83525af190811561044a575b60009161041c575b50610375565b61043d915060203d8111610443575b610435818361072e565b810190611248565b38610416565b503d61042b565b610452611260565b61040e565b503461000e57600036600319011261000e576001546040516001600160a01b039091168152602090f35b503461000e57602036600319011261000e5760043561049f81610303565b6000546001600160a01b03908116338190036102e1575060015416806104e757600180546001600160a01b0319166001600160a01b0384161790555b60405160018152602090f35b60a49060405190636d4c6c8960e01b8252600560048301526000602483015260006044830152606482015260006084820152fd5b503461000e57600036600319011261000e576020600554604051908152f35b503461000e57602036600319011261000e5760043561055881610303565b6000546001600160a01b03908116338190036102e15750600254168061059857600280546001600160a01b0319166001600160a01b0384161790556104db565b60a49060405190636d4c6c8960e01b8252600860048301526000602483015260006044830152606482015260006084820152fd5b503461000e57602036600319011261000e57600054600435906001600160a01b0316338190036102e157506005548061063457604051636d4c6c8960e01b8152601760048201526000602482018190526044820181905260648201819052608482015260a490fd5b80421060001461067357604051636d4c6c8960e01b8152601860048201524260248201526044810191909152600060648201819052608482015260a490fd5b5060065481106106c15761068681600455565b6106906000600555565b604051907e172ddfc5ae88d08b3de01a5a187667c37a5a53989e8c175055cb6c993792a7600083a260018152602090f35b604051636d4c6c8960e01b8152601960048201526000602482018190526044820181905260648201819052608482015260a490fd5b50634e487b7160e01b600052604160045260246000fd5b67ffffffffffffffff811161072157604052565b6107296106f6565b604052565b90601f8019910116810190811067ffffffffffffffff82111761072157604052565b503461000e57604036600319011261000e5760043561076e81610303565b6024359067ffffffffffffffff80831161000e573660238401121561000e578260040135908111610809575b8060051b92604051936020926107b28483018761072e565b85526024838601918301019136831161000e57602401905b8282106107f0576102d06107de878761114e565b60405190151581529081906020820190565b83809183356107fe81610303565b8152019101906107ca565b6108116106f6565b61079a565b503461000e57602036600319011261000e5760043561083481610303565b6000546001600160a01b03808216923384900361089657602093501680916bffffffffffffffffffffffff60a01b1617600055604051907f5a272403b402d892977df56625f4164ccaf70ca3863991c43ecfe76a6905b0a1600083a260018152f35b604051636d4c6c8960e01b8152806102ff863360048401611120565b503461000e5760a036600319011261000e576004356108d081610303565b6024356044356108df81610303565b606435916108ec83610303565b600154610903906103e5906001600160a01b031681565b60405163125cf47f60e01b81526001600160a01b0386166004820152602481018390526000604482018190529091602091839160649183915af19081156109ae575b600091610980575b506001600160a01b03811633036102e1576102d06109706084358686868a6128da565b6040519081529081906020820190565b6109a1915060203d81116109a7575b610999818361072e565b81019061126d565b3861094d565b503d61098f565b6109b6611260565b610945565b6004359060ff8216820361000e57565b6064359060ff8216820361000e57565b503461000e57608036600319011261000e576109f56109bb565b602435610a0181610303565b60443590610a0d6109cb565b92610a31610367846103588560018060a01b03166000526008602052604060002090565b8015610a8c575b610a4b57916102d093916107de93611bb1565b50604051636d4c6c8960e01b81526011600482015260248101929092526000604483018190526001600160a01b03919091166064830152608482015260a490fd5b50600254600090602090610aaa906103e5906001600160a01b031681565b60046040518094819363b9b8af0b60e01b83525af1908115610af3575b600091610ad5575b50610a38565b610aed915060203d811161044357610435818361072e565b38610acf565b610afb611260565b610ac7565b503461000e5760c036600319011261000e57610b1a6109bb565b60243590610b2782610303565b60a43590604435610b3783610303565b610b5a610367826103588760018060a01b03166000526008602052604060002090565b8015610bba575b610b7c57926107de92916102d0946084359260643592611fc5565b604051636d4c6c8960e01b81526011600482015260248101919091526000604482018190526001600160a01b0385166064830152608482015260a490fd5b50600254600090602090610bd8906103e5906001600160a01b031681565b60046040518094819363b9b8af0b60e01b83525af1908115610c21575b600091610c03575b50610b61565b610c1b915060203d811161044357610435818361072e565b38610bfd565b610c29611260565b610bf5565b503461000e57604036600319011261000e57600435610c4c81610303565b60018060a01b0316600052600760205260406000206024356000526020526020604060002054604051908152f35b503461000e57600036600319011261000e576020600454604051908152f35b503461000e57606036600319011261000e57610cb36109bb565b60243590610cc082610303565b60443590610ce7610367836103588660018060a01b03166000526008602052604060002090565b8015610d44575b610d0057906102d0926107de9261129e565b50604051636d4c6c8960e01b81526011600482015260248101919091526000604482018190526001600160a01b03929092166064820152608481019190915260a490fd5b50600254600090602090610d62906103e5906001600160a01b031681565b60046040518094819363b9b8af0b60e01b83525af1908115610dab575b600091610d8d575b50610cee565b610da5915060203d811161044357610435818361072e565b38610d87565b610db3611260565b610d7f565b8015150361000e57565b503461000e57606036600319011261000e57600435610de081610303565b604435602435610def82610db8565b6000805490936001600160a01b03918216338190036102e15750916040917f1258883257f202b4bfb5c92d9effcc9b5e7775dcbb52c2c31f39f2dff27d3c2d93169384865260086020528286208287526020528286209015159060ff1981541660ff831617905582519182526020820152a2604051f35b503461000e57606036600319011261000e57600435610e8481610303565b60443567ffffffffffffffff80821161000e573660238301121561000e57816004013590811161000e573660248260051b8401011161000e576102d092602461097093019060243590612c9d565b503461000e57600036600319011261000e576002546040516001600160a01b039091168152602090f35b503461000e57600036600319011261000e576003546040516001600160a01b039091168152602090f35b503461000e57606036600319011261000e57600435610f4481610303565b604435906001600160a01b0316610f5d82303384613ba8565b6000526007602052604060002060243560005260205260406000208054918201809211610f875755005b610f8f61121a565b55005b503461000e57600036600319011261000e5760206040516203f4808152f35b503461000e57602036600319011261000e57600435610fcf81610303565b6001546001600160a01b039081169133839003610ffd57811680610fef57005b610382916003541690613c5e565b604051636d4c6c8960e01b8152806102ff853360048401611120565b503461000e57600036600319011261000e576040517f000000000000000000000000db5fd0678eed82246b599da6bc36b56157e4bed86001600160a01b03168152602090f35b503461000e57600036600319011261000e576040517f000000000000000000000000373a06bd3067f8da90239a47f316f09312b7800f6001600160a01b03168152602090f35b503461000e57604036600319011261000e576004356110c381610303565b60018060a01b031660005260086020526040600020602435600052602052602060ff604060002054166040519015158152f35b503461000e57600036600319011261000e576000546040516001600160a01b039091168152602090f35b60008082526020820181905260408201526001600160a01b0391821660608201529116608082015260a00190565b6000549092916001600160a01b03918216338190036102e15750818416806bffffffffffffffffffffffff60a01b600354161760035560005b82518082146111d057906001918110156111c3575b6020600582901b850101516111bd90889087166001600160a01b0316613c5e565b01611187565b6111cb611203565b61119c565b505092935050507fc06343c9448e37c4ed257861469b4b35c140991c9cf08a4c38d335a1bc6a75d36000604051a2600190565b50634e487b7160e01b600052603260045260246000fd5b50634e487b7160e01b600052601160045260246000fd5b9190820180921161123e57565b61124661121a565b565b9081602091031261000e575161125d81610db8565b90565b506040513d6000823e3d90fd5b9081602091031261000e575161125d81610303565b9081602091031261000e575190565b9190820391821161123e57565b6001546112b5906103e5906001600160a01b031681565b6040805163125cf47f60e01b81526001600160a01b0385166004808301919091526024820187905260ff8516604483015260209591949193919291868680606481010381600080955af1958615611ba4575b8196611b85575b5060ff84169660028814928315611af057611328886137e3565b428111611ab557506002546001600160a01b031685516370a0823160e01b8082526001600160a01b03838116858401908152929b919282821692908e9088908f9081906020010381875afa9d8e15611aa8575b899e611a85575b508d61139991883091149788611a2f575b86613ba8565b818c169d8e958b519a868c52898c806113c3308d8301919091602081019260018060a01b03169052565b03818b5afa9b8c15611a22575b8b9c611a03575b501561155e57505089516301e9a69560e41b815230818801908152602081018f9052600080516020613cd68339815191529e9d9c6114f29c909b909a61147b9a919990986114629892978897509195509391928691839182908c90829060400103925af18015611551575b611534575b505b8951908152309281019283529485928391829160200190565b03915afa928315611527575b926114fa575b5050611291565b936114c9896103586114ad886114a7846103588860018060a01b03166000526007602052604060002090565b54611231565b6001600160a01b03909416600090815260076020526040902090565b555160ff9091168152602081019290925260408201929092523360608201529081906080820190565b0390a3600190565b6115199250803d10611520575b611511818361072e565b810190611282565b3880611474565b503d611507565b61152f611260565b61146e565b61154a90853d871161152057611511818361072e565b5038611447565b611559611260565b611442565b600382036115ff575050895163884e17f360e01b81528087018e8152306020820152600080516020613cd68339815191529e9d9c6114f29c909b909a61147b9a919990986114629892978897509195509391928691839182908c90829060400103925af180156115f2575b6115d5575b505b611449565b6115eb90853d871161152057611511818361072e565b50386115ce565b6115fa611260565b6115c9565b9293921561179f5750509484879461146294611708600080516020613cd68339815191529f9e9d9b988e8697916114f29f9d9288938f61147b9f9086849281611688845163781c18db60e01b815282818b81875afa908115611792575b8891611775575b50855163bcb7ea5d60e01b815230818c0190815290998a9384928b9291849160200190565b0393165af1958615611768575b8596611749575b50825163afd27bf560e01b815296879182905afa94851561173c575b839561171d575b505163769f8e5d60e01b8152308b820190815260208101949094526001600160a01b03909516604084015260006060840181905260808401529395869485939091849160a00190565b0393165af180156115f2576115d55750611449565b611735919550873d89116109a757610999818361072e565b93386116bf565b611744611260565b6116b8565b611761919650823d841161152057611511818361072e565b943861169c565b611770611260565b611695565b61178c9150833d85116109a757610999818361072e565b38611663565b61179a611260565b61165c565b6005819693949596146000146118d957505050908484928951938480926316f0115b60e01b82525afa9182156118cc575b86926118ad575b507f000000000000000000000000db5fd0678eed82246b599da6bc36b56157e4bed816803b156118a95793808b9a9997948d889561186e600080516020613cd68339815191529f9a6114629761147b9b6114f29f9d838f51809681958294636c8d4fa160e01b845230918d850191939290606091608084019560018060a01b03809316855260208501526000604085015216910152565b03925af1801561189c575b6118835750611449565b806118906118969261070d565b80610238565b386115ce565b6118a4611260565b611879565b8580fd5b6118c5919250853d87116109a757610999818361072e565b90386117d7565b6118d4611260565b6117d0565b61146294509188956114f29b99600080516020613cd68339815191529f9e9d9b98938f908f908c80998961147b9f9a60071460001461191f575050506115d0935061332e565b91945092506008915014611936575b505050611449565b8c51848152308782019081526119959291908f908590839081906020010381865afa9182156119f6575b86926119d7575b5051635d043b2960e11b81528881019182523060208301819052604083015294859384928391606090910190565b03925af180156119ca575b6119ad575b84889161192e565b6119c390853d871161152057611511818361072e565b50386119a5565b6119d2611260565b6119a0565b6119ef919250853d871161152057611511818361072e565b9038611967565b6119fe611260565b611960565b611a1b919c508a3d8c1161152057611511818361072e565b9a386113d7565b611a2a611260565b6113d0565b8d5163781c18db60e01b81529091508a818b818a5afa908115611a78575b8c91611a5b575b5090611393565b611a7291508b3d8d116109a757610999818361072e565b38611a54565b611a80611260565b611a4d565b611399919e50611aa190893d8b1161152057611511818361072e565b9d90611382565b611ab0611260565b61137b565b8551636d4c6c8960e01b81526007928101928352602083019190915260006040830181905260608301819052608083015290819060a0010390fd5b60038903611b0657611b018861383e565b611328565b888103611b1657611b0188613873565b60058903611b2757611b01886138a8565b60078903611b3857611b0188613958565b60088903611b4957611b0188613aa1565b8451636d4c6c8960e01b8152600691810191825260ff8716602083015260006040830181905260608301819052608083015290819060a0010390fd5b611b9d919650873d89116109a757610999818361072e565b943861130e565b611bac611260565b611307565b939293600160ff821603611f8c57600154611bd6906103e5906001600160a01b031681565b6040805163125cf47f60e01b81526001600160a01b0385166004808301919091526024820187905260ff8516604483015290979093926020808a80606481010381600080965af1998a15611f7f575b829a611f60575b50835163204f83f960e01b81526001600160a01b039a9096908b1682888381845afa978815611f53575b8498611f34575b50428811611efb5760025486516370a0823160e01b8082526001600160a01b039092168482018181529d9e9c9d9b9c919b8d9b9394929391929087908e9081900360200181845afa9c8d15611eee575b889d611ecb575b508c611cc291843091613ba8565b828b169c8d988d888c51809c8982528180611cef8d30908301919091602081019260018060a01b03169052565b03915afa9a8b15611ebe575b8a9b611e95575b508b51632c5a8cf360e21b815260ff9092168883019081526001600160a01b039093166020840152604083019390935260608201929092528690829081906080015b03818a877f000000000000000000000000373a06bd3067f8da90239a47f316f09312b7800f165af1908115611e88575b8791611e6b575b5015611e325716803b15611e2e578361147b948a9997948d8f95611de5600080516020613cd68339815191529f9e9a8098611462986114f29f9d838f51809681958294636b42450d60e01b84528c840160209093929193604081019460018060a01b031681520152565b03925af18015611e21575b611e0e57508951908152309281019283529485928391829160200190565b80611890611e1b9261070d565b38611447565b611e29611260565b611df0565b8480fd5b8751636d4c6c8960e01b8152600f81860190815260006020820181905260408201819052606082018190526080820152819060a0010390fd5b611e829150863d881161044357610435818361072e565b38611d7b565b611e90611260565b611d74565b899392919b50611eb4611d4491853d871161152057611511818361072e565b9b91929350611d02565b611ec6611260565b611cfb565b611cc2919d50611ee790883d8a1161152057611511818361072e565b9c90611cb4565b611ef6611260565b611cad565b8551636d4c6c8960e01b81526007818401908152602081018a9052600060408201819052606082018190526080820152819060a0010390fd5b611f4c919850833d851161152057611511818361072e565b9638611c5d565b611f5b611260565b611c56565b81611f78929b503d8c116109a757610999818361072e565b9838611c2c565b611f87611260565b611c25565b604051636d4c6c8960e01b81526006600482015260ff91909116602482015260006044820181905260648201819052608482015260a490fd5b90919493611fe06103e56103e560015460018060a01b031690565b6040805163125cf47f60e01b81526001600160a01b038616600480830191909152602482018a90526006604483015260209792949092888380606481010381600080995af1928315612570575b8593612551575b5060018060a01b03809316918a856120918c878d169e8f8c61205d60025460018060a01b031690565b9051636eb1769f60e11b81526001600160a01b03808316978201978852909316602087015294919384928391829160400190565b03915afa908115612544575b8991612527575b50156124ee57928b95928795926121018f9a99968c51908a82806120e4846370a0823160e01b9d8e84528301919091602081019260018060a01b03169052565b03818a5afa9182156124e1575b8c926124c2575b50309087613ba8565b8a5186815230888201908152909e908f9081906020010381875afa9d8e156124b5575b899e612494575b508a5186815230888201908152909a8c92918a918d918290819060200103915afa9a8b15612487575b8a9b61243d575b5088936121ed938b8a858a8996878e839d9a519a8b9182528180612190308a8301919091602081019260018060a01b03169052565b03915afa988915612430575b8599612409575b5090808880938686518094819363378efa3760e01b8352165afa9081156123fc575b86916123df575b508351630f922e0760e31b81528581019788529a9116958a91829160200190565b0381875afa9788156123d2575b83986123a9575b5051632b83cccd60e01b81526001600160a01b0388169181019182526020820194909452604081019590955291938492839182906060015b03925af1801561239c575b61237f575b50858a5180948193636a5c1cc960e11b8352165afa908115612372575b8691612355575b50600354612285906103e5906001600160a01b031681565b8851848152308682019081529093919291879185919082908190602001039185165afa928315612348575b8793612329575b50813b1561232557948a9997948d8895611de5600080516020613cd68339815191529f9e9a6114629786976114f29f9d838f9e61147b9f519687958694859363248391ff60e01b85528d850160409194939294606082019560018060a01b0380921683521660208201520152565b8680fd5b612341919350863d881161152057611511818361072e565b91386122b7565b612350611260565b6122b0565b61236c9150853d87116109a757610999818361072e565b3861226d565b61237a611260565b612266565b61239590833d851161152057611511818361072e565b5038612249565b6123a4611260565b612244565b61223998506123c89087949392943d89116109a757610999818361072e565b9792909192612201565b6123da611260565b6121fa565b6123f69150833d85116109a757610999818361072e565b386121cc565b612404611260565b6121c5565b8291995091886124268194823d841161152057611511818361072e565b9a929350506121a3565b612438611260565b61219c565b899b509387938b8a899895898e99968a97883d8a11612480575b612461818361072e565b810161246c91611282565b9f509650509598505050935093989a61215b565b503d612457565b61248f611260565b612154565b8b919e506124ae90893d8b1161152057611511818361072e565b9d9061212b565b6124bd611260565b612124565b6124da9192508b3d8d1161152057611511818361072e565b90386120f8565b6124e9611260565b6120f1565b8851636d4c6c8960e01b8152601d81890190815260006020820181905260408201819052606082018190526080820152819060a0010390fd5b61253e91508d803d1061152057611511818361072e565b386120a4565b61254c611260565b61209d565b612569919350893d8b116109a757610999818361072e565b9138612034565b612578611260565b61202d565b8060001904821181151516612590570290565b61259861121a565b0290565b81156125a6570490565b634e487b7160e01b600052601260045260246000fd5b6001546125d3906103e5906001600160a01b031681565b6040805163125cf47f60e01b81526001600160a01b038416600480830191909152602482018690526000604483015291939190602090818180606481010381600080995af19081156128cd575b85916128b0575b50855163204f83f960e01b81526001600160a01b03918216949083818681895afa9081156128a3575b8791612886575b50421061284e5786516370a0823160e01b8152338582019081529094908490869081906020010381895afa948515612841575b8795612820575b5083906126ea6126c16126ba8c6103588760018060a01b03166000526007602052604060002090565b548861257d565b8a516318160ddd60e01b815290848285818d5afa918215612813575b8b926127f4575b5061259c565b96878b6127098660018060a01b03166000526007602052604060002090565b600091825260205260409020549061272091611291565b6001600160a01b03851660009081526007602052604090208c906000918252602052604090205589516388ba33df60e01b81523392810192835260208301889052988992839190829060400103925af1600080516020613cd6833981519152966127c4949180156127e7575b6127c9575b505016946127a0843388613b37565b51600081526020810193909352604083019190915233606083015281906080820190565b0390a3565b816127df92903d1061044357610435818361072e565b503880612791565b6127ef611260565b61278c565b61280c919250853d871161152057611511818361072e565b90386126e4565b61281b611260565b6126dd565b8491955061283a90823d841161152057611511818361072e565b9490612691565b612849611260565b61268a565b8651636d4c6c8960e01b81526007818601908152426020820152604081018a90526000606082018190526080820152819060a0010390fd5b61289d9150843d861161152057611511818361072e565b38612657565b6128ab611260565b612650565b6128c79150823d84116109a757610999818361072e565b38612627565b6128d5611260565b612620565b6001600160a01b03811660009081526008602052604090209095949392919081600052602052612910604060002060ff90541690565b8015612963575b6129255761125d94956129d7565b604051636d4c6c8960e01b81526011600482015260248101919091526000604482018190526001600160a01b0387166064830152608482015260a490fd5b50600254600090602090612981906103e5906001600160a01b031681565b60046040518094819363b9b8af0b60e01b83525af19081156129ca575b6000916129ac575b50612917565b6129c4915060203d811161044357610435818361072e565b386129a6565b6129d2611260565b61299e565b600154919392916129f2906103e5906001600160a01b031681565b6040805163125cf47f60e01b81526001600160a01b0384166004820152602481018790526000604482018190529193909290916020918291859160649183915af1928315612c90575b600093612c71575b50835163204f83f960e01b81526001600160a01b03938416908281600481855afa908115612c64575b600091612c47575b50804210612c125750600080516020613cd683398151915295938383612b7e612b98958d8d96612af6612acc612ac58a612bbc9f9d6103589060018060a01b03166000526007602052604060002090565b548461257d565b8b516318160ddd60e01b8152908582600481875afa918215612c05575b600092612bed575061259c565b9a88612b46612b268e612b20819d6103588d60018060a01b03166000526007602052604060002090565b54611291565b6001600160a01b038a166000908152600760205260409020909290610358565b558a516388ba33df60e01b81526001600160a01b03909116600482015260248101929092529092839190829060009082906044820190565b03925af18015612be0575b612bc2575b5050169687613b37565b51600081526020810191909152604081018690523360608201529081906080820190565b0390a390565b81612bd892903d1061044357610435818361072e565b503880612b8e565b612be8611260565b612b89565b61280c919250863d881161152057611511818361072e565b612c0d611260565b612ae9565b8551636d4c6c8960e01b815260076004820152602481019190915260006044820181905260648201819052608482015260a490fd5b612c5e9150833d851161152057611511818361072e565b38612a74565b612c6c611260565b612a6c565b81612c899294503d85116109a757610999818361072e565b9138612a43565b612c98611260565b612a3b565b6001600160a01b038116600090815260086020526040902090949392919081600052602052612cd2604060002060ff90541690565b8015612d25575b612ce75761125d9394612dc1565b604051636d4c6c8960e01b81526011600482015260248101919091526000604482018190526001600160a01b0386166064830152608482015260a490fd5b50600254600090602090612d43906103e5906001600160a01b031681565b60046040518094819363b9b8af0b60e01b83525af1908115612d8c575b600091612d6e575b50612cd9565b612d86915060203d811161044357610435818361072e565b38612d68565b612d94611260565b612d60565b9190811015612daa575b60051b0190565b612db2611203565b612da3565b3561125d81610303565b600154909290612ddb906103e5906001600160a01b031681565b6040805163125cf47f60e01b81526001600160a01b0386166004820152602481018590526000604482015291959193909290602085806064810103816000809b5af1948515613321575b8795613300575b50835163204f83f960e01b808252906020816004816001600160a01b038b165afa9081156132f3575b89916132d4575b50421061323f5750869493929190855b838703612e92575061125d968896503395506001600160a01b03169350613b3792505050565b909192939495612ee68199848a6020612eb4612eaf878c8c612d99565b612db7565b8b51636eb1769f60e11b81526001600160a01b0390911660048201523060248201529485908d90829081906044820190565b03916001600160a01b03165afa938415613232575b8694613204575b506020612f18612eaf612f449596978c8c612d99565b8b516370a0823160e01b81526001600160a01b0390911660048201529384908d90829081906024820190565b03916001600160a01b03165afa9283156131f7575b86936131c1575b506001600160a01b03166000908152600760205260409020612fbd91600491612f8c91612ac591610358565b60208c8c51938480926318160ddd60e01b825260018060a01b03165afa9182156131b4575b879261319a575061259c565b90612fca6004548361259c565b9381811061316457508392916130f26001956130ec8f8f908f918f8f8f612eaf958f93613067612eaf886131009f96866130476020809a8c613013612eaf888861309f9f612d99565b8a516388ba33df60e01b81526001600160a01b03909116600482015260248101929092529092839190829081906044820190565b03916001600160a01b038d165af18015613157575b61313a575b50612d99565b90516326ce41a160e21b81526001600160a01b0390911660048201523060248201526000604482015293849283919082906064820190565b03926001600160a01b03165af1801561312d575b61310e575b506130e48d6103586114ad8a612b20846103588860018060a01b03166000526007602052604060002090565b558d8d612d99565b92611291565b90858060a01b038d16613b37565b019801959493929190612e6c565b6131269060203d60201161044357610435818361072e565b50386130b8565b613135611260565b6130b3565b613150908a3d8c1161044357610435818361072e565b5038613061565b61315f611260565b61305c565b8951636d4c6c8960e01b81526014600482015260248101919091526044810191909152600060648201819052608482015260a490fd5b61280c91925060203d60201161152057611511818361072e565b6131bc611260565b612fb1565b6004919350612ac5612fbd936103586131eb612f8c9460203d60201161152057611511818361072e565b96945050935050612f60565b6131ff611260565b612f59565b612f44939450612f18612eaf613228602093843d861161152057611511818361072e565b9695505050612f02565b61323a611260565b612efb565b84519081526102ff90889086906020816004816001600160a01b038d165afa9283156132c7575b926132a6575b5051636d4c6c8960e01b815260076004820152602481019190915260006044820181905260648201819052608482015290819060a4820190565b6132c091925060203d60201161152057611511818361072e565b908361326c565b6132cf611260565b613266565b6132ed915060203d60201161152057611511818361072e565b38612e5c565b6132fb611260565b612e55565b61331a91955060203d6020116109a757610999818361072e565b9338612e2c565b613329611260565b612e25565b60405163247c579160e21b815292600492916020908590859082906001600160a01b03165afa9384156137d6575b6000946137b5575b506040516362b9c05160e11b815260208185816001600160a01b0389165afa9081156137a8575b600091613789575b50604051635a7ad10d60e11b81526133e790602090818188816001600160a01b038c165afa90811561377c575b60009161375f575b50604051809381926371a5d76160e01b83528983019190602083019252565b03816001600160a01b038a165afa908115613752575b600091613733575b506002546001600160a01b03166040516370a0823160e01b81526001600160a01b03821687820190815290949060209086908190830103816001600160a01b0387165afa948515613726575b600095613705575b508085116136fd575b506001600160a01b0316803b1561000e5760408051633ad4d8b960e11b81526001600160a01b0390931687840190815260208101869052600092849290918391859183910103925af180156136f0575b6136dd575b506001600160a01b0316803b1561000e576040805163f3fef3a360e01b81526001600160a01b038716868201908152602081019490945292600092849290918391859183910103925af180156136d0575b6136bd575b5060405163f018ae6960e01b808252909360208585816001600160a01b0385165afa9485156136b0575b60009561368f575b5060035460209061355a906103e5906001600160a01b031681565b60405193845292918290869082906001600160a01b03165afa908115613682575b600091613663575b506040516370a0823160e01b815230858201908152909560209187919082908190850103916001600160a01b03165afa948515613656575b600095613635575b50813b1561000e576040805163248391ff60e01b81526001600160a01b039283169581019586529390911660208501528301939093529160009183919082908490829060600103925af18015613628575b61361b5750565b806118906112469261070d565b613630611260565b613614565b61364f91955060203d60201161152057611511818361072e565b93386135c3565b61365e611260565b6135bb565b61367c915060203d6020116109a757610999818361072e565b38613583565b61368a611260565b61357b565b6136a991955060203d6020116109a757610999818361072e565b933861353f565b6136b8611260565b613537565b806118906136ca9261070d565b3861350d565b6136d8611260565b613508565b806118906136ea9261070d565b386134b7565b6136f8611260565b6134b2565b935038613462565b61371f91955060203d60201161152057611511818361072e565b9338613459565b61372e611260565b613451565b61374c915060203d6020116109a757610999818361072e565b38613405565b61375a611260565b6133fd565b6137769150823d841161152057611511818361072e565b386133c8565b613784611260565b6133c0565b6137a2915060203d6020116109a757610999818361072e565b38613393565b6137b0611260565b61338b565b6137cf91945060203d6020116109a757610999818361072e565b9238613364565b6137de611260565b61335c565b60405163204f83f960e01b815290602090829060049082906001600160a01b03165afa908115613831575b600091613819575090565b61125d915060203d811161152057611511818361072e565b613839611260565b61380e565b60405163aa082a9d60e01b815290602090829060049082906001600160a01b03165afa90811561383157600091613819575090565b6040516370c264df60e11b815290602090829060049082906001600160a01b03165afa90811561383157600091613819575090565b6040516316f0115b60e01b81526020916001600160a01b0391839182908290600490829087165afa90811561394b575b60009161392e575b50600460405180948193632745fed560e11b8352165afa918215613921575b60009261390b57505090565b61125d9250803d1061152057611511818361072e565b613929611260565b6138ff565b6139459150823d84116109a757610999818361072e565b386138e0565b613953611260565b6138d8565b60405163247c579160e21b81526020916001600160a01b03918391613a0891849184908290600490829086165afa908115613a94575b600091613a77575b50166040516362b9c05160e11b8152838082600481865afa918215613a6a575b600092613a4a575b50604051636558954f60e01b81529192829060049082905afa908115613a3d575b600091613a20575b506040519485809481936220e10960e51b8352600483019190602083019252565b0392165afa9182156139215760009261390b57505090565b613a379150843d861161152057611511818361072e565b386139e7565b613a45611260565b6139df565b60049250613a6490823d84116109a757610999818361072e565b916139be565b613a72611260565b6139b6565b613a8e9150843d86116109a757610999818361072e565b38613996565b613a9c611260565b61398e565b60405163e16695b560e01b815290602090829060049082906001600160a01b03165afa908115613b2a575b600091613adf575b5064ffffffffff1690565b6020813d8211613b22575b81613af76020938361072e565b81010312613b1e57519064ffffffffff82168203613b1b575064ffffffffff613ad4565b80fd5b5080fd5b3d9150613aea565b613b32611260565b613acc565b6044600092838093613b6a966040519363a9059cbb60e01b855260018060a01b0316600485015260248401525af1613c27565b15613b7157565b60405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b6044820152606490fd5b60009283606492613be4968295604051946323b872dd60e01b865260018060a01b03809216600487015216602485015260448401525af1613c27565b15613beb57565b60405162461bcd60e51b81526020600482015260146024820152731d1c985b9cd9995c88199c9bdb4819985a5b195960621b6044820152606490fd5b6000903d9015613c57579081602014613c4a575015613c4557600090565b600190565b9050602081803e51151590565b908181803efd5b60405163095ea7b360e01b81526001600160a01b0390921660048301526000196024830152613c9891600091829160449183905af1613c27565b15613c9f57565b60405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b6044820152606490fdfef9820069613a0f200b0791623bec3ed14a17256cf61c3fc2160fdadbbcdda463a26469706673582212207234f4d02766bbc48118cc207dfdebcc367022e3d70cc9a1355fb4874657444a64736f6c63430008100033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000429b47c4aeadd42bbcb118651c8984086bfc4551000000000000000000000000373a06bd3067f8da90239a47f316f09312b7800f000000000000000000000000db5fd0678eed82246b599da6bc36b56157e4bed8
-----Decoded View---------------
Arg [0] : l (address): 0x429B47C4AEADD42BBcB118651C8984086Bfc4551
Arg [1] : s (address): 0x373a06bD3067f8DA90239a47f316F09312b7800F
Arg [2] : t (address): 0xdB5fD0678eED82246b599da6BC36B56157E4beD8
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000429b47c4aeadd42bbcb118651c8984086bfc4551
Arg [1] : 000000000000000000000000373a06bd3067f8da90239a47f316f09312b7800f
Arg [2] : 000000000000000000000000db5fd0678eed82246b599da6bc36b56157e4bed8
Loading...
Loading
Loading...
Loading
Net Worth in USD
$890.10
Net Worth in ETH
0.428353
Token Allocations
USDC
100.00%
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| ETH | 100.00% | $0.999903 | 890.1864 | $890.1 |
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.