More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
15719206 | 807 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Contract Name:
ERC4626Adapter
Compiler Version
v0.8.11+commit.d7f03943
Optimization Enabled:
Yes with 15000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.11; // External references import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol"; import { SafeTransferLib } from "@rari-capital/solmate/src/utils/SafeTransferLib.sol"; import { ERC4626 } from "@rari-capital/solmate/src/mixins/ERC4626.sol"; import { Errors } from "@sense-finance/v1-utils/src/libs/Errors.sol"; // Internal references import { MasterPriceOracle } from "../../implementations/oracles/MasterPriceOracle.sol"; import { FixedMath } from "../../../external/FixedMath.sol"; import { BaseAdapter } from "../BaseAdapter.sol"; /// @notice Adapter contract for ERC4626 Vaults contract ERC4626Adapter is BaseAdapter { using SafeTransferLib for ERC20; using FixedMath for uint256; address public constant RARI_MASTER_ORACLE = 0x1887118E49e0F4A78Bd71B792a49dE03504A764D; uint256 public immutable BASE_UINT; uint256 public immutable SCALE_FACTOR; constructor( address _divider, address _target, address _rewardsRecipient, uint128 _ifee, AdapterParams memory _adapterParams ) BaseAdapter(_divider, _target, address(ERC4626(_target).asset()), _rewardsRecipient, _ifee, _adapterParams) { uint256 tDecimals = ERC4626(target).decimals(); BASE_UINT = 10**tDecimals; SCALE_FACTOR = 10**(18 - tDecimals); // we assume targets decimals <= 18 ERC20(underlying).safeApprove(target, type(uint256).max); } function scale() external override returns (uint256) { return ERC4626(target).convertToAssets(BASE_UINT) * SCALE_FACTOR; } function scaleStored() external view override returns (uint256) { return ERC4626(target).convertToAssets(BASE_UINT) * SCALE_FACTOR; } function getUnderlyingPrice() external view override returns (uint256 price) { price = MasterPriceOracle(adapterParams.oracle).price(underlying); if (price == 0) { revert Errors.InvalidPrice(); } } function wrapUnderlying(uint256 assets) external override returns (uint256 _shares) { ERC20(underlying).safeTransferFrom(msg.sender, address(this), assets); _shares = ERC4626(target).deposit(assets, msg.sender); } function unwrapTarget(uint256 shares) external override returns (uint256 _assets) { _assets = ERC4626(target).redeem(shares, msg.sender, msg.sender); } function extractToken(address token) external virtual override { // Check that token is neither the target nor the stake if (token == target || token == adapterParams.stake) revert Errors.TokenNotSupported(); ERC20 t = ERC20(token); uint256 tBal = t.balanceOf(address(this)); t.safeTransfer(rewardsRecipient, tBal); emit RewardsClaimed(token, rewardsRecipient, tBal); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol) /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. abstract contract ERC20 { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); /*////////////////////////////////////////////////////////////// METADATA STORAGE //////////////////////////////////////////////////////////////*/ string public name; string public symbol; uint8 public immutable decimals; /*////////////////////////////////////////////////////////////// ERC20 STORAGE //////////////////////////////////////////////////////////////*/ uint256 public totalSupply; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; /*////////////////////////////////////////////////////////////// EIP-2612 STORAGE //////////////////////////////////////////////////////////////*/ uint256 internal immutable INITIAL_CHAIN_ID; bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; mapping(address => uint256) public nonces; /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor( string memory _name, string memory _symbol, uint8 _decimals ) { name = _name; symbol = _symbol; decimals = _decimals; INITIAL_CHAIN_ID = block.chainid; INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); } /*////////////////////////////////////////////////////////////// ERC20 LOGIC //////////////////////////////////////////////////////////////*/ function approve(address spender, uint256 amount) public virtual returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } function transfer(address to, uint256 amount) public virtual returns (bool) { balanceOf[msg.sender] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(msg.sender, to, amount); return true; } function transferFrom( address from, address to, uint256 amount ) public virtual returns (bool) { uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; balanceOf[from] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(from, to, amount); return true; } /*////////////////////////////////////////////////////////////// EIP-2612 LOGIC //////////////////////////////////////////////////////////////*/ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual { require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); // Unchecked because the only math done is incrementing // the owner's nonce which cannot realistically overflow. unchecked { address recoveredAddress = ecrecover( keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(), keccak256( abi.encode( keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ), owner, spender, value, nonces[owner]++, deadline ) ) ) ), v, r, s ); require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER"); allowance[recoveredAddress][spender] = value; } emit Approval(owner, spender, value); } function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); } function computeDomainSeparator() internal view virtual returns (bytes32) { return keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(name)), keccak256("1"), block.chainid, address(this) ) ); } /*////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint(address to, uint256 amount) internal virtual { totalSupply += amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(address(0), to, amount); } function _burn(address from, uint256 amount) internal virtual { balanceOf[from] -= amount; // Cannot underflow because a user's balance // will never be larger than the total supply. unchecked { totalSupply -= amount; } emit Transfer(from, address(0), amount); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; import {ERC20} from "../tokens/ERC20.sol"; /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol) /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer. /// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller. library SafeTransferLib { event Debug(bool one, bool two, uint256 retsize); /*////////////////////////////////////////////////////////////// ETH OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferETH(address to, uint256 amount) internal { bool success; assembly { // Transfer the ETH and store if it succeeded or not. success := call(gas(), to, amount, 0, 0, 0, 0) } require(success, "ETH_TRANSFER_FAILED"); } /*////////////////////////////////////////////////////////////// ERC20 OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferFrom( ERC20 token, address from, address to, uint256 amount ) internal { bool success; assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument. mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument. mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 100, 0, 32) ) } require(success, "TRANSFER_FROM_FAILED"); } function safeTransfer( ERC20 token, address to, uint256 amount ) internal { bool success; assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "TRANSFER_FAILED"); } function safeApprove( ERC20 token, address to, uint256 amount ) internal { bool success; assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "APPROVE_FAILED"); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; import {ERC20} from "../tokens/ERC20.sol"; import {SafeTransferLib} from "../utils/SafeTransferLib.sol"; import {FixedPointMathLib} from "../utils/FixedPointMathLib.sol"; /// @notice Minimal ERC4626 tokenized Vault implementation. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/mixins/ERC4626.sol) abstract contract ERC4626 is ERC20 { using SafeTransferLib for ERC20; using FixedPointMathLib for uint256; /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares); event Withdraw( address indexed caller, address indexed receiver, address indexed owner, uint256 assets, uint256 shares ); /*////////////////////////////////////////////////////////////// IMMUTABLES //////////////////////////////////////////////////////////////*/ ERC20 public immutable asset; constructor( ERC20 _asset, string memory _name, string memory _symbol ) ERC20(_name, _symbol, _asset.decimals()) { asset = _asset; } /*////////////////////////////////////////////////////////////// DEPOSIT/WITHDRAWAL LOGIC //////////////////////////////////////////////////////////////*/ function deposit(uint256 assets, address receiver) public virtual returns (uint256 shares) { // Check for rounding error since we round down in previewDeposit. require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES"); // Need to transfer before minting or ERC777s could reenter. asset.safeTransferFrom(msg.sender, address(this), assets); _mint(receiver, shares); emit Deposit(msg.sender, receiver, assets, shares); afterDeposit(assets, shares); } function mint(uint256 shares, address receiver) public virtual returns (uint256 assets) { assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up. // Need to transfer before minting or ERC777s could reenter. asset.safeTransferFrom(msg.sender, address(this), assets); _mint(receiver, shares); emit Deposit(msg.sender, receiver, assets, shares); afterDeposit(assets, shares); } function withdraw( uint256 assets, address receiver, address owner ) public virtual returns (uint256 shares) { shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up. if (msg.sender != owner) { uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares; } beforeWithdraw(assets, shares); _burn(owner, shares); emit Withdraw(msg.sender, receiver, owner, assets, shares); asset.safeTransfer(receiver, assets); } function redeem( uint256 shares, address receiver, address owner ) public virtual returns (uint256 assets) { if (msg.sender != owner) { uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares; } // Check for rounding error since we round down in previewRedeem. require((assets = previewRedeem(shares)) != 0, "ZERO_ASSETS"); beforeWithdraw(assets, shares); _burn(owner, shares); emit Withdraw(msg.sender, receiver, owner, assets, shares); asset.safeTransfer(receiver, assets); } /*////////////////////////////////////////////////////////////// ACCOUNTING LOGIC //////////////////////////////////////////////////////////////*/ function totalAssets() public view virtual returns (uint256); function convertToShares(uint256 assets) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets()); } function convertToAssets(uint256 shares) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? shares : shares.mulDivDown(totalAssets(), supply); } function previewDeposit(uint256 assets) public view virtual returns (uint256) { return convertToShares(assets); } function previewMint(uint256 shares) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? shares : shares.mulDivUp(totalAssets(), supply); } function previewWithdraw(uint256 assets) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? assets : assets.mulDivUp(supply, totalAssets()); } function previewRedeem(uint256 shares) public view virtual returns (uint256) { return convertToAssets(shares); } /*////////////////////////////////////////////////////////////// DEPOSIT/WITHDRAWAL LIMIT LOGIC //////////////////////////////////////////////////////////////*/ function maxDeposit(address) public view virtual returns (uint256) { return type(uint256).max; } function maxMint(address) public view virtual returns (uint256) { return type(uint256).max; } function maxWithdraw(address owner) public view virtual returns (uint256) { return convertToAssets(balanceOf[owner]); } function maxRedeem(address owner) public view virtual returns (uint256) { return balanceOf[owner]; } /*////////////////////////////////////////////////////////////// INTERNAL HOOKS LOGIC //////////////////////////////////////////////////////////////*/ function beforeWithdraw(uint256 assets, uint256 shares) internal virtual {} function afterDeposit(uint256 assets, uint256 shares) internal virtual {} }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.4; library Errors { // Auth error CombineRestricted(); error IssuanceRestricted(); error NotAuthorized(); error OnlyYT(); error OnlyDivider(); error OnlyPeriphery(); error OnlyPermissionless(); error RedeemRestricted(); error Untrusted(); // Adapters error TokenNotSupported(); error FlashCallbackFailed(); error SenderNotEligible(); error TargetMismatch(); error TargetNotSupported(); error InvalidAdapterType(); error PriceOracleNotFound(); // Divider error AlreadySettled(); error CollectNotSettled(); error GuardCapReached(); error IssuanceFeeCapExceeded(); error IssueOnSettle(); error NotSettled(); // Input & validations error AlreadyInitialized(); error DuplicateSeries(); error ExistingValue(); error InvalidAdapter(); error InvalidMaturity(); error InvalidParam(); error NotImplemented(); error OutOfWindowBoundaries(); error SeriesDoesNotExist(); error SwapTooSmall(); error TargetParamsNotSet(); error PoolParamsNotSet(); error PTParamsNotSet(); error AttemptFailed(); error InvalidPrice(); error BadContractInteration(); // Periphery error FactoryNotSupported(); error FlashBorrowFailed(); error FlashUntrustedBorrower(); error FlashUntrustedLoanInitiator(); error UnexpectedSwapAmount(); error TooMuchLeftoverTarget(); // Fuse error AdapterNotSet(); error FailedBecomeAdmin(); error FailedAddTargetMarket(); error FailedToAddPTMarket(); error FailedAddLpMarket(); error OracleNotReady(); error PoolAlreadyDeployed(); error PoolNotDeployed(); error PoolNotSet(); error SeriesNotQueued(); error TargetExists(); error TargetNotInFuse(); // Tokens error MintFailed(); error RedeemFailed(); error TransferFailed(); }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.11; import { Trust } from "@sense-finance/v1-utils/src/Trust.sol"; import { Errors } from "@sense-finance/v1-utils/src/libs/Errors.sol"; import { IPriceFeed } from "../../abstract/IPriceFeed.sol"; /// @notice This contract gets prices from an available oracle address which must implement IPriceFeed.sol /// If there's no oracle set, it will try getting the price from Chainlink's Oracle. /// @author Inspired by: https://github.com/Rari-Capital/fuse-v1/blob/development/src/oracles/MasterPriceOracle.sol contract MasterPriceOracle is IPriceFeed, Trust { address public senseChainlinkPriceOracle; /// @dev Maps underlying token addresses to oracle addresses. mapping(address => address) public oracles; /// @dev Constructor to initialize state variables. /// @param _chainlinkOracle The underlying ERC20 token addresses to link to `_oracles`. /// @param _underlyings The underlying ERC20 token addresses to link to `_oracles`. /// @param _oracles The `PriceOracle` contracts to be assigned to `underlyings`. constructor( address _chainlinkOracle, address[] memory _underlyings, address[] memory _oracles ) public Trust(msg.sender) { senseChainlinkPriceOracle = _chainlinkOracle; // Input validation if (_underlyings.length != _oracles.length) revert Errors.InvalidParam(); // Initialize state variables for (uint256 i = 0; i < _underlyings.length; i++) oracles[_underlyings[i]] = _oracles[i]; } /// @dev Sets `_oracles` for `underlyings`. /// Caller of this function must make sure that the oracles being added return non-stale, greater than 0 /// prices for all underlying tokens. function add(address[] calldata _underlyings, address[] calldata _oracles) external requiresTrust { if (_underlyings.length <= 0 || _underlyings.length != _oracles.length) revert Errors.InvalidParam(); for (uint256 i = 0; i < _underlyings.length; i++) { oracles[_underlyings[i]] = _oracles[i]; } } /// @dev Attempts to return the price in ETH of `underlying` (implements `BasePriceOracle`). function price(address underlying) external view override returns (uint256) { if (underlying == 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) return 1e18; // Return 1e18 for WETH address oracle = oracles[underlying]; if (oracle != address(0)) { return IPriceFeed(oracle).price(underlying); } else { // Try token/ETH from Sense's Chainlink Oracle try IPriceFeed(senseChainlinkPriceOracle).price(underlying) returns (uint256 price) { return price; } catch { revert Errors.PriceOracleNotFound(); } } } /// @dev Sets the `senseChainlinkPriceOracle`. function setSenseChainlinkPriceOracle(address _senseChainlinkPriceOracle) public requiresTrust { senseChainlinkPriceOracle = _senseChainlinkPriceOracle; emit SenseChainlinkPriceOracleChanged(senseChainlinkPriceOracle); } /* ========== LOGS ========== */ event SenseChainlinkPriceOracleChanged(address indexed senseChainlinkPriceOracle); }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.11; /// @title Fixed point arithmetic library /// @author Taken from https://github.com/Rari-Capital/solmate/blob/main/src/utils/FixedPointMathLib.sol library FixedMath { uint256 internal constant WAD = 1e18; uint256 internal constant RAY = 1e27; function fmul( uint256 x, uint256 y, uint256 baseUnit ) internal pure returns (uint256) { return mulDivDown(x, y, baseUnit); // Equivalent to (x * y) / baseUnit rounded down. } function fmul(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down. } function fmulUp( uint256 x, uint256 y, uint256 baseUnit ) internal pure returns (uint256) { return mulDivUp(x, y, baseUnit); // Equivalent to (x * y) / baseUnit rounded up. } function fmulUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up. } function fdiv( uint256 x, uint256 y, uint256 baseUnit ) internal pure returns (uint256) { return mulDivDown(x, baseUnit, y); // Equivalent to (x * baseUnit) / y rounded down. } function fdiv(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down. } function fdivUp( uint256 x, uint256 y, uint256 baseUnit ) internal pure returns (uint256) { return mulDivUp(x, baseUnit, y); // Equivalent to (x * baseUnit) / y rounded up. } function fdivUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up. } function mulDivDown( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 z) { assembly { // Store x * y in z for now. z := mul(x, y) // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y)) if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) { revert(0, 0) } // Divide z by the denominator. z := div(z, denominator) } } function mulDivUp( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 z) { assembly { // Store x * y in z for now. z := mul(x, y) // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y)) if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) { revert(0, 0) } // First, divide z - 1 by the denominator and add 1. // We allow z - 1 to underflow if z is 0, because we multiply the // end result by 0 if z is zero, ensuring we return 0 if z is zero. z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1)) } } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.11; // External references import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol"; import { SafeTransferLib } from "@rari-capital/solmate/src/utils/SafeTransferLib.sol"; import { IERC3156FlashLender } from "../../external/flashloan/IERC3156FlashLender.sol"; import { IERC3156FlashBorrower } from "../../external/flashloan/IERC3156FlashBorrower.sol"; // Internal references import { Divider } from "../../Divider.sol"; import { Crop } from "./extensions/Crop.sol"; import { Crops } from "./extensions/Crops.sol"; import { Errors } from "@sense-finance/v1-utils/src/libs/Errors.sol"; import { Trust } from "@sense-finance/v1-utils/src/Trust.sol"; /// @title Assign value to Target tokens abstract contract BaseAdapter is Trust, IERC3156FlashLender { using SafeTransferLib for ERC20; /* ========== CONSTANTS ========== */ bytes32 public constant CALLBACK_SUCCESS = keccak256("ERC3156FlashBorrower.onFlashLoan"); /* ========== PUBLIC IMMUTABLES ========== */ /// @notice Sense core Divider address address public immutable divider; /// @notice Target token to divide address public immutable target; /// @notice Underlying for the Target address public immutable underlying; /// @notice Issuance fee uint128 public immutable ifee; /// @notice Rewards recipient address public rewardsRecipient; /// @notice adapter params AdapterParams public adapterParams; /* ========== DATA STRUCTURES ========== */ struct AdapterParams { /// @notice Oracle address address oracle; /// @notice Token to stake at issuance address stake; /// @notice Amount to stake at issuance uint256 stakeSize; /// @notice Min maturity (seconds after block.timstamp) uint256 minm; /// @notice Max maturity (seconds after block.timstamp) uint256 maxm; /// @notice WAD number representing the percentage of the total /// principal that's set aside for Yield Tokens (e.g. 0.1e18 means that 10% of the principal is reserved). /// @notice If `0`, it means no principal is set aside for Yield Tokens uint64 tilt; /// @notice The number this function returns will be used to determine its access by checking for binary /// digits using the following scheme: <onRedeem(y/n)><collect(y/n)><combine(y/n)><issue(y/n)> /// (e.g. 0101 enables `collect` and `issue`, but not `combine`) uint48 level; /// @notice 0 for monthly, 1 for weekly uint16 mode; } /* ========== METADATA STORAGE ========== */ string public name; string public symbol; constructor( address _divider, address _target, address _underlying, address _rewardsRecipient, uint128 _ifee, AdapterParams memory _adapterParams ) Trust(msg.sender) { divider = _divider; target = _target; underlying = _underlying; rewardsRecipient = _rewardsRecipient; ifee = _ifee; adapterParams = _adapterParams; name = string(abi.encodePacked(ERC20(_target).name(), " Adapter")); symbol = string(abi.encodePacked(ERC20(_target).symbol(), "-adapter")); ERC20(_target).safeApprove(divider, type(uint256).max); ERC20(_adapterParams.stake).safeApprove(divider, type(uint256).max); } /// @notice Loan `amount` target to `receiver`, and takes it back after the callback. /// @param receiver The contract receiving target, needs to implement the /// `onFlashLoan(address user, address adapter, uint256 maturity, uint256 amount)` interface. /// @param amount The amount of target lent. /// @param data (encoded adapter address, maturity and YT amount the use has sent in) function flashLoan( IERC3156FlashBorrower receiver, address, /* fee */ uint256 amount, bytes calldata data ) external returns (bool) { if (Divider(divider).periphery() != msg.sender) revert Errors.OnlyPeriphery(); ERC20(target).safeTransfer(address(receiver), amount); bytes32 keccak = IERC3156FlashBorrower(receiver).onFlashLoan(msg.sender, target, amount, 0, data); if (keccak != CALLBACK_SUCCESS) revert Errors.FlashCallbackFailed(); ERC20(target).safeTransferFrom(address(receiver), address(this), amount); return true; } /* ========== REQUIRED VALUE GETTERS ========== */ /// @notice Calculate and return this adapter's Scale value for the current timestamp. To be overriden by child contracts /// @dev For some Targets, such as cTokens, this is simply the exchange rate, or `supply cToken / supply underlying` /// @dev For other Targets, such as AMM LP shares, specialized logic will be required /// @dev This function _must_ return a WAD number representing the current exchange rate /// between the Target and the Underlying. /// @return value WAD Scale value function scale() external virtual returns (uint256); /// @notice Cached scale value getter /// @dev For situations where you need scale from a view function function scaleStored() external view virtual returns (uint256); /// @notice Returns the current price of the underlying in ETH terms function getUnderlyingPrice() external view virtual returns (uint256); /* ========== REQUIRED UTILITIES ========== */ /// @notice Deposits underlying `amount`in return for target. Must be overriden by child contracts /// @param amount Underlying amount /// @return amount of target returned function wrapUnderlying(uint256 amount) external virtual returns (uint256); /// @notice Deposits target `amount`in return for underlying. Must be overriden by child contracts /// @param amount Target amount /// @return amount of underlying returned function unwrapTarget(uint256 amount) external virtual returns (uint256); function flashFee(address token, uint256) external view returns (uint256) { if (token != target) revert Errors.TokenNotSupported(); return 0; } function maxFlashLoan(address token) external view override returns (uint256) { return ERC20(token).balanceOf(address(this)); } function setRewardsRecipient(address recipient) external requiresTrust { emit RewardsRecipientChanged(rewardsRecipient, recipient); rewardsRecipient = recipient; } /// @notice Transfers reward tokens from the adapter to Sense's reward container /// @dev If adapter is either Crop or Crops, we check token is not any of the reward tokens function extractToken(address token) external virtual; /* ========== OPTIONAL HOOKS ========== */ /// @notice Notification whenever the Divider adds or removes Target function notify( address, /* usr */ uint256, /* amt */ bool /* join */ ) public virtual { return; } /// @notice Hook called whenever a user redeems PT function onRedeem( uint256, /* uBal */ uint256, /* mscale */ uint256, /* maxscale */ uint256 /* tBal */ ) public virtual { return; } /* ========== PUBLIC STORAGE ACCESSORS ========== */ function getMaturityBounds() external view virtual returns (uint256, uint256) { return (adapterParams.minm, adapterParams.maxm); } function getStakeAndTarget() external view returns ( address, address, uint256 ) { return (target, adapterParams.stake, adapterParams.stakeSize); } function mode() external view returns (uint256) { return adapterParams.mode; } function tilt() external view returns (uint256) { return adapterParams.tilt; } function level() external view returns (uint256) { return adapterParams.level; } /* ========== LOGS ========== */ event RewardsRecipientChanged(address indexed oldRecipient, address indexed newRecipient); event RewardsClaimed(address indexed token, address indexed recipient, uint256 indexed amount); }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Arithmetic library with operations for fixed-point numbers. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/FixedPointMathLib.sol) /// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol) library FixedPointMathLib { /*////////////////////////////////////////////////////////////// SIMPLIFIED FIXED POINT OPERATIONS //////////////////////////////////////////////////////////////*/ uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s. function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down. } function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up. } function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down. } function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up. } /*////////////////////////////////////////////////////////////// LOW LEVEL FIXED POINT OPERATIONS //////////////////////////////////////////////////////////////*/ function mulDivDown( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 z) { assembly { // Store x * y in z for now. z := mul(x, y) // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y)) if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) { revert(0, 0) } // Divide z by the denominator. z := div(z, denominator) } } function mulDivUp( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 z) { assembly { // Store x * y in z for now. z := mul(x, y) // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y)) if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) { revert(0, 0) } // First, divide z - 1 by the denominator and add 1. // We allow z - 1 to underflow if z is 0, because we multiply the // end result by 0 if z is zero, ensuring we return 0 if z is zero. z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1)) } } function rpow( uint256 x, uint256 n, uint256 scalar ) internal pure returns (uint256 z) { assembly { switch x case 0 { switch n case 0 { // 0 ** 0 = 1 z := scalar } default { // 0 ** n = 0 z := 0 } } default { switch mod(n, 2) case 0 { // If n is even, store scalar in z for now. z := scalar } default { // If n is odd, store x in z for now. z := x } // Shifting right by 1 is like dividing by 2. let half := shr(1, scalar) for { // Shift n right by 1 before looping to halve it. n := shr(1, n) } n { // Shift n right by 1 each iteration to halve it. n := shr(1, n) } { // Revert immediately if x ** 2 would overflow. // Equivalent to iszero(eq(div(xx, x), x)) here. if shr(128, x) { revert(0, 0) } // Store x squared. let xx := mul(x, x) // Round to the nearest number. let xxRound := add(xx, half) // Revert if xx + half overflowed. if lt(xxRound, xx) { revert(0, 0) } // Set x to scaled xxRound. x := div(xxRound, scalar) // If n is even: if mod(n, 2) { // Compute z * x. let zx := mul(z, x) // If z * x overflowed: if iszero(eq(div(zx, x), z)) { // Revert if x is non-zero. if iszero(iszero(x)) { revert(0, 0) } } // Round to the nearest number. let zxRound := add(zx, half) // Revert if zx + half overflowed. if lt(zxRound, zx) { revert(0, 0) } // Return properly scaled zxRound. z := div(zxRound, scalar) } } } } } /*////////////////////////////////////////////////////////////// GENERAL NUMBER UTILITIES //////////////////////////////////////////////////////////////*/ function sqrt(uint256 x) internal pure returns (uint256 z) { assembly { // Start off with z at 1. z := 1 // Used below to help find a nearby power of 2. let y := x // Find the lowest power of 2 that is at least sqrt(x). if iszero(lt(y, 0x100000000000000000000000000000000)) { y := shr(128, y) // Like dividing by 2 ** 128. z := shl(64, z) // Like multiplying by 2 ** 64. } if iszero(lt(y, 0x10000000000000000)) { y := shr(64, y) // Like dividing by 2 ** 64. z := shl(32, z) // Like multiplying by 2 ** 32. } if iszero(lt(y, 0x100000000)) { y := shr(32, y) // Like dividing by 2 ** 32. z := shl(16, z) // Like multiplying by 2 ** 16. } if iszero(lt(y, 0x10000)) { y := shr(16, y) // Like dividing by 2 ** 16. z := shl(8, z) // Like multiplying by 2 ** 8. } if iszero(lt(y, 0x100)) { y := shr(8, y) // Like dividing by 2 ** 8. z := shl(4, z) // Like multiplying by 2 ** 4. } if iszero(lt(y, 0x10)) { y := shr(4, y) // Like dividing by 2 ** 4. z := shl(2, z) // Like multiplying by 2 ** 2. } if iszero(lt(y, 0x8)) { // Equivalent to 2 ** z. z := shl(1, z) } // Shifting right by 1 is like dividing by 2. z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) // Compute a rounded down version of z. let zRoundDown := div(x, z) // If zRoundDown is smaller, use it. if lt(zRoundDown, z) { z := zRoundDown } } } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.7.0; /// @notice Ultra minimal authorization logic for smart contracts. /// @author From https://github.com/Rari-Capital/solmate/blob/fab107565a51674f3a3b5bfdaacc67f6179b1a9b/src/auth/Trust.sol abstract contract Trust { event UserTrustUpdated(address indexed user, bool trusted); mapping(address => bool) public isTrusted; constructor(address initialUser) { isTrusted[initialUser] = true; emit UserTrustUpdated(initialUser, true); } function setIsTrusted(address user, bool trusted) public virtual requiresTrust { isTrusted[user] = trusted; emit UserTrustUpdated(user, trusted); } modifier requiresTrust() { require(isTrusted[msg.sender], "UNTRUSTED"); _; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.11; /// @title IPriceFeed /// @notice Returns prices of underlying tokens /// @author Taken from: https://github.com/Rari-Capital/fuse-v1/blob/development/src/oracles/BasePriceOracle.sol interface IPriceFeed { /// @notice Get the price of an underlying asset. /// @param underlying The underlying asset to get the price of. /// @return price The underlying asset price in ETH as a mantissa (scaled by 1e18). /// Zero means the price is unavailable. function price(address underlying) external view returns (uint256 price); }
pragma solidity ^0.8.0; import "./IERC3156FlashBorrower.sol"; interface IERC3156FlashLender { /// @dev The amount of currency available to be lent. /// @param token The loan currency. /// @return The amount of `token` that can be borrowed. function maxFlashLoan(address token) external view returns (uint256); /// @dev The fee to be charged for a given loan. /// @param token The loan currency. /// @param amount The amount of tokens lent. /// @return The amount of `token` to be charged for the loan, on top of the returned principal. function flashFee(address token, uint256 amount) external view returns (uint256); /// @dev Initiate a flash loan. /// @param receiver The receiver of the tokens in the loan, and the receiver of the callback. /// @param token The loan currency. /// @param amount The amount of tokens lent. /// @param data Arbitrary data structure, intended to contain user-defined parameters. function flashLoan( IERC3156FlashBorrower receiver, address token, uint256 amount, bytes calldata data ) external returns (bool); }
pragma solidity ^0.8.0; interface IERC3156FlashBorrower { /// @dev Receive a flash loan. /// @param initiator The initiator of the loan. /// @param token The loan currency. /// @param amount The amount of tokens lent. /// @param fee The additional amount of tokens to repay. /// @param data Arbitrary data structure, intended to contain user-defined parameters. /// @return The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan" function onFlashLoan( address initiator, address token, uint256 amount, uint256 fee, bytes calldata data ) external returns (bytes32); }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.11; // External references import { Pausable } from "@openzeppelin/contracts/security/Pausable.sol"; import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol"; import { SafeTransferLib } from "@rari-capital/solmate/src/utils/SafeTransferLib.sol"; import { ReentrancyGuard } from "@rari-capital/solmate/src/utils/ReentrancyGuard.sol"; import { DateTime } from "./external/DateTime.sol"; import { FixedMath } from "./external/FixedMath.sol"; // Internal references import { Errors } from "@sense-finance/v1-utils/src/libs/Errors.sol"; import { Levels } from "@sense-finance/v1-utils/src/libs/Levels.sol"; import { Trust } from "@sense-finance/v1-utils/src/Trust.sol"; import { YT } from "./tokens/YT.sol"; import { Token } from "./tokens/Token.sol"; import { BaseAdapter as Adapter } from "./adapters/abstract/BaseAdapter.sol"; /// @title Sense Divider: Divide Assets in Two /// @author fedealconada + jparklev /// @notice You can use this contract to issue, combine, and redeem Sense ERC20 Principal and Yield Tokens contract Divider is Trust, ReentrancyGuard, Pausable { using SafeTransferLib for ERC20; using FixedMath for uint256; using Levels for uint256; /* ========== PUBLIC CONSTANTS ========== */ /// @notice Buffer before and after the actual maturity in which only the sponsor can settle the Series uint256 public constant SPONSOR_WINDOW = 3 hours; /// @notice Buffer after the sponsor window in which anyone can settle the Series uint256 public constant SETTLEMENT_WINDOW = 3 hours; /// @notice 5% issuance fee cap uint256 public constant ISSUANCE_FEE_CAP = 0.05e18; /* ========== PUBLIC MUTABLE STORAGE ========== */ address public periphery; /// @notice Sense community multisig address public immutable cup; /// @notice Principal/Yield tokens deployer address public immutable tokenHandler; /// @notice Permissionless flag bool public permissionless; /// @notice Guarded launch flag bool public guarded = true; /// @notice Number of adapters (including turned off) uint248 public adapterCounter; /// @notice adapter ID -> adapter address mapping(uint256 => address) public adapterAddresses; /// @notice adapter data mapping(address => AdapterMeta) public adapterMeta; /// @notice adapter -> maturity -> Series mapping(address => mapping(uint256 => Series)) public series; /// @notice adapter -> maturity -> user -> lscale (last scale) mapping(address => mapping(uint256 => mapping(address => uint256))) public lscales; /* ========== DATA STRUCTURES ========== */ struct Series { // Principal ERC20 token address pt; // Timestamp of series initialization uint48 issuance; // Yield ERC20 token address yt; // % of underlying principal initially reserved for Yield uint96 tilt; // Actor who initialized the Series address sponsor; // Tracks fees due to the series' settler uint256 reward; // Scale at issuance uint256 iscale; // Scale at maturity uint256 mscale; // Max scale value from this series' lifetime uint256 maxscale; } struct AdapterMeta { // Adapter ID uint248 id; // Adapter enabled/disabled bool enabled; // Max amount of Target allowed to be issued uint256 guard; // Adapter level uint248 level; } constructor(address _cup, address _tokenHandler) Trust(msg.sender) { cup = _cup; tokenHandler = _tokenHandler; } /* ========== MUTATIVE FUNCTIONS ========== */ /// @notice Enable an adapter /// @dev when permissionless is disabled, only the Periphery can onboard adapters /// @dev after permissionless is enabled, anyone can onboard adapters /// @param adapter Adapter's address function addAdapter(address adapter) external whenNotPaused { if (!permissionless && msg.sender != periphery) revert Errors.OnlyPermissionless(); if (adapterMeta[adapter].id > 0 && !adapterMeta[adapter].enabled) revert Errors.InvalidAdapter(); _setAdapter(adapter, true); } /// @notice Initializes a new Series /// @dev Deploys two ERC20 contracts, one for PTs and the other one for YTs /// @dev Transfers some fixed amount of stake asset to this contract /// @param adapter Adapter to associate with the Series /// @param maturity Maturity date for the new Series, in units of unix time /// @param sponsor Sponsor of the Series that puts up a token stake and receives the issuance fees function initSeries( address adapter, uint256 maturity, address sponsor ) external nonReentrant whenNotPaused returns (address pt, address yt) { if (periphery != msg.sender) revert Errors.OnlyPeriphery(); if (!adapterMeta[adapter].enabled) revert Errors.InvalidAdapter(); if (_exists(adapter, maturity)) revert Errors.DuplicateSeries(); if (!_isValid(adapter, maturity)) revert Errors.InvalidMaturity(); // Transfer stake asset stake from caller to adapter (address target, address stake, uint256 stakeSize) = Adapter(adapter).getStakeAndTarget(); // Deploy Principal & Yield Tokens for this new Series (pt, yt) = TokenHandler(tokenHandler).deploy(adapter, adapterMeta[adapter].id, maturity); // Initialize the new Series struct uint256 scale = Adapter(adapter).scale(); series[adapter][maturity].pt = pt; series[adapter][maturity].issuance = uint48(block.timestamp); series[adapter][maturity].yt = yt; series[adapter][maturity].tilt = uint96(Adapter(adapter).tilt()); series[adapter][maturity].sponsor = sponsor; series[adapter][maturity].iscale = scale; series[adapter][maturity].maxscale = scale; ERC20(stake).safeTransferFrom(msg.sender, adapter, stakeSize); emit SeriesInitialized(adapter, maturity, pt, yt, sponsor, target); } /// @notice Settles a Series and transfers the settlement reward to the caller /// @dev The Series' sponsor has a grace period where only they can settle the Series /// @dev After that, the reward becomes MEV /// @param adapter Adapter to associate with the Series /// @param maturity Maturity date for the new Series function settleSeries(address adapter, uint256 maturity) external nonReentrant whenNotPaused { if (!adapterMeta[adapter].enabled) revert Errors.InvalidAdapter(); if (!_exists(adapter, maturity)) revert Errors.SeriesDoesNotExist(); if (_settled(adapter, maturity)) revert Errors.AlreadySettled(); if (!_canBeSettled(adapter, maturity)) revert Errors.OutOfWindowBoundaries(); // The maturity scale value is all a Series needs for us to consider it "settled" uint256 mscale = Adapter(adapter).scale(); series[adapter][maturity].mscale = mscale; if (mscale > series[adapter][maturity].maxscale) { series[adapter][maturity].maxscale = mscale; } // Reward the caller for doing the work of settling the Series at around the correct time (address target, address stake, uint256 stakeSize) = Adapter(adapter).getStakeAndTarget(); ERC20(target).safeTransferFrom(adapter, msg.sender, series[adapter][maturity].reward); ERC20(stake).safeTransferFrom(adapter, msg.sender, stakeSize); emit SeriesSettled(adapter, maturity, msg.sender); } /// @notice Mint Principal & Yield Tokens of a specific Series /// @param adapter Adapter address for the Series /// @param maturity Maturity date for the Series [unix time] /// @param tBal Balance of Target to deposit /// @dev The balance of PTs and YTs minted will be the same value in units of underlying (less fees) function issue( address adapter, uint256 maturity, uint256 tBal ) external nonReentrant whenNotPaused returns (uint256 uBal) { if (!adapterMeta[adapter].enabled) revert Errors.InvalidAdapter(); if (!_exists(adapter, maturity)) revert Errors.SeriesDoesNotExist(); if (_settled(adapter, maturity)) revert Errors.IssueOnSettle(); uint256 level = adapterMeta[adapter].level; if (level.issueRestricted() && msg.sender != adapter) revert Errors.IssuanceRestricted(); ERC20 target = ERC20(Adapter(adapter).target()); // Take the issuance fee out of the deposited Target, and put it towards the settlement reward uint256 issuanceFee = Adapter(adapter).ifee(); if (issuanceFee > ISSUANCE_FEE_CAP) revert Errors.IssuanceFeeCapExceeded(); uint256 fee = tBal.fmul(issuanceFee); unchecked { // Safety: bounded by the Target's total token supply series[adapter][maturity].reward += fee; } uint256 tBalSubFee = tBal - fee; // Ensure the caller won't hit the issuance cap with this action unchecked { // Safety: bounded by the Target's total token supply if (guarded && target.balanceOf(adapter) + tBal > adapterMeta[address(adapter)].guard) revert Errors.GuardCapReached(); } // Update values on adapter Adapter(adapter).notify(msg.sender, tBalSubFee, true); uint256 scale = level.collectDisabled() ? series[adapter][maturity].iscale : Adapter(adapter).scale(); // Determine the amount of Underlying equal to the Target being sent in (the principal) uBal = tBalSubFee.fmul(scale); // If the caller has not collected on YT before, use the current scale, otherwise // use the harmonic mean of the last and the current scale value lscales[adapter][maturity][msg.sender] = lscales[adapter][maturity][msg.sender] == 0 ? scale : _reweightLScale( adapter, maturity, YT(series[adapter][maturity].yt).balanceOf(msg.sender), uBal, msg.sender, scale ); // Mint equal amounts of PT and YT Token(series[adapter][maturity].pt).mint(msg.sender, uBal); YT(series[adapter][maturity].yt).mint(msg.sender, uBal); target.safeTransferFrom(msg.sender, adapter, tBal); emit Issued(adapter, maturity, uBal, msg.sender); } /// @notice Reconstitute Target by burning PT and YT /// @dev Explicitly burns YTs before maturity, and implicitly does it at/after maturity through `_collect()` /// @param adapter Adapter address for the Series /// @param maturity Maturity date for the Series /// @param uBal Balance of PT and YT to burn function combine( address adapter, uint256 maturity, uint256 uBal ) external nonReentrant whenNotPaused returns (uint256 tBal) { if (!adapterMeta[adapter].enabled) revert Errors.InvalidAdapter(); if (!_exists(adapter, maturity)) revert Errors.SeriesDoesNotExist(); uint256 level = adapterMeta[adapter].level; if (level.combineRestricted() && msg.sender != adapter) revert Errors.CombineRestricted(); // Burn the PT Token(series[adapter][maturity].pt).burn(msg.sender, uBal); // Collect whatever excess is due uint256 collected = _collect(msg.sender, adapter, maturity, uBal, uBal, address(0)); uint256 cscale = series[adapter][maturity].mscale; bool settled = _settled(adapter, maturity); if (!settled) { // If it's not settled, then YT won't be burned automatically in `_collect()` YT(series[adapter][maturity].yt).burn(msg.sender, uBal); // If collect has been restricted, use the initial scale, otherwise use the current scale cscale = level.collectDisabled() ? series[adapter][maturity].iscale : lscales[adapter][maturity][msg.sender]; } // Convert from units of Underlying to units of Target tBal = uBal.fdiv(cscale); ERC20(Adapter(adapter).target()).safeTransferFrom(adapter, msg.sender, tBal); // Notify only when Series is not settled as when it is, the _collect() call above would trigger a _redeemYT which will call notify if (!settled) Adapter(adapter).notify(msg.sender, tBal, false); unchecked { // Safety: bounded by the Target's total token supply tBal += collected; } emit Combined(adapter, maturity, tBal, msg.sender); } /// @notice Burn PT of a Series once it's been settled /// @dev The balance of redeemable Target is a function of the change in Scale /// @param adapter Adapter address for the Series /// @param maturity Maturity date for the Series /// @param uBal Amount of PT to burn, which should be equivalent to the amount of Underlying owed to the caller function redeem( address adapter, uint256 maturity, uint256 uBal ) external nonReentrant whenNotPaused returns (uint256 tBal) { // If a Series is settled, we know that it must have existed as well, so that check is unnecessary if (!_settled(adapter, maturity)) revert Errors.NotSettled(); uint256 level = adapterMeta[adapter].level; if (level.redeemRestricted() && msg.sender == adapter) revert Errors.RedeemRestricted(); // Burn the caller's PT Token(series[adapter][maturity].pt).burn(msg.sender, uBal); // Principal Token holder's share of the principal = (1 - part of the principal that belongs to Yield) uint256 zShare = FixedMath.WAD - series[adapter][maturity].tilt; // If Principal Token are at a loss and Yield have some principal to help cover the shortfall, // take what we can from Yield Token's principal if (series[adapter][maturity].mscale.fdiv(series[adapter][maturity].maxscale) >= zShare) { tBal = (uBal * zShare) / series[adapter][maturity].mscale; } else { tBal = uBal.fdiv(series[adapter][maturity].maxscale); } if (!level.redeemHookDisabled()) { Adapter(adapter).onRedeem(uBal, series[adapter][maturity].mscale, series[adapter][maturity].maxscale, tBal); } ERC20(Adapter(adapter).target()).safeTransferFrom(adapter, msg.sender, tBal); emit PTRedeemed(adapter, maturity, tBal); } function collect( address usr, address adapter, uint256 maturity, uint256 uBalTransfer, address to ) external nonReentrant onlyYT(adapter, maturity) whenNotPaused returns (uint256 collected) { uint256 uBal = YT(msg.sender).balanceOf(usr); return _collect(usr, adapter, maturity, uBal, uBalTransfer > 0 ? uBalTransfer : uBal, to); } /// @notice Collect YT excess before, at, or after maturity /// @dev If `to` is set, we copy the lscale value from usr to this address /// @param usr User who's collecting for their YTs /// @param adapter Adapter address for the Series /// @param maturity Maturity date for the Series /// @param uBal yield Token balance /// @param uBalTransfer original transfer value /// @param to address to set the lscale value from usr function _collect( address usr, address adapter, uint256 maturity, uint256 uBal, uint256 uBalTransfer, address to ) internal returns (uint256 collected) { if (!_exists(adapter, maturity)) revert Errors.SeriesDoesNotExist(); // If the adapter is disabled, its Yield Token can only collect // if associated Series has been settled, which implies that an admin // has backfilled it if (!adapterMeta[adapter].enabled && !_settled(adapter, maturity)) revert Errors.InvalidAdapter(); Series memory _series = series[adapter][maturity]; // Get the scale value from the last time this holder collected (default to maturity) uint256 lscale = lscales[adapter][maturity][usr]; uint256 level = adapterMeta[adapter].level; if (level.collectDisabled()) { // If this Series has been settled, we ensure everyone's YT will // collect yield accrued since issuance if (_settled(adapter, maturity)) { lscale = series[adapter][maturity].iscale; // If the Series is not settled, we ensure no collections can happen } else { return 0; } } // If the Series has been settled, this should be their last collect, so redeem the user's Yield Tokens for them if (_settled(adapter, maturity)) { _redeemYT(usr, adapter, maturity, uBal); } else { // If we're not settled and we're past maturity + the sponsor window, // anyone can settle this Series so revert until someone does if (block.timestamp > maturity + SPONSOR_WINDOW) { revert Errors.CollectNotSettled(); // Otherwise, this is a valid pre-settlement collect and we need to determine the scale value } else { uint256 cscale = Adapter(adapter).scale(); // If this is larger than the largest scale we've seen for this Series, use it if (cscale > _series.maxscale) { _series.maxscale = cscale; lscales[adapter][maturity][usr] = cscale; // If not, use the previously noted max scale value } else { lscales[adapter][maturity][usr] = _series.maxscale; } } } // Determine how much underlying has accrued since the last time this user collected, in units of Target. // (Or take the last time as issuance if they haven't yet) // // Reminder: `Underlying / Scale = Target` // So the following equation is saying, for some amount of Underlying `u`: // "Balance of Target that equaled `u` at the last collection _minus_ Target that equals `u` now" // // Because maxscale must be increasing, the Target balance needed to equal `u` decreases, and that "excess" // is what Yield holders are collecting uint256 tBalNow = uBal.fdivUp(_series.maxscale); // preventive round-up towards the protocol uint256 tBalPrev = uBal.fdiv(lscale); unchecked { collected = tBalPrev > tBalNow ? tBalPrev - tBalNow : 0; } ERC20(Adapter(adapter).target()).safeTransferFrom(adapter, usr, collected); Adapter(adapter).notify(usr, collected, false); // Distribute reward tokens // If this collect is a part of a token transfer to another address, set the receiver's // last collection to a synthetic scale weighted based on the scale on their last collect, // the time elapsed, and the current scale if (to != address(0)) { uint256 ytBal = YT(_series.yt).balanceOf(to); // If receiver holds yields, we set lscale to a computed "synthetic" lscales value that, // for the updated yield balance, still assigns the correct amount of yield. lscales[adapter][maturity][to] = ytBal > 0 ? _reweightLScale(adapter, maturity, ytBal, uBalTransfer, to, _series.maxscale) : _series.maxscale; uint256 tBalTransfer = uBalTransfer.fdiv(_series.maxscale); Adapter(adapter).notify(usr, tBalTransfer, false); Adapter(adapter).notify(to, tBalTransfer, true); } series[adapter][maturity] = _series; emit Collected(adapter, maturity, collected); } /// @notice calculate the harmonic mean of the current scale and the last scale, /// weighted by amounts associated with each function _reweightLScale( address adapter, uint256 maturity, uint256 ytBal, uint256 uBal, address receiver, uint256 scale ) internal view returns (uint256) { // Target Decimals * 18 Decimals [from fdiv] / (Target Decimals * 18 Decimals [from fdiv] / 18 Decimals) // = 18 Decimals, which is the standard for scale values return (ytBal + uBal).fdiv((ytBal.fdiv(lscales[adapter][maturity][receiver]) + uBal.fdiv(scale))); } function _redeemYT( address usr, address adapter, uint256 maturity, uint256 uBal ) internal { // Burn the users's YTs YT(series[adapter][maturity].yt).burn(usr, uBal); // Default principal for a YT uint256 tBal = 0; // Principal Token holder's share of the principal = (1 - part of the principal that belongs to Yield Tokens) uint256 zShare = FixedMath.WAD - series[adapter][maturity].tilt; // If PTs are at a loss and YTs had their principal cut to help cover the shortfall, // calculate how much YTs have left if (series[adapter][maturity].mscale.fdiv(series[adapter][maturity].maxscale) >= zShare) { tBal = uBal.fdiv(series[adapter][maturity].maxscale) - (uBal * zShare) / series[adapter][maturity].mscale; ERC20(Adapter(adapter).target()).safeTransferFrom(adapter, usr, tBal); } // Always notify the Adapter of the full Target balance that will no longer // have its rewards distributed Adapter(adapter).notify(usr, uBal.fdivUp(series[adapter][maturity].maxscale), false); emit YTRedeemed(adapter, maturity, tBal); } /* ========== ADMIN ========== */ /// @notice Enable or disable a adapter /// @param adapter Adapter's address /// @param isOn Flag setting this adapter to enabled or disabled function setAdapter(address adapter, bool isOn) public requiresTrust { _setAdapter(adapter, isOn); } /// @notice Set adapter's guard /// @param adapter Adapter address /// @param cap The max target that can be deposited on the Adapter function setGuard(address adapter, uint256 cap) external requiresTrust { adapterMeta[adapter].guard = cap; emit GuardChanged(adapter, cap); } /// @notice Set guarded mode /// @param _guarded bool function setGuarded(bool _guarded) external requiresTrust { guarded = _guarded; emit GuardedChanged(_guarded); } /// @notice Set periphery's contract /// @param _periphery Target address function setPeriphery(address _periphery) external requiresTrust { periphery = _periphery; emit PeripheryChanged(_periphery); } /// @notice Set paused flag /// @param _paused boolean function setPaused(bool _paused) external requiresTrust { _paused ? _pause() : _unpause(); } /// @notice Set permissioless mode /// @param _permissionless bool function setPermissionless(bool _permissionless) external requiresTrust { permissionless = _permissionless; emit PermissionlessChanged(_permissionless); } /// @notice Backfill a Series' Scale value at maturity if keepers failed to settle it /// @param adapter Adapter's address /// @param maturity Maturity date for the Series /// @param mscale Value to set as the Series' Scale value at maturity /// @param _usrs Values to set on lscales mapping /// @param _lscales Values to set on lscales mapping function backfillScale( address adapter, uint256 maturity, uint256 mscale, address[] calldata _usrs, uint256[] calldata _lscales ) external requiresTrust { if (!_exists(adapter, maturity)) revert Errors.SeriesDoesNotExist(); uint256 cutoff = maturity + SPONSOR_WINDOW + SETTLEMENT_WINDOW; // Admin can never backfill before maturity if (block.timestamp <= cutoff) revert Errors.OutOfWindowBoundaries(); // Set user's last scale values the Series (needed for the `collect` method) for (uint256 i = 0; i < _usrs.length; i++) { lscales[adapter][maturity][_usrs[i]] = _lscales[i]; } if (mscale > 0) { Series memory _series = series[adapter][maturity]; // Set the maturity scale for the Series (needed for `redeem` methods) series[adapter][maturity].mscale = mscale; if (mscale > _series.maxscale) { series[adapter][maturity].maxscale = mscale; } (address target, address stake, uint256 stakeSize) = Adapter(adapter).getStakeAndTarget(); address stakeDst = adapterMeta[adapter].enabled ? cup : _series.sponsor; ERC20(target).safeTransferFrom(adapter, cup, _series.reward); series[adapter][maturity].reward = 0; ERC20(stake).safeTransferFrom(adapter, stakeDst, stakeSize); } emit Backfilled(adapter, maturity, mscale, _usrs, _lscales); } /* ========== INTERNAL VIEWS ========== */ function _exists(address adapter, uint256 maturity) internal view returns (bool) { return series[adapter][maturity].pt != address(0); } function _settled(address adapter, uint256 maturity) internal view returns (bool) { return series[adapter][maturity].mscale > 0; } function _canBeSettled(address adapter, uint256 maturity) internal view returns (bool) { uint256 cutoff = maturity + SPONSOR_WINDOW + SETTLEMENT_WINDOW; // If the sender is the sponsor for the Series if (msg.sender == series[adapter][maturity].sponsor) { return maturity - SPONSOR_WINDOW <= block.timestamp && cutoff >= block.timestamp; } else { return maturity + SPONSOR_WINDOW < block.timestamp && cutoff >= block.timestamp; } } function _isValid(address adapter, uint256 maturity) internal view returns (bool) { (uint256 minm, uint256 maxm) = Adapter(adapter).getMaturityBounds(); if (maturity < block.timestamp + minm || maturity > block.timestamp + maxm) return false; (, , uint256 day, uint256 hour, uint256 minute, uint256 second) = DateTime.timestampToDateTime(maturity); if (hour != 0 || minute != 0 || second != 0) return false; uint256 mode = Adapter(adapter).mode(); if (mode == 0) { return day == 1; } if (mode == 1) { return DateTime.getDayOfWeek(maturity) == 1; } return false; } /* ========== INTERNAL UTILS ========== */ function _setAdapter(address adapter, bool isOn) internal { AdapterMeta memory am = adapterMeta[adapter]; if (am.enabled == isOn) revert Errors.ExistingValue(); am.enabled = isOn; // If this adapter is being added for the first time if (isOn && am.id == 0) { am.id = ++adapterCounter; adapterAddresses[am.id] = adapter; } // Set level and target (can only be done once); am.level = uint248(Adapter(adapter).level()); adapterMeta[adapter] = am; emit AdapterChanged(adapter, am.id, isOn); } /* ========== PUBLIC GETTERS ========== */ /// @notice Returns address of Principal Token function pt(address adapter, uint256 maturity) public view returns (address) { return series[adapter][maturity].pt; } /// @notice Returns address of Yield Token function yt(address adapter, uint256 maturity) public view returns (address) { return series[adapter][maturity].yt; } function mscale(address adapter, uint256 maturity) public view returns (uint256) { return series[adapter][maturity].mscale; } /* ========== MODIFIERS ========== */ modifier onlyYT(address adapter, uint256 maturity) { if (series[adapter][maturity].yt != msg.sender) revert Errors.OnlyYT(); _; } /* ========== LOGS ========== */ /// @notice Admin event Backfilled( address indexed adapter, uint256 indexed maturity, uint256 mscale, address[] _usrs, uint256[] _lscales ); event GuardChanged(address indexed adapter, uint256 cap); event AdapterChanged(address indexed adapter, uint256 indexed id, bool indexed isOn); event PeripheryChanged(address indexed periphery); /// @notice Series lifecycle /// *---- beginning event SeriesInitialized( address adapter, uint256 indexed maturity, address pt, address yt, address indexed sponsor, address indexed target ); /// -***- middle event Issued(address indexed adapter, uint256 indexed maturity, uint256 balance, address indexed sender); event Combined(address indexed adapter, uint256 indexed maturity, uint256 balance, address indexed sender); event Collected(address indexed adapter, uint256 indexed maturity, uint256 collected); /// ----* end event SeriesSettled(address indexed adapter, uint256 indexed maturity, address indexed settler); event PTRedeemed(address indexed adapter, uint256 indexed maturity, uint256 redeemed); event YTRedeemed(address indexed adapter, uint256 indexed maturity, uint256 redeemed); /// *----* misc event GuardedChanged(bool indexed guarded); event PermissionlessChanged(bool indexed permissionless); } contract TokenHandler is Trust { /// @notice Program state address public divider; constructor() Trust(msg.sender) {} function init(address _divider) external requiresTrust { if (divider != address(0)) revert Errors.AlreadyInitialized(); divider = _divider; } function deploy( address adapter, uint248 id, uint256 maturity ) external returns (address pt, address yt) { if (msg.sender != divider) revert Errors.OnlyDivider(); ERC20 target = ERC20(Adapter(adapter).target()); uint8 decimals = target.decimals(); string memory symbol = target.symbol(); (string memory d, string memory m, string memory y) = DateTime.toDateString(maturity); string memory date = DateTime.format(maturity); string memory datestring = string(abi.encodePacked(d, "-", m, "-", y)); string memory adapterId = DateTime.uintToString(id); pt = address( new Token( string(abi.encodePacked(date, " ", symbol, " Sense Principal Token, A", adapterId)), string(abi.encodePacked("sP-", symbol, ":", datestring, ":", adapterId)), decimals, divider ) ); yt = address( new YT( adapter, maturity, string(abi.encodePacked(date, " ", symbol, " Sense Yield Token, A", adapterId)), string(abi.encodePacked("sY-", symbol, ":", datestring, ":", adapterId)), decimals, divider ) ); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.11; // External references import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol"; import { SafeTransferLib } from "@rari-capital/solmate/src/utils/SafeTransferLib.sol"; // Internal references import { Divider } from "../../../Divider.sol"; import { BaseAdapter } from "../BaseAdapter.sol"; import { IClaimer } from "../IClaimer.sol"; import { FixedMath } from "../../../external/FixedMath.sol"; import { Trust } from "@sense-finance/v1-utils/src/Trust.sol"; import { Errors } from "@sense-finance/v1-utils/src/libs/Errors.sol"; /// @notice This is meant to be used with BaseAdapter.sol abstract contract Crop is Trust { using SafeTransferLib for ERC20; using FixedMath for uint256; /// @notice Program state address public claimer; // claimer address address public reward; uint256 public share; // accumulated reward token per collected target uint256 public rewardBal; // last recorded balance of reward token uint256 public totalTarget; // total target accumulated by all users mapping(address => uint256) public tBalance; // target balance per user mapping(address => uint256) public rewarded; // reward token per user mapping(address => uint256) public reconciledAmt; // reconciled target amount per user mapping(address => mapping(uint256 => bool)) public reconciled; // whether a user has been reconciled for a given maturity event Distributed(address indexed usr, address indexed token, uint256 amount); constructor(address _divider, address _reward) { setIsTrusted(_divider, true); reward = _reward; } /// @notice Distribute the rewards tokens to the user according to their share /// @dev The reconcile amount allows us to prevent diluting other users' rewards function notify( address _usr, uint256 amt, bool join ) public virtual requiresTrust { _distribute(_usr); if (amt > 0) { if (join) { totalTarget += amt; tBalance[_usr] += amt; } else { uint256 uReconciledAmt = reconciledAmt[_usr]; if (uReconciledAmt > 0) { if (amt < uReconciledAmt) { unchecked { uReconciledAmt -= amt; } amt = 0; } else { unchecked { amt -= uReconciledAmt; } uReconciledAmt = 0; } reconciledAmt[_usr] = uReconciledAmt; } if (amt > 0) { totalTarget -= amt; tBalance[_usr] -= amt; } } } rewarded[_usr] = tBalance[_usr].fmulUp(share, FixedMath.RAY); } /// @notice Reconciles users target balances to zero by distributing rewards on their holdings, /// to avoid dilution of next Series' YT holders. /// This function should be called right after a Series matures and will save the user's YT balance /// (in target terms) on reconciledAmt[usr]. When `notify()` is triggered, we take that amount and /// subtract it from the user's target balance (`tBalance`) which will fix (or reconcile) /// his position to prevent dilution. /// @param _usrs Users to reconcile /// @param _maturities Maturities of the series that we want to reconcile users on. function reconcile(address[] calldata _usrs, uint256[] calldata _maturities) public { Divider divider = Divider(BaseAdapter(address(this)).divider()); for (uint256 j = 0; j < _maturities.length; j++) { for (uint256 i = 0; i < _usrs.length; i++) { address usr = _usrs[i]; uint256 ytBal = ERC20(divider.yt(address(this), _maturities[j])).balanceOf(usr); // We don't want to reconcile users if maturity has not been reached or if they have already been reconciled if (_maturities[j] <= block.timestamp && ytBal > 0 && !reconciled[usr][_maturities[j]]) { _distribute(usr); uint256 tBal = ytBal.fdiv(divider.lscales(address(this), _maturities[j], usr)); totalTarget -= tBal; tBalance[usr] -= tBal; reconciledAmt[usr] += tBal; // We increase reconciledAmt with the user's YT balance in target terms reconciled[usr][_maturities[j]] = true; emit Reconciled(usr, tBal, _maturities[j]); } } } } /// @notice Distributes rewarded tokens to users proportionally based on their `tBalance` /// @param _usr User to distribute reward tokens to function _distribute(address _usr) internal { _claimReward(); uint256 crop = ERC20(reward).balanceOf(address(this)) - rewardBal; if (totalTarget > 0) share += (crop.fdiv(totalTarget, FixedMath.RAY)); uint256 last = rewarded[_usr]; uint256 curr = tBalance[_usr].fmul(share, FixedMath.RAY); if (curr > last) { unchecked { ERC20(reward).safeTransfer(_usr, curr - last); } } rewardBal = ERC20(reward).balanceOf(address(this)); emit Distributed(_usr, reward, curr > last ? curr - last : 0); } /// @notice Some protocols don't airdrop reward tokens, instead users must claim them. /// This method may be overriden by child contracts to claim a protocol's rewards function _claimReward() internal virtual { if (claimer != address(0)) { ERC20 target = ERC20(BaseAdapter(address(this)).target()); uint256 tBal = ERC20(target).balanceOf(address(this)); if (tBal > 0) { // We send all the target balance to the claimer contract to it can claim rewards ERC20(target).transfer(claimer, tBal); // Make claimer to claim rewards IClaimer(claimer).claim(); // Get the target back if (ERC20(target).balanceOf(address(this)) < tBal) revert Errors.BadContractInteration(); } } } /// @notice Overrides the rewardToken address. /// @param _reward New reward token address function setRewardToken(address _reward) public requiresTrust { _claimReward(); reward = _reward; emit RewardTokenChanged(reward); } /// @notice Sets `claimer`. /// @param _claimer New claimer contract address function setClaimer(address _claimer) public requiresTrust { claimer = _claimer; emit ClaimerChanged(claimer); } /* ========== LOGS ========== */ event Reconciled(address indexed usr, uint256 tBal, uint256 maturity); event RewardTokenChanged(address indexed reward); event ClaimerChanged(address indexed claimer); }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.11; // External references import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol"; import { SafeTransferLib } from "@rari-capital/solmate/src/utils/SafeTransferLib.sol"; // Internal references import { Divider } from "../../../Divider.sol"; import { BaseAdapter } from "../BaseAdapter.sol"; import { IClaimer } from "../IClaimer.sol"; import { FixedMath } from "../../../external/FixedMath.sol"; import { Errors } from "@sense-finance/v1-utils/src/libs/Errors.sol"; import { Trust } from "@sense-finance/v1-utils/src/Trust.sol"; /// @notice This is meant to be used with BaseAdapter.sol abstract contract Crops is Trust { using SafeTransferLib for ERC20; using FixedMath for uint256; /// @notice Program state address public claimer; // claimer address uint256 public totalTarget; // total target accumulated by all users mapping(address => uint256) public tBalance; // target balance per user mapping(address => uint256) public reconciledAmt; // reconciled target amount per user mapping(address => mapping(uint256 => bool)) public reconciled; // whether a user has been reconciled for a given maturity address[] public rewardTokens; // reward tokens addresses mapping(address => Crop) public data; struct Crop { // Accumulated reward token per collected target uint256 shares; // Last recorded balance of this contract per reward token uint256 rewardedBalances; // Rewarded token per token per user mapping(address => uint256) rewarded; } constructor(address _divider, address[] memory _rewardTokens) { setIsTrusted(_divider, true); rewardTokens = _rewardTokens; } function notify( address _usr, uint256 amt, bool join ) public virtual requiresTrust { _distribute(_usr); if (amt > 0) { if (join) { totalTarget += amt; tBalance[_usr] += amt; } else { uint256 uReconciledAmt = reconciledAmt[_usr]; if (uReconciledAmt > 0) { if (amt < uReconciledAmt) { unchecked { uReconciledAmt -= amt; } amt = 0; } else { unchecked { amt -= uReconciledAmt; } uReconciledAmt = 0; } reconciledAmt[_usr] = uReconciledAmt; } if (amt > 0) { totalTarget -= amt; tBalance[_usr] -= amt; } } } for (uint256 i = 0; i < rewardTokens.length; i++) { data[rewardTokens[i]].rewarded[_usr] = tBalance[_usr].fmulUp(data[rewardTokens[i]].shares, FixedMath.RAY); } } /// @notice Reconciles users target balances to zero by distributing rewards on their holdings, /// to avoid dilution of next Series' YT holders. /// This function should be called right after a Series matures and will save the user's YT balance /// (in target terms) on reconciledAmt[usr]. When `notify()` is triggered for on a new Series, we will /// take that amount and subtract it from the user's target balance (`tBalance`) which will fix (or reconcile) /// his position to prevent dilution. /// @param _usrs Users to reconcile /// @param _maturities Maturities of the series that we want to reconcile users on. function reconcile(address[] calldata _usrs, uint256[] calldata _maturities) public { Divider divider = Divider(BaseAdapter(address(this)).divider()); for (uint256 j = 0; j < _maturities.length; j++) { for (uint256 i = 0; i < _usrs.length; i++) { address usr = _usrs[i]; uint256 ytBal = ERC20(divider.yt(address(this), _maturities[j])).balanceOf(usr); // We don't want to reconcile users if maturity has not been reached or if they have already been reconciled if (_maturities[j] <= block.timestamp && ytBal > 0 && !reconciled[usr][_maturities[j]]) { _distribute(usr); uint256 tBal = ytBal.fdiv(divider.lscales(address(this), _maturities[j], usr)); totalTarget -= tBal; tBalance[usr] -= tBal; reconciledAmt[usr] += tBal; // We increase reconciledAmt with the user's YT balance in target terms reconciled[usr][_maturities[j]] = true; emit Reconciled(usr, tBal, _maturities[j]); } } } } /// @notice Distributes rewarded tokens to users proportionally based on their `tBalance` /// @param _usr User to distribute reward tokens to function _distribute(address _usr) internal { _claimRewards(); for (uint256 i = 0; i < rewardTokens.length; i++) { uint256 crop = ERC20(rewardTokens[i]).balanceOf(address(this)) - data[rewardTokens[i]].rewardedBalances; if (totalTarget > 0) data[rewardTokens[i]].shares += (crop.fdiv(totalTarget, FixedMath.RAY)); uint256 last = data[rewardTokens[i]].rewarded[_usr]; uint256 curr = tBalance[_usr].fmul(data[rewardTokens[i]].shares, FixedMath.RAY); if (curr > last) { unchecked { ERC20(rewardTokens[i]).safeTransfer(_usr, curr - last); } } data[rewardTokens[i]].rewardedBalances = ERC20(rewardTokens[i]).balanceOf(address(this)); emit Distributed(_usr, rewardTokens[i], curr > last ? curr - last : 0); } } /// @notice Some protocols don't airdrop reward tokens, instead users must claim them. /// This method may be overriden by child contracts to claim a protocol's rewards function _claimRewards() internal virtual { if (claimer != address(0)) { ERC20 target = ERC20(BaseAdapter(address(this)).target()); uint256 tBal = ERC20(target).balanceOf(address(this)); if (tBal > 0) { // We send all the target balance to the claimer contract to it can claim rewards ERC20(target).transfer(claimer, tBal); // Make claimer to claim rewards IClaimer(claimer).claim(); // Get the target back if (ERC20(target).balanceOf(address(this)) < tBal) revert Errors.BadContractInteration(); } } } /// @notice Overrides the rewardTokens array with a new one. /// @dev Calls _claimRewards() in case the new array contains less reward tokens than the old one. /// @param _rewardTokens New reward tokens array function setRewardTokens(address[] memory _rewardTokens) public requiresTrust { _claimRewards(); rewardTokens = _rewardTokens; emit RewardTokensChanged(rewardTokens); } /// @notice Sets `claimer`. /// @param _claimer New claimer contract address function setClaimer(address _claimer) public requiresTrust { claimer = _claimer; emit ClaimerChanged(claimer); } /* ========== LOGS ========== */ event Distributed(address indexed usr, address indexed token, uint256 amount); event RewardTokensChanged(address[] indexed rewardTokens); event Reconciled(address indexed usr, uint256 tBal, uint256 maturity); event ClaimerChanged(address indexed claimer); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract Pausable is Context { /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); bool private _paused; /** * @dev Initializes the contract in unpaused state. */ constructor() { _paused = false; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { _requireNotPaused(); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { _requirePaused(); _; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Throws if the contract is paused. */ function _requireNotPaused() internal view virtual { require(!paused(), "Pausable: paused"); } /** * @dev Throws if the contract is not paused. */ function _requirePaused() internal view virtual { require(paused(), "Pausable: not paused"); } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Gas optimized reentrancy protection for smart contracts. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/ReentrancyGuard.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol) abstract contract ReentrancyGuard { uint256 private locked = 1; modifier nonReentrant() { require(locked == 1, "REENTRANCY"); locked = 2; _; locked = 1; } }
pragma solidity 0.8.11; /// @author Taken from: https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary library DateTime { uint256 constant SECONDS_PER_DAY = 24 * 60 * 60; uint256 constant SECONDS_PER_HOUR = 60 * 60; uint256 constant SECONDS_PER_MINUTE = 60; int256 constant OFFSET19700101 = 2440588; function timestampToDate(uint256 timestamp) internal pure returns ( uint256 year, uint256 month, uint256 day ) { (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); } function timestampToDateTime(uint256 timestamp) internal pure returns ( uint256 year, uint256 month, uint256 day, uint256 hour, uint256 minute, uint256 second ) { (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); uint256 secs = timestamp % SECONDS_PER_DAY; hour = secs / SECONDS_PER_HOUR; secs = secs % SECONDS_PER_HOUR; minute = secs / SECONDS_PER_MINUTE; second = secs % SECONDS_PER_MINUTE; } function toDateString(uint256 _timestamp) internal pure returns ( string memory d, string memory m, string memory y ) { (uint256 year, uint256 month, uint256 day) = timestampToDate(_timestamp); d = uintToString(day); m = uintToString(month); y = uintToString(year); // append a 0 to numbers < 10 so we should, e.g, 01 instead of just 1 if (day < 10) d = string(abi.encodePacked("0", d)); if (month < 10) m = string(abi.encodePacked("0", m)); } function format(uint256 _timestamp) internal pure returns (string memory datestring) { string[12] memory months = [ "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec" ]; (uint256 year, uint256 month, uint256 day) = timestampToDate(_timestamp); uint256 last = day % 10; string memory suffix = "th"; if (day < 11 || day > 20) { if (last == 1) suffix = "st"; if (last == 2) suffix = "nd"; if (last == 3) suffix = "rd"; } return string(abi.encodePacked(uintToString(day), suffix, " ", months[month - 1], " ", uintToString(year))); } function getDayOfWeek(uint256 timestamp) internal pure returns (uint256 dayOfWeek) { uint256 _days = timestamp / SECONDS_PER_DAY; dayOfWeek = ((_days + 3) % 7) + 1; } /// Taken from https://stackoverflow.com/questions/47129173/how-to-convert-uint-to-string-in-solidity function uintToString(uint256 _i) internal pure returns (string memory _uintAsString) { if (_i == 0) return "0"; uint256 j = _i; uint256 len; while (j != 0) { len++; j /= 10; } bytes memory bstr = new bytes(len); uint256 k = len; while (_i != 0) { k = k - 1; uint8 temp = (48 + uint8(_i - (_i / 10) * 10)); bytes1 b1 = bytes1(temp); bstr[k] = b1; _i /= 10; } return string(bstr); } // ------------------------------------------------------------------------ // Calculate the number of days from 1970/01/01 to year/month/day using // the date conversion algorithm from // http://aa.usno.navy.mil/faq/docs/JD_Formula.php // and subtracting the offset 2440588 so that 1970/01/01 is day 0 // // days = day // - 32075 // + 1461 * (year + 4800 + (month - 14) / 12) / 4 // + 367 * (month - 2 - (month - 14) / 12 * 12) / 12 // - 3 * ((year + 4900 + (month - 14) / 12) / 100) / 4 // - offset // ------------------------------------------------------------------------ function _daysFromDate( uint256 year, uint256 month, uint256 day ) internal pure returns (uint256 _days) { require(year >= 1970); int256 _year = int256(year); int256 _month = int256(month); int256 _day = int256(day); int256 __days = _day - 32075 + (1461 * (_year + 4800 + (_month - 14) / 12)) / 4 + (367 * (_month - 2 - ((_month - 14) / 12) * 12)) / 12 - (3 * ((_year + 4900 + (_month - 14) / 12) / 100)) / 4 - OFFSET19700101; _days = uint256(__days); } // ------------------------------------------------------------------------ // Calculate year/month/day from the number of days since 1970/01/01 using // the date conversion algorithm from // http://aa.usno.navy.mil/faq/docs/JD_Formula.php // and adding the offset 2440588 so that 1970/01/01 is day 0 // // int L = days + 68569 + offset // int N = 4 * L / 146097 // L = L - (146097 * N + 3) / 4 // year = 4000 * (L + 1) / 1461001 // L = L - 1461 * year / 4 + 31 // month = 80 * L / 2447 // dd = L - 2447 * month / 80 // L = month / 11 // month = month + 2 - 12 * L // year = 100 * (N - 49) + year + L // ------------------------------------------------------------------------ function _daysToDate(uint256 _days) internal pure returns ( uint256 year, uint256 month, uint256 day ) { int256 __days = int256(_days); int256 L = __days + 68569 + OFFSET19700101; int256 N = (4 * L) / 146097; L = L - (146097 * N + 3) / 4; int256 _year = (4000 * (L + 1)) / 1461001; L = L - (1461 * _year) / 4 + 31; int256 _month = (80 * L) / 2447; int256 _day = L - (2447 * _month) / 80; L = _month / 11; _month = _month + 2 - 12 * L; _year = 100 * (N - 49) + _year + L; year = uint256(_year); month = uint256(_month); day = uint256(_day); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.7.0; library Levels { uint256 private constant _INIT_BIT = 0x1; uint256 private constant _ISSUE_BIT = 0x2; uint256 private constant _COMBINE_BIT = 0x4; uint256 private constant _COLLECT_BIT = 0x8; uint256 private constant _REDEEM_BIT = 0x10; uint256 private constant _REDEEM_HOOK_BIT = 0x20; function initRestricted(uint256 level) internal pure returns (bool) { return level & _INIT_BIT != _INIT_BIT; } function issueRestricted(uint256 level) internal pure returns (bool) { return level & _ISSUE_BIT != _ISSUE_BIT; } function combineRestricted(uint256 level) internal pure returns (bool) { return level & _COMBINE_BIT != _COMBINE_BIT; } function collectDisabled(uint256 level) internal pure returns (bool) { return level & _COLLECT_BIT != _COLLECT_BIT; } function redeemRestricted(uint256 level) internal pure returns (bool) { return level & _REDEEM_BIT != _REDEEM_BIT; } function redeemHookDisabled(uint256 level) internal pure returns (bool) { return level & _REDEEM_HOOK_BIT != _REDEEM_HOOK_BIT; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.11; // Internal references import { Divider } from "../Divider.sol"; import { Token } from "./Token.sol"; /// @title Yield Token /// @notice Strips off excess before every transfer contract YT is Token { address public immutable adapter; address public immutable divider; uint256 public immutable maturity; constructor( address _adapter, uint256 _maturity, string memory _name, string memory _symbol, uint8 _decimals, address _divider ) Token(_name, _symbol, _decimals, _divider) { adapter = _adapter; maturity = _maturity; divider = _divider; } function collect() external returns (uint256 _collected) { return Divider(divider).collect(msg.sender, adapter, maturity, 0, address(0)); } function transfer(address to, uint256 value) public override returns (bool) { Divider(divider).collect(msg.sender, adapter, maturity, value, to); return super.transfer(to, value); } function transferFrom( address from, address to, uint256 value ) public override returns (bool) { if (value > 0) Divider(divider).collect(from, adapter, maturity, value, to); return super.transferFrom(from, to, value); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.11; // External references import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol"; // Internal references import { Trust } from "@sense-finance/v1-utils/src/Trust.sol"; /// @title Base Token contract Token is ERC20, Trust { constructor( string memory _name, string memory _symbol, uint8 _decimals, address _trusted ) ERC20(_name, _symbol, _decimals) Trust(_trusted) {} /// @param usr The address to send the minted tokens /// @param amount The amount to be minted function mint(address usr, uint256 amount) public requiresTrust { _mint(usr, amount); } /// @param usr The address from where to burn tokens from /// @param amount The amount to be burned function burn(address usr, uint256 amount) public requiresTrust { _burn(usr, amount); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.11; interface IClaimer { /// @dev Claims rewards on protocol. function claim() external; }
{ "optimizer": { "enabled": true, "runs": 15000 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "metadata": { "useLiteralContent": true }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"_divider","type":"address"},{"internalType":"address","name":"_target","type":"address"},{"internalType":"address","name":"_rewardsRecipient","type":"address"},{"internalType":"uint128","name":"_ifee","type":"uint128"},{"components":[{"internalType":"address","name":"oracle","type":"address"},{"internalType":"address","name":"stake","type":"address"},{"internalType":"uint256","name":"stakeSize","type":"uint256"},{"internalType":"uint256","name":"minm","type":"uint256"},{"internalType":"uint256","name":"maxm","type":"uint256"},{"internalType":"uint64","name":"tilt","type":"uint64"},{"internalType":"uint48","name":"level","type":"uint48"},{"internalType":"uint16","name":"mode","type":"uint16"}],"internalType":"struct BaseAdapter.AdapterParams","name":"_adapterParams","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"FlashCallbackFailed","type":"error"},{"inputs":[],"name":"InvalidPrice","type":"error"},{"inputs":[],"name":"OnlyPeriphery","type":"error"},{"inputs":[],"name":"TokenNotSupported","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardsClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldRecipient","type":"address"},{"indexed":true,"internalType":"address","name":"newRecipient","type":"address"}],"name":"RewardsRecipientChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"bool","name":"trusted","type":"bool"}],"name":"UserTrustUpdated","type":"event"},{"inputs":[],"name":"BASE_UINT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CALLBACK_SUCCESS","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RARI_MASTER_ORACLE","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SCALE_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"adapterParams","outputs":[{"internalType":"address","name":"oracle","type":"address"},{"internalType":"address","name":"stake","type":"address"},{"internalType":"uint256","name":"stakeSize","type":"uint256"},{"internalType":"uint256","name":"minm","type":"uint256"},{"internalType":"uint256","name":"maxm","type":"uint256"},{"internalType":"uint64","name":"tilt","type":"uint64"},{"internalType":"uint48","name":"level","type":"uint48"},{"internalType":"uint16","name":"mode","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"divider","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"extractToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"flashFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC3156FlashBorrower","name":"receiver","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"flashLoan","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getMaturityBounds","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStakeAndTarget","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUnderlyingPrice","outputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ifee","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isTrusted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"level","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"maxFlashLoan","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mode","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"notify","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"onRedeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardsRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"scale","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"scaleStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"bool","name":"trusted","type":"bool"}],"name":"setIsTrusted","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"}],"name":"setRewardsRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"target","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tilt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"underlying","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"unwrapTarget","outputs":[{"internalType":"uint256","name":"_assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"wrapUnderlying","outputs":[{"internalType":"uint256","name":"_shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
6101406040523480156200001257600080fd5b50604051620022f6380380620022f6833981016040819052620000359162000626565b8484856001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000076573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200009c919062000740565b3360008181526020818152604091829020805460ff19166001908117909155915191825288928892889282917fe95aec380cae16330d146d5499ef7db6f3657e477104a733e771bc09e500d986910160405180910390a2506001600160a01b03808716608090815286821660a081815287841660c0908152600180548987166001600160a01b0319918216179091556001600160801b03881660e09081528751600280549189169184169190911790556020880151600380549190981692169190911790955560408087015160049081556060880151600555948701516006559186015160078054928801519688015161ffff16600160701b0261ffff60701b1965ffffffffffff90981668010000000000000000026001600160701b03199094166001600160401b039093169290921792909217959095169490941790935582516306fdde0360e01b8152925190926306fdde0392818101926000929091908290030181865afa15801562000216573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262000240919081019062000796565b6040516020016200025291906200084e565b6040516020818303038152906040526008908051906020019062000278929190620004d1565b50846001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa158015620002b8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620002e2919081019062000796565b604051602001620002f491906200087c565b604051602081830303815290604052600990805190602001906200031a929190620004d1565b5062000344608051600019876001600160a01b03166200044f60201b620011bf179092919060201c565b6200037160805160001983602001516001600160a01b03166200044f60201b620011bf179092919060201c565b505050505050600060a0516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015620003ba573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620003e09190620008aa565b60ff169050620003f281600a620009e4565b6101005262000403816012620009f2565b6200041090600a620009e4565b61012081815250506200044360a05160001960c0516001600160a01b03166200044f60201b620011bf179092919060201c565b50505050505062000a49565b600060405163095ea7b360e01b8152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080620004cb5760405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b604482015260640160405180910390fd5b50505050565b828054620004df9062000a0c565b90600052602060002090601f0160209004810192826200050357600085556200054e565b82601f106200051e57805160ff19168380011785556200054e565b828001600101855582156200054e579182015b828111156200054e57825182559160200191906001019062000531565b506200055c92915062000560565b5090565b5b808211156200055c576000815560010162000561565b6001600160a01b03811681146200058d57600080fd5b50565b80516200059d8162000577565b919050565b634e487b7160e01b600052604160045260246000fd5b60405161010081016001600160401b0381118282101715620005de57620005de620005a2565b60405290565b80516001600160401b03811681146200059d57600080fd5b805165ffffffffffff811681146200059d57600080fd5b805161ffff811681146200059d57600080fd5b60008060008060008587036101808112156200064157600080fd5b86516200064e8162000577565b6020880151909650620006618162000577565b6040880151909550620006748162000577565b60608801519094506001600160801b03811681146200069257600080fd5b9250610100607f198201811315620006a957600080fd5b620006b3620005b8565b91506080880151620006c58162000577565b8252620006d560a0890162000590565b602083015260c0880151604083015260e088015160608301528701516080820152620007056101208801620005e4565b60a0820152620007196101408801620005fc565b60c08201526200072d610160880162000613565b60e0820152809150509295509295909350565b6000602082840312156200075357600080fd5b8151620007608162000577565b9392505050565b60005b83811015620007845781810151838201526020016200076a565b83811115620004cb5750506000910152565b600060208284031215620007a957600080fd5b81516001600160401b0380821115620007c157600080fd5b818401915084601f830112620007d657600080fd5b815181811115620007eb57620007eb620005a2565b604051601f8201601f19908116603f01168101908382118183101715620008165762000816620005a2565b816040528281528760208487010111156200083057600080fd5b6200084383602083016020880162000767565b979650505050505050565b600082516200086281846020870162000767565b671020b230b83a32b960c11b920191825250600801919050565b600082516200089081846020870162000767565b6716b0b230b83a32b960c11b920191825250600801919050565b600060208284031215620008bd57600080fd5b815160ff811681146200076057600080fd5b634e487b7160e01b600052601160045260246000fd5b600181815b80851115620009265781600019048211156200090a576200090a620008cf565b808516156200091857918102915b93841c9390800290620008ea565b509250929050565b6000826200093f57506001620009de565b816200094e57506000620009de565b8160018114620009675760028114620009725762000992565b6001915050620009de565b60ff841115620009865762000986620008cf565b50506001821b620009de565b5060208310610133831016604e8410600b8410161715620009b7575081810a620009de565b620009c38383620008e5565b8060001904821115620009da57620009da620008cf565b0290505b92915050565b60006200076083836200092e565b60008282101562000a075762000a07620008cf565b500390565b600181811c9082168062000a2157607f821691505b6020821081141562000a4357634e487b7160e01b600052602260045260246000fd5b50919050565b60805160a05160c05160e05161010051610120516117fb62000afb600039600081816105b2015261093501526000818161058b015261090c0152600061053101526000818161048901528181610a260152610af2015260008181610352015281816105d90152818161096c01528181610b4f01528181610c1701528181610d6a01528181610de001528181610ec801528181610eff01526111330152600081816103ab0152610c7601526117fb6000f3fe608060405234801561001057600080fd5b50600436106101e55760003560e01c806364c56e3c1161010f578063c39a3b29116100a2578063d9d98ce411610071578063d9d98ce4146105fb578063f51e181a1461031b578063f60c05ce1461060e578063ff2a7d301461062957600080fd5b8063c39a3b2914610574578063cce5938614610586578063ce4b5bbe146105ad578063d4b83992146105d457600080fd5b806395d89b41116100de57806395d89b41146104ee57806396d64879146104f6578063b484972a14610519578063b8c15a9f1461052c57600080fd5b806364c56e3c146104695780636f307dc3146104845780636fd5ae15146104ab5780638237e538146104c757600080fd5b8063295a52121161018757806351c39ea11161015657806351c39ea11461040d5780635cffe9de14610420578063608c44d614610443578063613255ab1461045657600080fd5b8063295a521214610388578063378efa37146103a6578063468f02d2146103f25780634aae9fed146103fa57600080fd5b806314282f58116101c357806314282f58146102f45780631a54259c14610308578063226778491461031b57806327b327d01461033157600080fd5b806306fdde03146101ea578063083118d5146102085780631393916a146102df575b600080fd5b6101f2610649565b6040516101ff91906113f7565b60405180910390f35b6002546003546004546005546006546007546102769573ffffffffffffffffffffffffffffffffffffffff90811695169392919067ffffffffffffffff81169068010000000000000000810465ffffffffffff16906e010000000000000000000000000000900461ffff1688565b6040805173ffffffffffffffffffffffffffffffffffffffff998a168152989097166020890152958701949094526060860192909252608085015267ffffffffffffffff1660a084015265ffffffffffff1660c083015261ffff1660e0820152610100016101ff565b6102f26102ed3660046114a4565b6106d7565b005b6102f26103023660046114d9565b50505050565b6102f261031636600461150b565b6107dd565b6103236108e4565b6040519081526020016101ff565b6003546004546040805173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000008116825290931660208401528201526060016101ff565b6007546e010000000000000000000000000000900461ffff16610323565b6103cd7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101ff565b6103236109e6565b61032361040836600461152f565b610ad6565b61032361041b36600461152f565b610bd8565b61043361042e366004611548565b610c5b565b60405190151581526020016101ff565b6102f261045136600461150b565b610efd565b61032361046436600461150b565b6110b5565b600554600654604080519283526020830191909152016101ff565b6103cd7f000000000000000000000000000000000000000000000000000000000000000081565b60075468010000000000000000900465ffffffffffff16610323565b6103237f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd981565b6101f2611122565b61043361050436600461150b565b60006020819052908152604090205460ff1681565b6102f26105273660046115e7565b505050565b6105537f000000000000000000000000000000000000000000000000000000000000000081565b6040516fffffffffffffffffffffffffffffffff90911681526020016101ff565b60075467ffffffffffffffff16610323565b6103237f000000000000000000000000000000000000000000000000000000000000000081565b6103237f000000000000000000000000000000000000000000000000000000000000000081565b6103cd7f000000000000000000000000000000000000000000000000000000000000000081565b610323610609366004611625565b61112f565b6103cd731887118e49e0f4a78bd71b792a49de03504a764d81565b6001546103cd9073ffffffffffffffffffffffffffffffffffffffff1681565b6008805461065690611651565b80601f016020809104026020016040519081016040528092919081815260200182805461068290611651565b80156106cf5780601f106106a4576101008083540402835291602001916106cf565b820191906000526020600020905b8154815290600101906020018083116106b257829003601f168201915b505050505081565b3360009081526020819052604090205460ff16610755576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e54525553544544000000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff82166000818152602081815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001685151590811790915591519182527fe95aec380cae16330d146d5499ef7db6f3657e477104a733e771bc09e500d986910160405180910390a25050565b3360009081526020819052604090205460ff16610856576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e545255535445440000000000000000000000000000000000000000000000604482015260640161074c565b60015460405173ffffffffffffffffffffffffffffffffffffffff8084169216907f952c654458751f95c1440bdb1537809137b3de28466b80ad096a711707ae987a90600090a3600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6040517f07a2d13a0000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000060048201526000907f00000000000000000000000000000000000000000000000000000000000000009073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906307a2d13a90602401602060405180830381865afa1580156109b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109d791906116a5565b6109e191906116be565b905090565b6002546040517faea9107800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000081166004830152600092169063aea9107890602401602060405180830381865afa158015610a77573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a9b91906116a5565b905080610ad3576040517ebfc92100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b90565b6000610b1a73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016333085611278565b6040517f6e553f65000000000000000000000000000000000000000000000000000000008152600481018390523360248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690636e553f65906044015b6020604051808303816000875af1158015610bae573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bd291906116a5565b92915050565b6040517fba08765200000000000000000000000000000000000000000000000000000000815260048101829052336024820181905260448201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063ba08765290606401610b8f565b60003373ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166377aace1a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610cdf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d039190611722565b73ffffffffffffffffffffffffffffffffffffffff1614610d50576040517ffb02114900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610d9173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016878661133e565b6040517f23e30c8b00000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff8816906323e30c8b90610e109033907f0000000000000000000000000000000000000000000000000000000000000000908a9087908b908b9060040161173f565b6020604051808303816000875af1158015610e2f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e5391906116a5565b90507f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd98114610eae576040517f207df21c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ef073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016883088611278565b5060019695505050505050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161480610f71575060035473ffffffffffffffffffffffffffffffffffffffff8281169116145b15610fa8576040517f3dd1b30500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152819060009073ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa158015611017573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061103b91906116a5565b6001549091506110659073ffffffffffffffffffffffffffffffffffffffff84811691168361133e565b600154604051829173ffffffffffffffffffffffffffffffffffffffff90811691908616907f9310ccfcb8de723f578a9e4282ea9f521f05ae40dc08f3068dfad528a65ee3c790600090a4505050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa158015610bae573d6000803e3d6000fd5b6009805461065690611651565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16146111b6576040517f3dd1b30500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50600092915050565b60006040517f095ea7b3000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080610302576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f415050524f56455f4641494c4544000000000000000000000000000000000000604482015260640161074c565b60006040517f23b872dd0000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201526020600060648360008a5af13d15601f3d1160016000511416171691505080611337576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c4544000000000000000000000000604482015260640161074c565b5050505050565b60006040517fa9059cbb000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080610302576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c45440000000000000000000000000000000000604482015260640161074c565b600060208083528351808285015260005b8181101561142457858101830151858201604001528201611408565b81811115611436576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461148c57600080fd5b50565b8035801515811461149f57600080fd5b919050565b600080604083850312156114b757600080fd5b82356114c28161146a565b91506114d06020840161148f565b90509250929050565b600080600080608085870312156114ef57600080fd5b5050823594602084013594506040840135936060013592509050565b60006020828403121561151d57600080fd5b81356115288161146a565b9392505050565b60006020828403121561154157600080fd5b5035919050565b60008060008060006080868803121561156057600080fd5b853561156b8161146a565b9450602086013561157b8161146a565b935060408601359250606086013567ffffffffffffffff8082111561159f57600080fd5b818801915088601f8301126115b357600080fd5b8135818111156115c257600080fd5b8960208285010111156115d457600080fd5b9699959850939650602001949392505050565b6000806000606084860312156115fc57600080fd5b83356116078161146a565b92506020840135915061161c6040850161148f565b90509250925092565b6000806040838503121561163857600080fd5b82356116438161146a565b946020939093013593505050565b600181811c9082168061166557607f821691505b6020821081141561169f577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b6000602082840312156116b757600080fd5b5051919050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561171d577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b500290565b60006020828403121561173457600080fd5b81516115288161146a565b600073ffffffffffffffffffffffffffffffffffffffff808916835280881660208401525085604083015284606083015260a060808301528260a0830152828460c0840137600060c0848401015260c07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f850116830101905097965050505050505056fea2646970667358221220e00b8665331451c6068edd5514c063abf1108e8576e16f2629c9417f17bb852264736f6c634300080b003300000000000000000000000086ba3e96be68563e41c2f5769f1af9faf758e6e000000000000000000000000036f8d0d0573ae92326827c4a82fe4ce4c244cab6000000000000000000000000dd76360c26eaf63afcf3a8d2c0121f13ae864d5700000000000000000000000000000000000000000000000000038d7ea4c6800000000000000000000000000011d341d35bf95654bc7a9db59dbc557ccb4ea101000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000003782dace9d9000000000000000000000000000000000000000000000000000000000000002820a80000000000000000000000000000000000000000000000000000000012cf4ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f0000000000000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101e55760003560e01c806364c56e3c1161010f578063c39a3b29116100a2578063d9d98ce411610071578063d9d98ce4146105fb578063f51e181a1461031b578063f60c05ce1461060e578063ff2a7d301461062957600080fd5b8063c39a3b2914610574578063cce5938614610586578063ce4b5bbe146105ad578063d4b83992146105d457600080fd5b806395d89b41116100de57806395d89b41146104ee57806396d64879146104f6578063b484972a14610519578063b8c15a9f1461052c57600080fd5b806364c56e3c146104695780636f307dc3146104845780636fd5ae15146104ab5780638237e538146104c757600080fd5b8063295a52121161018757806351c39ea11161015657806351c39ea11461040d5780635cffe9de14610420578063608c44d614610443578063613255ab1461045657600080fd5b8063295a521214610388578063378efa37146103a6578063468f02d2146103f25780634aae9fed146103fa57600080fd5b806314282f58116101c357806314282f58146102f45780631a54259c14610308578063226778491461031b57806327b327d01461033157600080fd5b806306fdde03146101ea578063083118d5146102085780631393916a146102df575b600080fd5b6101f2610649565b6040516101ff91906113f7565b60405180910390f35b6002546003546004546005546006546007546102769573ffffffffffffffffffffffffffffffffffffffff90811695169392919067ffffffffffffffff81169068010000000000000000810465ffffffffffff16906e010000000000000000000000000000900461ffff1688565b6040805173ffffffffffffffffffffffffffffffffffffffff998a168152989097166020890152958701949094526060860192909252608085015267ffffffffffffffff1660a084015265ffffffffffff1660c083015261ffff1660e0820152610100016101ff565b6102f26102ed3660046114a4565b6106d7565b005b6102f26103023660046114d9565b50505050565b6102f261031636600461150b565b6107dd565b6103236108e4565b6040519081526020016101ff565b6003546004546040805173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000036f8d0d0573ae92326827c4a82fe4ce4c244cab68116825290931660208401528201526060016101ff565b6007546e010000000000000000000000000000900461ffff16610323565b6103cd7f00000000000000000000000086ba3e96be68563e41c2f5769f1af9faf758e6e081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101ff565b6103236109e6565b61032361040836600461152f565b610ad6565b61032361041b36600461152f565b610bd8565b61043361042e366004611548565b610c5b565b60405190151581526020016101ff565b6102f261045136600461150b565b610efd565b61032361046436600461150b565b6110b5565b600554600654604080519283526020830191909152016101ff565b6103cd7f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f81565b60075468010000000000000000900465ffffffffffff16610323565b6103237f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd981565b6101f2611122565b61043361050436600461150b565b60006020819052908152604090205460ff1681565b6102f26105273660046115e7565b505050565b6105537f00000000000000000000000000000000000000000000000000038d7ea4c6800081565b6040516fffffffffffffffffffffffffffffffff90911681526020016101ff565b60075467ffffffffffffffff16610323565b6103237f0000000000000000000000000000000000000000000000000de0b6b3a764000081565b6103237f000000000000000000000000000000000000000000000000000000000000000181565b6103cd7f00000000000000000000000036f8d0d0573ae92326827c4a82fe4ce4c244cab681565b610323610609366004611625565b61112f565b6103cd731887118e49e0f4a78bd71b792a49de03504a764d81565b6001546103cd9073ffffffffffffffffffffffffffffffffffffffff1681565b6008805461065690611651565b80601f016020809104026020016040519081016040528092919081815260200182805461068290611651565b80156106cf5780601f106106a4576101008083540402835291602001916106cf565b820191906000526020600020905b8154815290600101906020018083116106b257829003601f168201915b505050505081565b3360009081526020819052604090205460ff16610755576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e54525553544544000000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff82166000818152602081815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001685151590811790915591519182527fe95aec380cae16330d146d5499ef7db6f3657e477104a733e771bc09e500d986910160405180910390a25050565b3360009081526020819052604090205460ff16610856576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e545255535445440000000000000000000000000000000000000000000000604482015260640161074c565b60015460405173ffffffffffffffffffffffffffffffffffffffff8084169216907f952c654458751f95c1440bdb1537809137b3de28466b80ad096a711707ae987a90600090a3600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6040517f07a2d13a0000000000000000000000000000000000000000000000000000000081527f0000000000000000000000000000000000000000000000000de0b6b3a764000060048201526000907f00000000000000000000000000000000000000000000000000000000000000019073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000036f8d0d0573ae92326827c4a82fe4ce4c244cab616906307a2d13a90602401602060405180830381865afa1580156109b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109d791906116a5565b6109e191906116be565b905090565b6002546040517faea9107800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f81166004830152600092169063aea9107890602401602060405180830381865afa158015610a77573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a9b91906116a5565b905080610ad3576040517ebfc92100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b90565b6000610b1a73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f16333085611278565b6040517f6e553f65000000000000000000000000000000000000000000000000000000008152600481018390523360248201527f00000000000000000000000036f8d0d0573ae92326827c4a82fe4ce4c244cab673ffffffffffffffffffffffffffffffffffffffff1690636e553f65906044015b6020604051808303816000875af1158015610bae573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bd291906116a5565b92915050565b6040517fba08765200000000000000000000000000000000000000000000000000000000815260048101829052336024820181905260448201526000907f00000000000000000000000036f8d0d0573ae92326827c4a82fe4ce4c244cab673ffffffffffffffffffffffffffffffffffffffff169063ba08765290606401610b8f565b60003373ffffffffffffffffffffffffffffffffffffffff167f00000000000000000000000086ba3e96be68563e41c2f5769f1af9faf758e6e073ffffffffffffffffffffffffffffffffffffffff166377aace1a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610cdf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d039190611722565b73ffffffffffffffffffffffffffffffffffffffff1614610d50576040517ffb02114900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610d9173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000036f8d0d0573ae92326827c4a82fe4ce4c244cab616878661133e565b6040517f23e30c8b00000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff8816906323e30c8b90610e109033907f00000000000000000000000036f8d0d0573ae92326827c4a82fe4ce4c244cab6908a9087908b908b9060040161173f565b6020604051808303816000875af1158015610e2f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e5391906116a5565b90507f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd98114610eae576040517f207df21c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ef073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000036f8d0d0573ae92326827c4a82fe4ce4c244cab616883088611278565b5060019695505050505050565b7f00000000000000000000000036f8d0d0573ae92326827c4a82fe4ce4c244cab673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161480610f71575060035473ffffffffffffffffffffffffffffffffffffffff8281169116145b15610fa8576040517f3dd1b30500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152819060009073ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa158015611017573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061103b91906116a5565b6001549091506110659073ffffffffffffffffffffffffffffffffffffffff84811691168361133e565b600154604051829173ffffffffffffffffffffffffffffffffffffffff90811691908616907f9310ccfcb8de723f578a9e4282ea9f521f05ae40dc08f3068dfad528a65ee3c790600090a4505050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa158015610bae573d6000803e3d6000fd5b6009805461065690611651565b60007f00000000000000000000000036f8d0d0573ae92326827c4a82fe4ce4c244cab673ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16146111b6576040517f3dd1b30500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50600092915050565b60006040517f095ea7b3000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080610302576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f415050524f56455f4641494c4544000000000000000000000000000000000000604482015260640161074c565b60006040517f23b872dd0000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201526020600060648360008a5af13d15601f3d1160016000511416171691505080611337576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c4544000000000000000000000000604482015260640161074c565b5050505050565b60006040517fa9059cbb000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080610302576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c45440000000000000000000000000000000000604482015260640161074c565b600060208083528351808285015260005b8181101561142457858101830151858201604001528201611408565b81811115611436576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461148c57600080fd5b50565b8035801515811461149f57600080fd5b919050565b600080604083850312156114b757600080fd5b82356114c28161146a565b91506114d06020840161148f565b90509250929050565b600080600080608085870312156114ef57600080fd5b5050823594602084013594506040840135936060013592509050565b60006020828403121561151d57600080fd5b81356115288161146a565b9392505050565b60006020828403121561154157600080fd5b5035919050565b60008060008060006080868803121561156057600080fd5b853561156b8161146a565b9450602086013561157b8161146a565b935060408601359250606086013567ffffffffffffffff8082111561159f57600080fd5b818801915088601f8301126115b357600080fd5b8135818111156115c257600080fd5b8960208285010111156115d457600080fd5b9699959850939650602001949392505050565b6000806000606084860312156115fc57600080fd5b83356116078161146a565b92506020840135915061161c6040850161148f565b90509250925092565b6000806040838503121561163857600080fd5b82356116438161146a565b946020939093013593505050565b600181811c9082168061166557607f821691505b6020821081141561169f577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b6000602082840312156116b757600080fd5b5051919050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561171d577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b500290565b60006020828403121561173457600080fd5b81516115288161146a565b600073ffffffffffffffffffffffffffffffffffffffff808916835280881660208401525085604083015284606083015260a060808301528260a0830152828460c0840137600060c0848401015260c07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f850116830101905097965050505050505056fea2646970667358221220e00b8665331451c6068edd5514c063abf1108e8576e16f2629c9417f17bb852264736f6c634300080b0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000086ba3e96be68563e41c2f5769f1af9faf758e6e000000000000000000000000036f8d0d0573ae92326827c4a82fe4ce4c244cab6000000000000000000000000dd76360c26eaf63afcf3a8d2c0121f13ae864d5700000000000000000000000000000000000000000000000000038d7ea4c6800000000000000000000000000011d341d35bf95654bc7a9db59dbc557ccb4ea101000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000003782dace9d9000000000000000000000000000000000000000000000000000000000000002820a80000000000000000000000000000000000000000000000000000000012cf4ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f0000000000000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : _divider (address): 0x86bA3E96Be68563E41c2f5769F1AF9fAf758e6E0
Arg [1] : _target (address): 0x36F8d0D0573ae92326827C4a82Fe4CE4C244cAb6
Arg [2] : _rewardsRecipient (address): 0xDd76360C26Eaf63AFCF3a8d2c0121F13AE864D57
Arg [3] : _ifee (uint128): 1000000000000000
Arg [4] : _adapterParams (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
-----Encoded View---------------
12 Constructor Arguments found :
Arg [0] : 00000000000000000000000086ba3e96be68563e41c2f5769f1af9faf758e6e0
Arg [1] : 00000000000000000000000036f8d0d0573ae92326827c4a82fe4ce4c244cab6
Arg [2] : 000000000000000000000000dd76360c26eaf63afcf3a8d2c0121f13ae864d57
Arg [3] : 00000000000000000000000000000000000000000000000000038d7ea4c68000
Arg [4] : 00000000000000000000000011d341d35bf95654bc7a9db59dbc557ccb4ea101
Arg [5] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Arg [6] : 00000000000000000000000000000000000000000000000003782dace9d90000
Arg [7] : 00000000000000000000000000000000000000000000000000000000002820a8
Arg [8] : 0000000000000000000000000000000000000000000000000000000012cf4ec0
Arg [9] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [10] : 000000000000000000000000000000000000000000000000000000000000001f
Arg [11] : 0000000000000000000000000000000000000000000000000000000000000000
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
[ 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.