Source Code
Latest 25 from a total of 79 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Route Frax To Ti... | 16776501 | 336 days ago | IN | 0 FRAX | 0.00000012 | ||||
| Route Frax To Ti... | 16152420 | 350 days ago | IN | 0 FRAX | 0.00000041 | ||||
| Route Frax To Ti... | 14955171 | 378 days ago | IN | 0 FRAX | 0.00000061 | ||||
| Route Frax To Ti... | 14491959 | 389 days ago | IN | 0 FRAX | 0.0000088 | ||||
| Route Frax To Ti... | 13476785 | 412 days ago | IN | 0 FRAX | 0.00000593 | ||||
| Route Frax To Ti... | 13200148 | 419 days ago | IN | 0 FRAX | 0.00000276 | ||||
| Route Frax To Ti... | 12452289 | 436 days ago | IN | 0 FRAX | 0.00000262 | ||||
| Route Frax To Ti... | 12452263 | 436 days ago | IN | 0 FRAX | 0.00000254 | ||||
| Route Frax To Ti... | 12452242 | 436 days ago | IN | 0 FRAX | 0.00000249 | ||||
| Route Frax To Ti... | 12452213 | 436 days ago | IN | 0 FRAX | 0.00000262 | ||||
| Route Frax To Ti... | 12452151 | 436 days ago | IN | 0 FRAX | 0.00000252 | ||||
| Route Frax To Ti... | 12452086 | 436 days ago | IN | 0 FRAX | 0.00000246 | ||||
| Route Frax To Ti... | 12246679 | 441 days ago | IN | 0 FRAX | 0.00000304 | ||||
| Route Frax To Ti... | 12222648 | 441 days ago | IN | 0 FRAX | 0.00000404 | ||||
| Route Frax To Ti... | 12222514 | 441 days ago | IN | 0 FRAX | 0.00000414 | ||||
| Route Frax To Ti... | 11657111 | 454 days ago | IN | 0 FRAX | 0.00000172 | ||||
| Route Frax To Ti... | 11656825 | 454 days ago | IN | 0 FRAX | 0.00000211 | ||||
| Route Frax To Ti... | 11383039 | 461 days ago | IN | 0 FRAX | 0.00000084 | ||||
| Route Frax To Ti... | 11153224 | 466 days ago | IN | 0 FRAX | 0.00000152 | ||||
| Route Frax To Ti... | 11005657 | 470 days ago | IN | 0 FRAX | 0.00000159 | ||||
| Route Frax To Ti... | 11005633 | 470 days ago | IN | 0 FRAX | 0.00000173 | ||||
| Route Frax To Ti... | 10587949 | 479 days ago | IN | 0 FRAX | 0.00000182 | ||||
| Route Frax To Ti... | 10587747 | 479 days ago | IN | 0 FRAX | 0.00000242 | ||||
| Route Frax To Ti... | 10587715 | 479 days ago | IN | 0 FRAX | 0.00000257 | ||||
| Route Frax To Ti... | 10412604 | 483 days ago | IN | 0 FRAX | 0.00000186 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
Contract Name:
FRAXToFXBLockerRouter
Compiler Version
v0.8.26+commit.8a97fa7a
Contract Source Code (Solidity)
/**
*Submitted for verification at fraxscan.com on 2024-08-13
*/
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.0;
// node_modules/@openzeppelin/contracts/security/ReentrancyGuard.sol
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}
// node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
}
// node_modules/@openzeppelin/contracts/utils/Context.sol
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
/**
* @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;
}
}
// node_modules/@openzeppelin/contracts/utils/math/Math.sol
// OpenZeppelin Contracts (last updated v4.7.0) (utils/math/Math.sol)
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1);
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator,
Rounding rounding
) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. It the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`.
// We also know that `k`, the position of the most significant bit, is such that `msb(a) = 2**k`.
// This gives `2**k < a <= 2**(k+1)` → `2**(k/2) <= sqrt(a) < 2 ** (k/2+1)`.
// Using an algorithm similar to the msb conmputation, we are able to compute `result = 2**(k/2)` which is a
// good first aproximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1;
uint256 x = a;
if (x >> 128 > 0) {
x >>= 128;
result <<= 64;
}
if (x >> 64 > 0) {
x >>= 64;
result <<= 32;
}
if (x >> 32 > 0) {
x >>= 32;
result <<= 16;
}
if (x >> 16 > 0) {
x >>= 16;
result <<= 8;
}
if (x >> 8 > 0) {
x >>= 8;
result <<= 4;
}
if (x >> 4 > 0) {
x >>= 4;
result <<= 2;
}
if (x >> 2 > 0) {
result <<= 1;
}
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
uint256 result = sqrt(a);
if (rounding == Rounding.Up && result * result < a) {
result += 1;
}
return result;
}
}
// src/contracts/Miscellany/OwnedV2.sol
// https://docs.synthetix.io/contracts/Owned
contract OwnedV2 {
error OwnerCannotBeZero();
error InvalidOwnershipAcceptance();
error OnlyOwner();
address public owner;
address public nominatedOwner;
constructor(address _owner) {
// require(_owner != address(0), "Owner address cannot be 0");
if (_owner == address(0)) revert OwnerCannotBeZero();
owner = _owner;
emit OwnerChanged(address(0), _owner);
}
function nominateNewOwner(address _owner) external onlyOwner {
nominatedOwner = _owner;
emit OwnerNominated(_owner);
}
function acceptOwnership() external {
// require(msg.sender == nominatedOwner, "You must be nominated before you can accept ownership");
if (msg.sender != nominatedOwner) revert InvalidOwnershipAcceptance();
emit OwnerChanged(owner, nominatedOwner);
owner = nominatedOwner;
nominatedOwner = address(0);
}
modifier onlyOwner() {
// require(msg.sender == owner, "Only the contract owner may perform this action");
if (msg.sender != owner) revert OnlyOwner();
_;
}
event OwnerNominated(address newOwner);
event OwnerChanged(address oldOwner, address newOwner);
}
// src/contracts/Miscellany/interfaces/IFXB.sol
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC4626.sol)
interface IFXB {
/// @notice Bond Information
/// @param symbol The symbol of the bond
/// @param name The name of the bond
/// @param maturityTimestamp Timestamp the bond will mature
struct BondInfo {
string symbol;
string name;
uint256 maturityTimestamp;
}
function DOMAIN_SEPARATOR() external view returns (bytes32);
function FRAX() external view returns (address);
function MATURITY_TIMESTAMP() external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function balanceOf(address account) external view returns (uint256);
function bondInfo() external view returns (BondInfo memory);
function burn(address to, uint256 value) external;
function decimals() external view returns (uint8);
function eip712Domain()
external
view
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
);
function factory() external view returns (address);
function isRedeemable() external view returns (bool _isRedeemable);
function mint(address account, uint256 value) external;
function name() external view returns (string memory);
function nonces(address owner) external view returns (uint256);
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
function symbol() external view returns (string memory);
function totalFxbMinted() external view returns (uint256);
function totalFxbRedeemed() external view returns (uint256);
function totalSupply() external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
function version() external pure returns (uint256 _major, uint256 _minor, uint256 _patch);
}
// src/contracts/Miscellany/interfaces/ISlippageAuction.sol
interface ISlippageAuction {
/// @notice Detail information behind an auction
/// @notice Auction information
/// @param amountListed Amount of sellToken placed for auction
/// @param amountLeft Amount of sellToken remaining to buy
/// @param amountExcessBuy Amount of any additional TOKEN_BUY sent to contract during auction
/// @param amountExcessSell Amount of any additional TOKEN_SELL sent to contract during auction
/// @param tokenBuyReceived Amount of tokenBuy that came in from sales
/// @param priceLast Price of the last sale, in tokenBuy amount per tokenSell (amount of tokenBuy to purchase 1e18 tokenSell)
/// @param priceMin Minimum price of 1e18 tokenSell, in tokenBuy
/// @param priceDecay Price decay, (wei per second), using PRECISION
/// @param priceSlippage Slippage fraction. E.g (0.01 * PRECISION) = 1%
/// @param lastBuyTime Time of the last sale
/// @param expiry UNIX timestamp when the auction ends
/// @param active If the auction is active
struct Detail {
uint128 amountListed;
uint128 amountLeft;
uint128 amountExcessBuy;
uint128 amountExcessSell;
uint128 tokenBuyReceived;
uint128 priceLast;
uint128 priceMin;
uint64 priceDecay;
uint64 priceSlippage;
uint32 lastBuyTime;
uint32 expiry;
bool active;
}
/// @notice Parameters for starting an auction
/// @dev Sender must have an allowance on tokenSell
/// @param amountListed Amount of tokenSell being sold
/// @param priceStart Starting price of 1e18 tokenSell, in tokenBuy
/// @param priceMin Minimum price of 1e18 tokenSell, in tokenBuy
/// @param priceDecay Price decay, (wei per second), using PRECISION
/// @param priceSlippage Slippage fraction. E.g (0.01 * PRECISION) = 1%
/// @param expiry UNIX timestamp when the auction ends
struct StartAuctionParams {
uint128 amountListed;
uint128 priceStart;
uint128 priceMin;
uint64 priceDecay;
uint64 priceSlippage;
uint32 expiry;
}
function MINIMUM_LIQUIDITY() external pure returns (uint256);
function PRECISION() external view returns (uint256);
function TOKEN_BUY() external view returns (address);
function TOKEN_SELL() external view returns (address);
function acceptTransferTimelock() external;
function details(
uint256
)
external
view
returns (
uint128 amountListed,
uint128 amountLeft,
uint128 amountExcessBuy,
uint128 amountExcessSell,
uint128 tokenBuyReceived,
uint128 priceLast,
uint128 priceMin,
uint64 priceDecay,
uint64 priceSlippage,
uint32 lastBuyTime,
uint32 expiry,
bool active
);
function detailsLength() external view returns (uint256 _length);
function factory() external pure returns (address);
function getAmountIn(uint256 amountOut, address tokenOut) external view returns (uint256 amountIn);
function getAmountIn(uint256, uint256, uint256) external pure returns (uint256);
function getAmountIn(
uint256 amountOut
) external view returns (uint256 amountIn, uint256 _slippagePerTokenSell, uint256 _postPriceSlippage);
function getAmountInMax()
external
view
returns (uint256 amountIn, uint256 _slippagePerTokenSell, uint256 _postPriceSlippage);
function getAmountOut(uint256, uint256, uint256) external pure returns (uint256);
function getAmountOut(
uint256 amountIn,
bool _revertOnOverAmountLeft
) external view returns (uint256 amountOut, uint256 _slippagePerTokenSell, uint256 _postPriceSlippage);
function getAmountOut(uint256 amountIn, address tokenIn) external view returns (uint256 amountOut);
function getDetailStruct(uint256 _auctionNumber) external view returns (Detail memory);
function getLatestAuction() external view returns (Detail memory _latestAuction);
function getPreSlippagePrice() external view returns (uint256);
function getPreSlippagePrice(Detail memory _detail) external view returns (uint256 _price);
function getReserves() external pure returns (uint112, uint112, uint32);
function initialize(address, address) external pure;
function kLast() external pure returns (uint256);
function name() external view returns (string memory);
function pendingTimelockAddress() external view returns (address);
function price0CumulativeLast() external pure returns (uint256);
function price1CumulativeLast() external pure returns (uint256);
function renounceTimelock() external;
function skim(address) external pure;
function startAuction(StartAuctionParams memory _params) external returns (uint256 _auctionNumber);
function stopAuction() external returns (uint256 tokenBuyReceived, uint256 tokenSellRemaining);
function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes memory data) external;
function swapExactTokensForTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] memory path,
address to,
uint256 deadline
) external returns (uint256[] memory _amounts);
function swapTokensForExactTokens(
uint256 amountOut,
uint256 amountInMax,
address[] memory path,
address to,
uint256 deadline
) external returns (uint256[] memory _amounts);
function sync() external pure;
function timelockAddress() external view returns (address);
function token0() external view returns (address);
function token1() external view returns (address);
function transferTimelock(address _newTimelock) external;
function version() external pure returns (uint256 _major, uint256 _minor, uint256 _patch);
}
// src/contracts/VestedFXS-and-Flox/Flox/TransferHelper.sol
// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
library TransferHelper {
error TransferHelperApproveFailed();
error TransferHelperTransferFailed();
error TransferHelperTransferFromFailed();
error TransferHelperTransferETHFailed();
function safeApprove(address token, address to, uint256 value) internal {
// bytes4(keccak256(bytes('approve(address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
// require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: APPROVE_FAILED');
if (!success || (data.length != 0 && !abi.decode(data, (bool)))) revert TransferHelperApproveFailed();
}
function safeTransfer(address token, address to, uint256 value) internal {
// bytes4(keccak256(bytes('transfer(address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
// require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FAILED');
if (!success || (data.length != 0 && !abi.decode(data, (bool)))) revert TransferHelperTransferFailed();
}
function safeTransferFrom(address token, address from, address to, uint256 value) internal {
// bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
// require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FROM_FAILED');
if (!success || (data.length != 0 && !abi.decode(data, (bool)))) revert TransferHelperTransferFromFailed();
}
function safeTransferETH(address to, uint256 value) internal {
(bool success, ) = to.call{ value: value }(new bytes(0));
// require(success, 'TransferHelper: ETH_TRANSFER_FAILED');
if (!success) revert TransferHelperTransferETHFailed();
}
}
// node_modules/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
// node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/ERC20.sol)
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* The default value of {decimals} is 18. To select a different value for
* {decimals} you should overload it.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the value {ERC20} uses, unless this function is
* overridden;
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual override returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(
address from,
address to,
uint256 amount
) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(
address from,
address to,
uint256 amount
) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
}
_balances[to] += amount;
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
_balances[account] += amount;
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
}
_totalSupply -= amount;
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(
address owner,
address spender,
uint256 amount
) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(
address owner,
address spender,
uint256 amount
) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
}
// src/contracts/Miscellany/TimedLocker.sol
// @version 0.2.8
/**
* ====================================================================
* | ______ _______ |
* | / _____________ __ __ / ____(_____ ____ _____ ________ |
* | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
* | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
* | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
* | |
* ====================================================================
* =========================== TimedLocker ============================
* ====================================================================
* Fixed-rate FXS rewards for locking tokens.
* Total amount of staking token lockable is capped
* Locked positions are transferable as vault tokens
* After a set ending timestamp, all positions are unlockable
* Frax Finance: https://github.com/FraxFinance
*/
// import "forge-std/console2.sol";
contract TimedLocker is ERC20, OwnedV2, ReentrancyGuard {
/* ========== STATE VARIABLES ========== */
// Core variables
// ----------------------------------------
/// @notice When the locker was deployed
uint256 public immutable deployTimestamp;
/// @notice When the locker ends
uint256 public immutable endingTimestamp;
/// @notice Maximum amount of staking token that can be staked
/// @dev Can be increased only if more reward tokens are simultaneously provided, to keep the new rewardPerSecondPerToken >= old rewardPerSecondPerToken
uint256 public cap;
/// @notice The token being staked
ERC20 public stakingToken;
// Global reward-related
// ----------------------------------------
/// @notice Helper to see if a token is a reward token on this locker
mapping(address => bool) public isRewardToken;
/// @notice The last time rewards were sent in
uint256 public lastRewardPull;
/// @notice The last time this contract was updated
uint256 public lastUpdateTime;
/// @notice The time the rewards period should finish. Should be endingTimestamp
uint256 public immutable periodFinish;
/// @notice The duration of the reward period. Should be endingTimestamp - deploy block.timestamp
uint256 public immutable rewardsDuration;
/// @notice Mapping of addresses that are allowed to deposit reward tokens
mapping(address => bool) public rewardNotifiers;
/// @notice Accumulator for rewardsPerToken
// https://www.paradigm.xyz/2021/05/liquidity-mining-on-uniswap-v3
uint256[] public rewardsPerTokenStored;
/// @notice The reward tokens per second
uint256[] public rewardRates;
/// @notice Helper to get the reward token index, given the address of the token
mapping(address => uint256) public rewardTokenAddrToIdx;
/// @notice Array of all the reward tokens
address[] public rewardTokens;
// User reward-related
// ----------------------------------------
/// @notice The last time a farmer claimed their rewards
mapping(address => uint256) public lastRewardClaimTime; // staker addr -> timestamp
/// @notice Used for tracking stored/collectible rewards. earned()
mapping(address => mapping(uint256 => uint256)) public rewards; // staker addr -> token id -> reward amount
/// @notice Accumulator for userRewardsPerTokenPaid
mapping(address => mapping(uint256 => uint256)) public userRewardsPerTokenPaid; // staker addr -> token id -> paid amount
// Emergency variables
// ----------------------------------------
/// @notice If external syncEarned calls via bulkSyncEarnedUsers are allowed
bool public externalSyncEarningPaused;
/// @notice If reward collections are paused
bool public rewardsCollectionPaused;
/// @notice If staking is paused
bool public stakingPaused;
/// @notice Release locked stakes in case of system migration or emergency
bool public stakesUnlocked;
// For emergencies if a token is overemitted or something else. Only callable once.
// Bypasses certain logic, which will cause reward calculations to be off
// But the goal is for the users to recover LP, and they couldn't claim the erroneous rewards anyways.
// Reward reimbursement claims would be handled with pre-issue earned() snapshots and a claim contract, or similar.
bool public withdrawalOnlyShutdown;
/// @notice If withdrawals are paused
bool public withdrawalsPaused;
/* ========== CONSTRUCTOR ========== */
/// @notice Constructor
/// @param _owner The owner of the locker
/// @param _rewardTokens Array of reward tokens
/// @param _name Name for the vault token
/// @param _symbol Symbol for the vault token
/// @param _stakingToken The token being staked
/// @param _endingTimestamp Timestamp when all locks become unlocked
/// @param _cap Maximum amount of staking tokens allowed to be locked
/// @param _extraNotifier Additional reward notifier to add when constructing. Can add more / remove later
constructor(
address _owner,
address[] memory _rewardTokens,
address _stakingToken,
string memory _name,
string memory _symbol,
uint256 _endingTimestamp,
uint256 _cap,
address _extraNotifier
) ERC20(_name, _symbol) OwnedV2(_owner) {
// Set state variables
stakingToken = ERC20(_stakingToken);
rewardTokens = _rewardTokens;
endingTimestamp = _endingTimestamp;
cap = _cap;
// Loop through the reward tokens
for (uint256 i = 0; i < _rewardTokens.length; i++) {
// For fast token address -> token ID lookups later
rewardTokenAddrToIdx[_rewardTokens[i]] = i;
// Add to the mapping
isRewardToken[_rewardTokens[i]] = true;
// Initialize the stored rewards
rewardsPerTokenStored.push(0);
// Initialize the reward rates
rewardRates.push(0);
}
// Set the owner as an allowed reward notifier
rewardNotifiers[_owner] = true;
// Add the additional reward notifier, if present
if (_extraNotifier != address(0)) rewardNotifiers[_extraNotifier] = true;
// Other booleans
stakesUnlocked = false;
// For initialization
deployTimestamp = block.timestamp;
lastUpdateTime = block.timestamp;
rewardsDuration = _endingTimestamp - block.timestamp;
periodFinish = _endingTimestamp;
}
/* ========== MODIFIERS ========== */
/// @notice Staking should not be paused
modifier notStakingPaused() {
require(!stakingPaused, "Staking paused");
_;
}
/// @notice Update rewards and balances
modifier updateRewards(address account) {
_updateRewards(account);
_;
}
/* ========== VIEWS ========== */
/// @notice Remaining amount of stakingToken you can lock before hitting the cap
/// @return _amount The amount
function availableToLock() public view returns (uint256 _amount) {
_amount = (cap - totalSupply());
}
/// @notice The last time rewards were applicable. Should be the lesser of the current timestamp, or the end of the last period
/// @return uint256 The last timestamp where rewards were applicable
function lastTimeRewardApplicable() internal view returns (uint256) {
return Math.min(block.timestamp, periodFinish);
}
/// @notice Minimum amount of additional reward tokens needed so rewardPerSecondPerToken remains the same after a cap increase
/// @param _newCap The new cap you want to increase to
/// @return _minRewRates Minimum rewardRate needed after the cap is raised
/// @return _minAddlTkns Minimum amount of additional reward tokens needed
function minAddlRewTknsForCapIncrease(
uint256 _newCap
) public view returns (uint256[] memory _minRewRates, uint256[] memory _minAddlTkns) {
// Cap can only increase
if (_newCap < cap) revert CapCanOnlyIncrease();
// Initialize return arrays
_minRewRates = new uint256[](rewardTokens.length);
_minAddlTkns = new uint256[](rewardTokens.length);
// See how much time is left
uint256 _timeLeft = endingTimestamp - block.timestamp;
// Loop through the reward tokens
for (uint256 i = 0; i < rewardTokens.length; ) {
// Solve for the new reward rate, assuming (Rate / Tokens) is constant
// Round up by 1 wei
_minRewRates[i] = ((rewardRates[i] * _newCap) + 1) / cap;
// Calculate the additional tokens needed
_minAddlTkns[i] = _timeLeft * (_minRewRates[i] - rewardRates[i]);
unchecked {
++i;
}
}
}
/// @notice The calculated rewardPerTokenStored accumulator
/// @return _rtnRewardsPerTokenStored Array of rewardsPerTokenStored
function rewardPerToken() public view returns (uint256[] memory _rtnRewardsPerTokenStored) {
// Prepare the return variable
_rtnRewardsPerTokenStored = new uint256[](rewardTokens.length);
// Calculate
if (totalSupply() == 0) {
// Return 0 if there are no vault tokens
_rtnRewardsPerTokenStored = rewardsPerTokenStored;
} else {
// Loop through the reward tokens
for (uint256 i = 0; i < rewardTokens.length; ) {
_rtnRewardsPerTokenStored[i] =
rewardsPerTokenStored[i] +
(((lastTimeRewardApplicable() - lastUpdateTime) * rewardRates[i] * 1e18) / totalSupply());
unchecked {
++i;
}
}
}
}
/// @notice The currently earned rewards for a user
/// @param _account The staker's address
/// @return _rtnEarned Array of the amounts of reward tokens the staker can currently collect
function earned(address _account) public view returns (uint256[] memory _rtnEarned) {
// Prepare the return variable
_rtnEarned = new uint256[](rewardTokens.length);
// Get the reward rate per token
uint256[] memory _rtnRewardsPerToken = rewardPerToken();
// Loop through the reward tokens
for (uint256 i = 0; i < rewardTokens.length; ) {
_rtnEarned[i] =
rewards[_account][i] +
((balanceOf(_account) * ((_rtnRewardsPerToken[i] - userRewardsPerTokenPaid[_account][i]))) / 1e18);
unchecked {
++i;
}
}
}
/// @notice Amount of rewards remaining
/// @return _rtnRewardsRemaining Array of the amounts of the reward tokens
function getRewardsRemaining() external view returns (uint256[] memory _rtnRewardsRemaining) {
// Prepare the return variable
_rtnRewardsRemaining = new uint256[](rewardTokens.length);
// Return 0 if the locker has already ended
if (endingTimestamp <= block.timestamp) return _rtnRewardsRemaining;
// See how much time is left
uint256 _timeLeft = endingTimestamp - block.timestamp;
// Calculate the duration rewards
for (uint256 i = 0; i < rewardTokens.length; ) {
_rtnRewardsRemaining[i] = rewardRates[i] * _timeLeft;
unchecked {
++i;
}
}
}
/* ========== ERC20 OVERRIDES ========== */
/// @notice Override the _update logic to claim/sync earnings before transferring.
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 value) internal override {
// TODO for auditors: Make sure this is not manipulatable
// If you aren't minting, sync rewards first so the owner doesn't lose them after transferring
// Also so the recipient doesn't get free rewards
// withdrawalOnlyShutdown sacrifices rewards buts lets the token move, for emergencies only.
if (!withdrawalOnlyShutdown && (from != address(0))) {
sync();
_syncEarnedInner(from);
_syncEarnedInner(to);
}
}
/* ========== MUTATIVE FUNCTIONS ========== */
/// @notice Sync earnings for many users. Convenience function
/// @param _users The account to sync
/// @dev _beforeTokenTransfer essentially does this
function bulkSyncEarnedUsers(address[] memory _users) external {
// Check for withdrawal-only shutdown as well as the pause
if (withdrawalOnlyShutdown || externalSyncEarningPaused) revert ExternalSyncEarningPaused();
// Sync normally first
sync();
// Loop through the users and sync them. Skip global sync() to save gas
for (uint256 i = 0; i < _users.length; ) {
_syncEarnedInner(_users[i]);
unchecked {
++i;
}
}
}
/// @notice Sync contract-wide variables
function sync() public {
// Update rewardsPerTokenStored
rewardsPerTokenStored = rewardPerToken();
// Update the last update time
lastUpdateTime = lastTimeRewardApplicable();
}
/// @notice Update the reward and balance state for a staker
/// @param account The address of the user
function _updateRewards(address account) internal {
if (account != address(0)) {
// Calculate the earnings first
// Skip if we are in emergency shutdown
if (!withdrawalOnlyShutdown) _syncEarned(account);
}
}
/// @notice [MUST be proceeded by global sync()] Sync earnings for a specific staker. Skips the global sync() to save gas (mainly for bulkSyncEarnedUsers())
/// @param _account The account to sync
function _syncEarnedInner(address _account) internal {
if (_account != address(0)) {
// Calculate the earnings
uint256[] memory _earneds = earned(_account);
// Update the stake
for (uint256 i = 0; i < rewardTokens.length; ) {
rewards[_account][i] = _earneds[i];
userRewardsPerTokenPaid[_account][i] = rewardsPerTokenStored[i];
unchecked {
++i;
}
}
}
}
/// @notice Sync earnings for a specific staker
/// @param _account The account to sync
function _syncEarned(address _account) internal {
// Update rewardsPerTokenStored and last update time
sync();
// Sync the account's earnings
_syncEarnedInner(_account);
}
/// @notice Stake stakingToken for vault tokens
/// @param _amount The amount of stakingToken
function stake(uint256 _amount) public nonReentrant updateRewards(msg.sender) {
// Do checks
if (block.timestamp >= endingTimestamp) revert LockerHasEnded();
if (stakingPaused) revert StakingPaused();
if (stakesUnlocked) revert StakesAreUnlocked();
if (withdrawalOnlyShutdown) revert OnlyWithdrawalsAllowed();
if (_amount == 0) revert MustBeNonZero();
if ((totalSupply() + _amount) > cap) revert Capped();
// Pull the staking tokens from the msg.sender
TransferHelper.safeTransferFrom(address(stakingToken), msg.sender, address(this), _amount);
// Mint an equal amount of vault tokens to the staker
_mint(msg.sender, _amount);
// Update rewards
_updateRewards(msg.sender);
emit Stake(msg.sender, _amount, msg.sender);
}
/// @notice Withdraw stakingToken from vault tokens.
/// @param _vaultTknAmount Amount of vault tokens to use
/// @param _collectRewards Whether to also collect rewards
function withdraw(
uint256 _vaultTknAmount,
bool _collectRewards
) public nonReentrant returns (uint256[] memory _rtnRewards) {
if ((block.timestamp < endingTimestamp) && !(stakesUnlocked || withdrawalOnlyShutdown)) {
revert LockerStillActive();
}
if (withdrawalsPaused) revert WithdrawalsPaused();
// Burn the vault token from the sender
_burn(msg.sender, _vaultTknAmount);
// Give the stakingToken to the msg.sender
// Should throw if insufficient balance
TransferHelper.safeTransfer(address(stakingToken), msg.sender, _vaultTknAmount);
// Collect rewards
_rtnRewards = new uint256[](rewardTokens.length);
if (_collectRewards) _rtnRewards = getReward(msg.sender);
emit Withdrawal(msg.sender, _vaultTknAmount);
}
/// @notice Collect rewards
/// @param _destinationAddress Destination address for the rewards
/// @return _rtnRewards The amounts of collected reward tokens
function getReward(
address _destinationAddress
) public updateRewards(msg.sender) returns (uint256[] memory _rtnRewards) {
// Make sure you are not in shutdown
if (withdrawalOnlyShutdown) revert OnlyWithdrawalsAllowed();
// Make sure reward collections are not paused
if (rewardsCollectionPaused) revert RewardCollectionIsPaused();
// Prepare the return variable
_rtnRewards = new uint256[](rewardTokens.length);
// Loop through the rewards
for (uint256 i = 0; i < rewardTokens.length; ) {
_rtnRewards[i] = rewards[msg.sender][i];
// Do reward accounting
if (_rtnRewards[i] > 0) {
rewards[msg.sender][i] = 0;
TransferHelper.safeTransfer(rewardTokens[i], _destinationAddress, _rtnRewards[i]);
emit RewardPaid(msg.sender, _rtnRewards[i], rewardTokens[i], _destinationAddress);
}
unchecked {
++i;
}
}
// Update the last reward claim time
lastRewardClaimTime[msg.sender] = block.timestamp;
}
/// @notice Supply rewards. Only callable by whitelisted addresses.
/// @param _amounts Amount of each reward token to add
function notifyRewardAmounts(uint256[] memory _amounts) public {
// Only the owner and the whitelisted addresses can notify rewards
if (!((owner == msg.sender) || rewardNotifiers[msg.sender])) revert SenderNotOwnerOrRewarder();
// Make sure the locker has not ended
if (block.timestamp >= endingTimestamp) revert LockerHasEnded();
// Pull in the reward tokens from the sender
for (uint256 i = 0; i < rewardTokens.length; ) {
// Handle the transfer of emission tokens via `transferFrom` to reduce the number
// of transactions required and ensure correctness of the emission amount
TransferHelper.safeTransferFrom(rewardTokens[i], msg.sender, address(this), _amounts[i]);
unchecked {
++i;
}
}
// Update rewardsPerTokenStored and last update time
sync();
// Calculate the reward rate
for (uint256 i = 0; i < rewardTokens.length; ) {
// Account for unemitted tokens
uint256 remainingTime = periodFinish - block.timestamp;
uint256 leftoverRwd = remainingTime * rewardRates[i];
// Replace rewardsDuration with remainingTime here since we only have one big period
// rewardRates[i] = (_amounts[i] + leftoverRwd) / rewardsDuration;
rewardRates[i] = (_amounts[i] + leftoverRwd) / remainingTime;
emit RewardAdded(rewardTokens[i], _amounts[i], rewardRates[i]);
unchecked {
++i;
}
}
// Update rewardsPerTokenStored and last update time (again)
sync();
}
/* ========== RESTRICTED FUNCTIONS ========== */
/// @notice Only settable to true
function initiateWithdrawalOnlyShutdown() external onlyOwner {
withdrawalOnlyShutdown = true;
}
/// @notice Increase the staking token cap. Can be increased only if more reward tokens are simultaneously provided, to keep the new rewardPerSecondPerToken >= old rewardPerSecondPerToken.
/// @param _newCap The address of the token
/// @param _addlRewTknAmounts The amount(s) of reward tokens being supplied as part of this cap increase.
function increaseCapWithRewards(uint256 _newCap, uint256[] memory _addlRewTknAmounts) external onlyOwner {
// Cap can only increase
if (_newCap < cap) revert CapCanOnlyIncrease();
// Sync first
sync();
// Fetch the calculated new rewardRates as well as the amount of additional tokens needed
(uint256[] memory _minRewRates, uint256[] memory _minAddlTkns) = minAddlRewTknsForCapIncrease(_newCap);
// Make sure enough reward tokens were supplied
for (uint256 i = 0; i < rewardTokens.length; i++) {
if (_addlRewTknAmounts[i] < _minAddlTkns[i]) revert NotEnoughAddlRewTkns();
}
// Increase the cap
cap = _newCap;
// Add in the new rewards
notifyRewardAmounts(_addlRewTknAmounts);
// Compare the new rewardRate with the calculated minimum
// New must be >= old
for (uint256 i = 0; i < rewardTokens.length; i++) {
if (rewardRates[i] < _minRewRates[i]) {
revert NotEnoughAddlRewTkns();
}
}
}
/// @notice Added to support recovering LP Rewards and other mistaken tokens from other systems to be distributed to holders
/// @param _tokenAddress The address of the token
/// @param _tokenAmount The amount of the token
function recoverERC20(address _tokenAddress, uint256 _tokenAmount) external onlyOwner {
// Only the owner address can ever receive the recovery withdrawal
TransferHelper.safeTransfer(_tokenAddress, owner, _tokenAmount);
emit Recovered(_tokenAddress, _tokenAmount);
}
/// @notice Toggle the ability to syncEarned externally via bulkSyncEarnedUsers
function toggleExternalSyncEarning() external onlyOwner {
externalSyncEarningPaused = !externalSyncEarningPaused;
}
/// @notice Toggle the ability to stake
function toggleStaking() external onlyOwner {
stakingPaused = !stakingPaused;
}
/// @notice Toggle the ability to collect rewards
function toggleRewardsCollection() external onlyOwner {
rewardsCollectionPaused = !rewardsCollectionPaused;
}
/// @notice Toggle an address as being able to be a reward notifier
/// @param _notifierAddr The address to toggle
function toggleRewardNotifier(address _notifierAddr) external onlyOwner {
rewardNotifiers[_notifierAddr] = !rewardNotifiers[_notifierAddr];
}
/// @notice Toggle the ability to withdraw
function toggleWithdrawals() external onlyOwner {
withdrawalsPaused = !withdrawalsPaused;
}
/// @notice Unlock all stakes, in the case of an emergency
function unlockStakes() external onlyOwner {
stakesUnlocked = !stakesUnlocked;
}
/* ========== ERRORS ========== */
/// @notice When you are trying to lower the cap, which is not allowed
error CapCanOnlyIncrease();
/// @notice When you are trying to lock more tokens than are allowed
error Capped();
/// @notice If syncEarned should only be callable indirectly through methods or internally. Also occurs if in a withdrawal-only shutdown
error ExternalSyncEarningPaused();
/// @notice If the locker ending timestamp has passed
error LockerHasEnded();
/// @notice If the locker ending timestamp has not yet passed
error LockerStillActive();
/// @notice If an input value must be non-zero
error MustBeNonZero();
/// @notice If only withdrawals are allowed
error OnlyWithdrawalsAllowed();
/// @notice If reward collections are paused
error RewardCollectionIsPaused();
/// @notice When the cap is increased, the rewardPerSecondPerToken must either increase or stay the same
error NotEnoughAddlRewTkns();
/// @notice If the sender is not the owner or a rewarder
error SenderNotOwnerOrRewarder();
/// @notice If staking has been paused
error StakingPaused();
/// @notice If you are trying to stake after stakes have been unlock
error StakesAreUnlocked();
/// @notice If you didn't acknowledge the notifyRewardAmounts sync warning
error MustSyncAllUsersBeforeNotifying();
/// @notice If withdrawals have been paused
error WithdrawalsPaused();
/* ========== EVENTS ========== */
/// @notice When LP tokens are locked
/// @param user The staker
/// @param amount Amount of LP staked
/// @param source_address The origin address of the LP tokens. Usually the same as the user unless there is a migration in progress
event Stake(address indexed user, uint256 amount, address source_address);
/// @notice When LP tokens are withdrawn
/// @param user The staker
/// @param amount Amount of LP withdrawn
event Withdrawal(address indexed user, uint256 amount);
/// @notice When tokens are recovered, in the case of an emergency
/// @param token Address of the token
/// @param amount Amount of the recovered tokens
event Recovered(address token, uint256 amount);
/// @notice When a reward is deposited
/// @param reward_address The address of the reward token
/// @param reward Amount of tokens deposited
/// @param yieldRate The resultant yield/emission rate
event RewardAdded(address indexed reward_address, uint256 reward, uint256 yieldRate);
/// @notice When a staker collects rewards
/// @param user The staker
/// @param reward Amount of reward tokens
/// @param token_address Address of the reward token
/// @param destination_address Destination address of the reward tokens
event RewardPaid(address indexed user, uint256 reward, address token_address, address destination_address);
}
// src/contracts/Miscellany/FRAXToFXBLockerRouter.sol
// @version 0.2.8
/**
* ====================================================================
* | ______ _______ |
* | / _____________ __ __ / ____(_____ ____ _____ ________ |
* | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
* | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
* | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
* | |
* ====================================================================
* ====================== FRAXToFXBLockerRouter =======================
* ====================================================================
* Takes FRAX and converts it into FXB, then places it in a TimedLocker
* Frax Finance: https://github.com/FraxFinance
*/
// import "forge-std/console2.sol";
contract FRAXToFXBLockerRouter is OwnedV2, ReentrancyGuard {
/* ========== STATE VARIABLES ========== */
// Core variables
// ----------------------------------------
/// @notice The FRAX token
ERC20 public frax = ERC20(0xFc00000000000000000000000000000000000001);
/// @notice Routes for the FXB -> Auction -> TimedLocker -> isValid
mapping(address fxb => mapping(address auction => mapping(address locker => bool isValid))) public routeStatuses;
/* ========== CONSTRUCTOR ========== */
/// @notice Constructor
/// @param _owner The owner of the locker
constructor(address _owner) OwnedV2(_owner) {}
/* ========== MODIFIERS ========== */
/* ========== VIEWS ========== */
/* ========== MUTATIVE FUNCTIONS ========== */
/// @notice Routes FRAX into a FXB TimedLocker
/// @param _fxbAddr Address of the FXB token
/// @param _auctionAddr Address of the FXB SlippageAuction
/// @param _lockerAddr Address of the desired TimedLocker
/// @param _fraxIn Amount of input FRAX
/// @param _minOutFxb Minimum amount of FXB out
/// @return _fxbOut Amount of FXB (and the vault tokens) that was generated
/// @dev Approve FRAX to this contract first
function routeFraxToTimedLocker(
address _fxbAddr,
address _auctionAddr,
address _lockerAddr,
uint256 _fraxIn,
uint256 _minOutFxb
) public returns (uint256 _fxbOut) {
// Take the FRAX from the user
TransferHelper.safeTransferFrom(address(frax), msg.sender, address(this), _fraxIn);
// Check the Route's validity
if (!routeStatuses[_fxbAddr][_auctionAddr][_lockerAddr]) revert InvalidRoute();
// Instantiate the SlippageAuction and the TimedLocker
ISlippageAuction _auction = ISlippageAuction(_auctionAddr);
TimedLocker _locker = TimedLocker(_lockerAddr);
// Approve FRAX to the auction
frax.approve(_auctionAddr, _fraxIn);
// Prepare the path
address[] memory _path = new address[](2);
_path[0] = address(frax);
_path[1] = _fxbAddr;
// Buy FXBs from the auction
uint256[] memory _amounts = _auction.swapExactTokensForTokens(
_fraxIn,
_minOutFxb,
_path,
address(this),
block.timestamp + 3600
);
_fxbOut = _amounts[1];
// Approve the FXB to the TimedLocker
ERC20(_fxbAddr).approve(_lockerAddr, _fxbOut);
// Do the lock
_locker.stake(_fxbOut);
// Give the vault tokens to the sender
TransferHelper.safeTransfer(_lockerAddr, msg.sender, _fxbOut);
emit FraxRouted(_fxbAddr, _auctionAddr, _lockerAddr, _fraxIn, _fxbOut);
}
/* ========== RESTRICTED FUNCTIONS ========== */
/// @notice Set an allowed FRAX -> FXB (auction) -> TimedLocker route
/// @param _fxb The address of the FXB
/// @param _auction The address of the FXB SlippageAuction
/// @param _locker The address of the TimedLocker
/// @param _status True if the specified route is to be allowed
function setRouteStatus(address _fxb, address _auction, address _locker, bool _status) external onlyOwner {
// Check inputs
if (_fxb == address(0)) revert InvalidFXB();
// Will revert if inputs are not contracts, or if other checks don't pass
try IFXB(_fxb).MATURITY_TIMESTAMP() {} catch {
revert InvalidFXB();
}
if (ISlippageAuction(_auction).TOKEN_SELL() != _fxb) revert InvalidAuction();
if (address(TimedLocker(_locker).stakingToken()) != _fxb) revert InvalidTimedLocker();
// Set the route status
routeStatuses[_fxb][_auction][_locker] = _status;
emit RouteStatusSet(_fxb, _auction, _locker, _status);
}
/// @notice Added to support recovering LP Rewards and other mistaken tokens from other systems to be distributed to holders
/// @param _tokenAddress The address of the token
/// @param _tokenAmount The amount of the token
function recoverERC20(address _tokenAddress, uint256 _tokenAmount) external onlyOwner {
// Only the owner address can ever receive the recovery withdrawal
TransferHelper.safeTransfer(_tokenAddress, owner, _tokenAmount);
emit RecoveredERC20(_tokenAddress, _tokenAmount);
}
/* ========== ERRORS ========== */
/// @notice When the SlippageAuction you are trying to set is invalid
error InvalidAuction();
/// @notice When the FXB you are trying to set is invalid
error InvalidFXB();
/// @notice When the Route you are using is invalid or disabled
error InvalidRoute();
/// @notice When the TimedLocker you are trying to set is invalid
error InvalidTimedLocker();
/* ========== EVENTS ========== */
/// @notice When a FRAX -> FXB (auction) -> TimedLocker route is executed
/// @param fxb The address of the FXB
/// @param auction The address of the FXB SlippageAuction
/// @param locker The address of the TimedLocker
/// @param fraxIn Input amout of FRAX
/// @param fxbOut Output amount of FXB that was subsequently locked in the TimedLocker
event FraxRouted(address indexed fxb, address auction, address locker, uint256 fraxIn, uint256 fxbOut);
/// @notice When ERC20 tokens were recovered
/// @param token Token address
/// @param amount Amount of tokens collected
event RecoveredERC20(address token, uint256 amount);
/// @notice When a FRAX -> FXB (auction) -> TimedLocker route is set
/// @param fxb The address of the FXB
/// @param auction The address of the FXB SlippageAuction
/// @param locker The address of the TimedLocker
/// @param status True if the specified route is to be allowed
event RouteStatusSet(address indexed fxb, address auction, address locker, bool status);
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidAuction","type":"error"},{"inputs":[],"name":"InvalidFXB","type":"error"},{"inputs":[],"name":"InvalidOwnershipAcceptance","type":"error"},{"inputs":[],"name":"InvalidRoute","type":"error"},{"inputs":[],"name":"InvalidTimedLocker","type":"error"},{"inputs":[],"name":"OnlyOwner","type":"error"},{"inputs":[],"name":"OwnerCannotBeZero","type":"error"},{"inputs":[],"name":"TransferHelperTransferFailed","type":"error"},{"inputs":[],"name":"TransferHelperTransferFromFailed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"fxb","type":"address"},{"indexed":false,"internalType":"address","name":"auction","type":"address"},{"indexed":false,"internalType":"address","name":"locker","type":"address"},{"indexed":false,"internalType":"uint256","name":"fraxIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fxbOut","type":"uint256"}],"name":"FraxRouted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerNominated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RecoveredERC20","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"fxb","type":"address"},{"indexed":false,"internalType":"address","name":"auction","type":"address"},{"indexed":false,"internalType":"address","name":"locker","type":"address"},{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"RouteStatusSet","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"frax","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"nominateNewOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nominatedOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_tokenAmount","type":"uint256"}],"name":"recoverERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_fxbAddr","type":"address"},{"internalType":"address","name":"_auctionAddr","type":"address"},{"internalType":"address","name":"_lockerAddr","type":"address"},{"internalType":"uint256","name":"_fraxIn","type":"uint256"},{"internalType":"uint256","name":"_minOutFxb","type":"uint256"}],"name":"routeFraxToTimedLocker","outputs":[{"internalType":"uint256","name":"_fxbOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"fxb","type":"address"},{"internalType":"address","name":"auction","type":"address"},{"internalType":"address","name":"locker","type":"address"}],"name":"routeStatuses","outputs":[{"internalType":"bool","name":"isValid","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_fxb","type":"address"},{"internalType":"address","name":"_auction","type":"address"},{"internalType":"address","name":"_locker","type":"address"},{"internalType":"bool","name":"_status","type":"bool"}],"name":"setRouteStatus","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
6080604052600380546001600160a01b03191673fc00000000000000000000000000000000000001179055348015610035575f80fd5b506040516113fe3803806113fe833981016040819052610054916100de565b806001600160a01b03811661007c57604051639b15e16f60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b03831690811782556040805192835260208301919091527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a15050600160025561010b565b5f602082840312156100ee575f80fd5b81516001600160a01b0381168114610104575f80fd5b9392505050565b6112e6806101185f395ff3fe608060405234801561000f575f80fd5b506004361061009f575f3560e01c80638980f11f116100725780638da5cb5b116100585780638da5cb5b14610180578063a79926e01461019f578063ebc089da146101c0575f80fd5b80638980f11f1461014d57806389b4ec8e14610160575f80fd5b80631627540c146100a357806346dab679146100b857806353a47bb71461010057806379ba509714610145575b5f80fd5b6100b66100b1366004610ef4565b6101d3565b005b6100eb6100c6366004610f16565b600460209081525f938452604080852082529284528284209052825290205460ff1681565b60405190151581526020015b60405180910390f35b6001546101209073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100f7565b6100b661029c565b6100b661015b366004610f5e565b61038f565b6003546101209073ffffffffffffffffffffffffffffffffffffffff1681565b5f546101209073ffffffffffffffffffffffffffffffffffffffff1681565b6101b26101ad366004610f88565b610456565b6040519081526020016100f7565b6100b66101ce366004610fec565b610898565b5f5473ffffffffffffffffffffffffffffffffffffffff163314610223576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce229060200160405180910390a150565b60015473ffffffffffffffffffffffffffffffffffffffff1633146102ed576040517fd74b334e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f546001546040805173ffffffffffffffffffffffffffffffffffffffff93841681529290911660208301527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a1600180545f80547fffffffffffffffffffffffff000000000000000000000000000000000000000090811673ffffffffffffffffffffffffffffffffffffffff841617909155169055565b5f5473ffffffffffffffffffffffffffffffffffffffff1633146103df576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5461040390839073ffffffffffffffffffffffffffffffffffffffff1683610c48565b6040805173ffffffffffffffffffffffffffffffffffffffff84168152602081018390527f55350610fe57096d8c0ffa30beede987326bccfcb0b4415804164d0dd50ce8b1910160405180910390a15050565b6003545f9061047d9073ffffffffffffffffffffffffffffffffffffffff16333086610d88565b73ffffffffffffffffffffffffffffffffffffffff8087165f908152600460209081526040808320898516845282528083209388168352929052205460ff166104f2576040517f84e505d200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003546040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8088166004830152602482018690528792879291169063095ea7b3906044016020604051808303815f875af115801561056b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061058f9190611045565b506040805160028082526060820183525f926020830190803683375050600354825192935073ffffffffffffffffffffffffffffffffffffffff16918391505f906105dc576105dc61108d565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050888160018151811061062a5761062a61108d565b73ffffffffffffffffffffffffffffffffffffffff92831660209182029290920101525f9084166338ed17398888853061066642610e106110ba565b6040518663ffffffff1660e01b81526004016106869594939291906110f8565b5f604051808303815f875af11580156106a1573d5f803e3d5ffd5b505050506040513d5f823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526106e69190810190611182565b9050806001815181106106fb576106fb61108d565b60209081029190910101516040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8a8116600483015260248201839052919650908b169063095ea7b3906044016020604051808303815f875af115801561077c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107a09190611045565b506040517fa694fc3a0000000000000000000000000000000000000000000000000000000081526004810186905273ffffffffffffffffffffffffffffffffffffffff84169063a694fc3a906024015f604051808303815f87803b158015610806575f80fd5b505af1158015610818573d5f803e3d5ffd5b50505050610827883387610c48565b6040805173ffffffffffffffffffffffffffffffffffffffff8b811682528a8116602083015291810189905260608101879052908b16907fa73e61301ee5e018e2d47ce42cdfcf519eaed0ff45f1f626d780908f344ea9119060800160405180910390a25050505095945050505050565b5f5473ffffffffffffffffffffffffffffffffffffffff1633146108e8576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8416610935576040517f6df3583d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8373ffffffffffffffffffffffffffffffffffffffff16638e3bc0ac6040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156109ba575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526109b791810190611268565b60015b6109f0576040517f6df3583d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b508373ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16635c80aeee6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a51573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a75919061127f565b73ffffffffffffffffffffffffffffffffffffffff1614610ac2576040517f2156216000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff166372f702f36040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b22573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b46919061127f565b73ffffffffffffffffffffffffffffffffffffffff1614610b93576040517f91f9b3dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8481165f8181526004602090815260408083208886168085529083528184209588168085529583529281902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001687151590811790915581519384529183019490945292810192909252907f6fce32a4fb69cfc2ab72262a5a942271f18af7e0b35869d4bf829e48eca253e59060600160405180910390a250505050565b6040805173ffffffffffffffffffffffffffffffffffffffff8481166024830152604480830185905283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb0000000000000000000000000000000000000000000000000000000017905291515f92839290871691610cde919061129a565b5f604051808303815f865af19150503d805f8114610d17576040519150601f19603f3d011682016040523d82523d5f602084013e610d1c565b606091505b5091509150811580610d4a5750805115801590610d4a575080806020019051810190610d489190611045565b155b15610d81576040517f19a9708500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050505050565b6040805173ffffffffffffffffffffffffffffffffffffffff85811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd0000000000000000000000000000000000000000000000000000000017905291515f92839290881691610e26919061129a565b5f604051808303815f865af19150503d805f8114610e5f576040519150601f19603f3d011682016040523d82523d5f602084013e610e64565b606091505b5091509150811580610e925750805115801590610e92575080806020019051810190610e909190611045565b155b15610ec8576040517e298ffd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050565b73ffffffffffffffffffffffffffffffffffffffff81168114610ef1575f80fd5b50565b5f60208284031215610f04575f80fd5b8135610f0f81610ed0565b9392505050565b5f805f60608486031215610f28575f80fd5b8335610f3381610ed0565b92506020840135610f4381610ed0565b91506040840135610f5381610ed0565b809150509250925092565b5f8060408385031215610f6f575f80fd5b8235610f7a81610ed0565b946020939093013593505050565b5f805f805f60a08688031215610f9c575f80fd5b8535610fa781610ed0565b94506020860135610fb781610ed0565b93506040860135610fc781610ed0565b94979396509394606081013594506080013592915050565b8015158114610ef1575f80fd5b5f805f8060808587031215610fff575f80fd5b843561100a81610ed0565b9350602085013561101a81610ed0565b9250604085013561102a81610ed0565b9150606085013561103a81610fdf565b939692955090935050565b5f60208284031215611055575f80fd5b8151610f0f81610fdf565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b808201808211156110f2577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b92915050565b5f60a0820187835286602084015260a0604084015280865180835260c0850191506020880192505f5b8181101561115557835173ffffffffffffffffffffffffffffffffffffffff16835260209384019390920191600101611121565b505073ffffffffffffffffffffffffffffffffffffffff9590951660608401525050608001529392505050565b5f60208284031215611192575f80fd5b815167ffffffffffffffff8111156111a8575f80fd5b8201601f810184136111b8575f80fd5b805167ffffffffffffffff8111156111d2576111d2611060565b8060051b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f830116810181811067ffffffffffffffff8211171561121d5761121d611060565b60405291825260208184018101929081018784111561123a575f80fd5b6020850194505b8385101561125d57845180825260209586019590935001611241565b509695505050505050565b5f60208284031215611278575f80fd5b5051919050565b5f6020828403121561128f575f80fd5b8151610f0f81610ed0565b5f82518060208501845e5f92019182525091905056fea264697066735822122061170840bd936c1f9f0aad0cc103505fe3fde9cbecad1ed0f737e726a92414ed64736f6c634300081a0033000000000000000000000000625e700125ff054f75e5348497cbfab1ee4b7a40
Deployed Bytecode
0x608060405234801561000f575f80fd5b506004361061009f575f3560e01c80638980f11f116100725780638da5cb5b116100585780638da5cb5b14610180578063a79926e01461019f578063ebc089da146101c0575f80fd5b80638980f11f1461014d57806389b4ec8e14610160575f80fd5b80631627540c146100a357806346dab679146100b857806353a47bb71461010057806379ba509714610145575b5f80fd5b6100b66100b1366004610ef4565b6101d3565b005b6100eb6100c6366004610f16565b600460209081525f938452604080852082529284528284209052825290205460ff1681565b60405190151581526020015b60405180910390f35b6001546101209073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100f7565b6100b661029c565b6100b661015b366004610f5e565b61038f565b6003546101209073ffffffffffffffffffffffffffffffffffffffff1681565b5f546101209073ffffffffffffffffffffffffffffffffffffffff1681565b6101b26101ad366004610f88565b610456565b6040519081526020016100f7565b6100b66101ce366004610fec565b610898565b5f5473ffffffffffffffffffffffffffffffffffffffff163314610223576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce229060200160405180910390a150565b60015473ffffffffffffffffffffffffffffffffffffffff1633146102ed576040517fd74b334e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f546001546040805173ffffffffffffffffffffffffffffffffffffffff93841681529290911660208301527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a1600180545f80547fffffffffffffffffffffffff000000000000000000000000000000000000000090811673ffffffffffffffffffffffffffffffffffffffff841617909155169055565b5f5473ffffffffffffffffffffffffffffffffffffffff1633146103df576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5461040390839073ffffffffffffffffffffffffffffffffffffffff1683610c48565b6040805173ffffffffffffffffffffffffffffffffffffffff84168152602081018390527f55350610fe57096d8c0ffa30beede987326bccfcb0b4415804164d0dd50ce8b1910160405180910390a15050565b6003545f9061047d9073ffffffffffffffffffffffffffffffffffffffff16333086610d88565b73ffffffffffffffffffffffffffffffffffffffff8087165f908152600460209081526040808320898516845282528083209388168352929052205460ff166104f2576040517f84e505d200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003546040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8088166004830152602482018690528792879291169063095ea7b3906044016020604051808303815f875af115801561056b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061058f9190611045565b506040805160028082526060820183525f926020830190803683375050600354825192935073ffffffffffffffffffffffffffffffffffffffff16918391505f906105dc576105dc61108d565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050888160018151811061062a5761062a61108d565b73ffffffffffffffffffffffffffffffffffffffff92831660209182029290920101525f9084166338ed17398888853061066642610e106110ba565b6040518663ffffffff1660e01b81526004016106869594939291906110f8565b5f604051808303815f875af11580156106a1573d5f803e3d5ffd5b505050506040513d5f823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526106e69190810190611182565b9050806001815181106106fb576106fb61108d565b60209081029190910101516040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8a8116600483015260248201839052919650908b169063095ea7b3906044016020604051808303815f875af115801561077c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107a09190611045565b506040517fa694fc3a0000000000000000000000000000000000000000000000000000000081526004810186905273ffffffffffffffffffffffffffffffffffffffff84169063a694fc3a906024015f604051808303815f87803b158015610806575f80fd5b505af1158015610818573d5f803e3d5ffd5b50505050610827883387610c48565b6040805173ffffffffffffffffffffffffffffffffffffffff8b811682528a8116602083015291810189905260608101879052908b16907fa73e61301ee5e018e2d47ce42cdfcf519eaed0ff45f1f626d780908f344ea9119060800160405180910390a25050505095945050505050565b5f5473ffffffffffffffffffffffffffffffffffffffff1633146108e8576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8416610935576040517f6df3583d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8373ffffffffffffffffffffffffffffffffffffffff16638e3bc0ac6040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156109ba575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526109b791810190611268565b60015b6109f0576040517f6df3583d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b508373ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16635c80aeee6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a51573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a75919061127f565b73ffffffffffffffffffffffffffffffffffffffff1614610ac2576040517f2156216000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff166372f702f36040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b22573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b46919061127f565b73ffffffffffffffffffffffffffffffffffffffff1614610b93576040517f91f9b3dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8481165f8181526004602090815260408083208886168085529083528184209588168085529583529281902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001687151590811790915581519384529183019490945292810192909252907f6fce32a4fb69cfc2ab72262a5a942271f18af7e0b35869d4bf829e48eca253e59060600160405180910390a250505050565b6040805173ffffffffffffffffffffffffffffffffffffffff8481166024830152604480830185905283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb0000000000000000000000000000000000000000000000000000000017905291515f92839290871691610cde919061129a565b5f604051808303815f865af19150503d805f8114610d17576040519150601f19603f3d011682016040523d82523d5f602084013e610d1c565b606091505b5091509150811580610d4a5750805115801590610d4a575080806020019051810190610d489190611045565b155b15610d81576040517f19a9708500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050505050565b6040805173ffffffffffffffffffffffffffffffffffffffff85811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd0000000000000000000000000000000000000000000000000000000017905291515f92839290881691610e26919061129a565b5f604051808303815f865af19150503d805f8114610e5f576040519150601f19603f3d011682016040523d82523d5f602084013e610e64565b606091505b5091509150811580610e925750805115801590610e92575080806020019051810190610e909190611045565b155b15610ec8576040517e298ffd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050565b73ffffffffffffffffffffffffffffffffffffffff81168114610ef1575f80fd5b50565b5f60208284031215610f04575f80fd5b8135610f0f81610ed0565b9392505050565b5f805f60608486031215610f28575f80fd5b8335610f3381610ed0565b92506020840135610f4381610ed0565b91506040840135610f5381610ed0565b809150509250925092565b5f8060408385031215610f6f575f80fd5b8235610f7a81610ed0565b946020939093013593505050565b5f805f805f60a08688031215610f9c575f80fd5b8535610fa781610ed0565b94506020860135610fb781610ed0565b93506040860135610fc781610ed0565b94979396509394606081013594506080013592915050565b8015158114610ef1575f80fd5b5f805f8060808587031215610fff575f80fd5b843561100a81610ed0565b9350602085013561101a81610ed0565b9250604085013561102a81610ed0565b9150606085013561103a81610fdf565b939692955090935050565b5f60208284031215611055575f80fd5b8151610f0f81610fdf565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b808201808211156110f2577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b92915050565b5f60a0820187835286602084015260a0604084015280865180835260c0850191506020880192505f5b8181101561115557835173ffffffffffffffffffffffffffffffffffffffff16835260209384019390920191600101611121565b505073ffffffffffffffffffffffffffffffffffffffff9590951660608401525050608001529392505050565b5f60208284031215611192575f80fd5b815167ffffffffffffffff8111156111a8575f80fd5b8201601f810184136111b8575f80fd5b805167ffffffffffffffff8111156111d2576111d2611060565b8060051b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f830116810181811067ffffffffffffffff8211171561121d5761121d611060565b60405291825260208184018101929081018784111561123a575f80fd5b6020850194505b8385101561125d57845180825260209586019590935001611241565b509695505050505050565b5f60208284031215611278575f80fd5b5051919050565b5f6020828403121561128f575f80fd5b8151610f0f81610ed0565b5f82518060208501845e5f92019182525091905056fea264697066735822122061170840bd936c1f9f0aad0cc103505fe3fde9cbecad1ed0f737e726a92414ed64736f6c634300081a0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000625e700125ff054f75e5348497cbfab1ee4b7a40
-----Decoded View---------------
Arg [0] : _owner (address): 0x625e700125FF054f75e5348497cBFab1ee4b7A40
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000625e700125ff054f75e5348497cbfab1ee4b7a40
Deployed Bytecode Sourcemap
69693:6048:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;16128:141;;;;;;:::i;:::-;;:::i;:::-;;70065:112;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1124:14:1;;1117:22;1099:41;;1087:2;1072:18;70065:112:0;;;;;;;;15838:29;;;;;;;;;;;;1327:42:1;1315:55;;;1297:74;;1285:2;1270:18;15838:29:0;1151:226:1;16277:354:0;;;:::i;73870:305::-;;;;;;:::i;:::-;;:::i;69914:69::-;;;;;;;;;15811:20;;;;;;;;;70961:1570;;;;;;:::i;:::-;;:::i;:::-;;;2921:25:1;;;2909:2;2894:18;70961:1570:0;2775:177:1;72902:722:0;;;;;;:::i;:::-;;:::i;16128:141::-;16782:5;;;;16768:10;:19;16764:43;;16796:11;;;;;;;;;;;;;;16764:43;16200:14:::1;:23:::0;;;::::1;;::::0;::::1;::::0;;::::1;::::0;;;16239:22:::1;::::0;1297:74:1;;;16239:22:0::1;::::0;1285:2:1;1270:18;16239:22:0::1;;;;;;;16128:141:::0;:::o;16277:354::-;16450:14;;;;16436:10;:28;16432:69;;16473:28;;;;;;;;;;;;;;16432:69;16530:5;;;16537:14;16517:35;;;16530:5;;;;3924:74:1;;16537:14:0;;;;4029:2:1;4014:18;;4007:83;16517:35:0;;3897:18:1;16517:35:0;;;;;;;16571:14;;;;16563:22;;;;;;16571:14;;;16563:22;;;;16596:27;;;16277:354::o;73870:305::-;16782:5;;;;16768:10;:19;16764:43;;16796:11;;;;;;;;;;;;;;16764:43;74086:5:::1;::::0;74043:63:::1;::::0;74071:13;;74086:5:::1;;74093:12:::0;74043:27:::1;:63::i;:::-;74124:43;::::0;;4305:42:1;4293:55;;4275:74;;4380:2;4365:18;;4358:34;;;74124:43:0::1;::::0;4248:18:1;74124:43:0::1;;;;;;;73870:305:::0;;:::o;70961:1570::-;71267:4;;71159:15;;71227:82;;71267:4;;71274:10;71294:4;71301:7;71227:31;:82::i;:::-;71366:23;;;;;;;;:13;:23;;;;;;;;:37;;;;;;;;;;:50;;;;;;;;;;;;71361:78;;71425:14;;;;;;;;;;;;;;71361:78;71684:4;;:35;;;;;:4;4293:55:1;;;71684:35:0;;;4275:74:1;4365:18;;;4358:34;;;71561:12:0;;71619:11;;71684:4;;;:12;;4248:18:1;;71684:35:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;71786:16:0;;;71800:1;71786:16;;;;;;;;71761:22;;71786:16;;;;;;;;-1:-1:-1;;71832:4:0;;71813:8;;;;-1:-1:-1;71832:4:0;;;71813:8;;-1:-1:-1;71832:4:0;;71813:8;;;;:::i;:::-;;;;;;:24;;;;;;;;;;;71859:8;71848:5;71854:1;71848:8;;;;;;;;:::i;:::-;:19;;;;:8;;;;;;;;;:19;71918:25;;71946:33;;;71994:7;72016:10;72041:5;72069:4;72089:22;:15;72107:4;72089:22;:::i;:::-;71946:176;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;71918:204;;72143:8;72152:1;72143:11;;;;;;;;:::i;:::-;;;;;;;;;;;72214:45;;;;;:23;4293:55:1;;;72214:45:0;;;4275:74:1;4365:18;;;4358:34;;;72143:11:0;;-1:-1:-1;72214:23:0;;;;;;4248:18:1;;72214:45:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;72296:22:0;;;;;;;;2921:25:1;;;72296:13:0;;;;;;2894:18:1;;72296:22:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;72379:61;72407:11;72420:10;72432:7;72379:27;:61::i;:::-;72458:65;;;;7795:55:1;;;7777:74;;7887:55;;;7882:2;7867:18;;7860:83;7959:18;;;7952:34;;;8017:2;8002:18;;7995:34;;;72458:65:0;;;;;;7764:3:1;7749:19;72458:65:0;;;;;;;71176:1355;;;;70961:1570;;;;;;;:::o;72902:722::-;16782:5;;;;16768:10;:19;16764:43;;16796:11;;;;;;;;;;;;;;16764:43;73048:18:::1;::::0;::::1;73044:43;;73075:12;;;;;;;;;;;;;;73044:43;73192:4;73187:29;;;:31;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1::0;73187:31:0::1;::::0;;::::1;;::::0;;::::1;::::0;::::1;::::0;::::1;::::0;;;::::1;::::0;;::::1;::::0;::::1;:::i;:::-;;;73183:91;;73250:12;;;;;;;;;;;;;;73183:91;;73331:4;73288:47;;73305:8;73288:37;;;:39;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:47;;;73284:76;;73344:16;;;;;;;;;;;;;;73284:76;73423:4;73375:52;;73395:7;73383:33;;;:35;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;73375:52;;;73371:85;;73436:20;;;;;;;;;;;;;;73371:85;73502:19;::::0;;::::1;;::::0;;;:13:::1;:19;::::0;;;;;;;:29;;::::1;::::0;;;;;;;;;:38;;::::1;::::0;;;;;;;;;;:48;;;::::1;::::0;::::1;;::::0;;::::1;::::0;;;73568;;8997:74:1;;;9087:18;;;9080:83;;;;9179:18;;;9172:50;;;;73502:19:0;73568:48:::1;::::0;8985:2:1;8970:18;73568:48:0::1;;;;;;;72902:722:::0;;;;:::o;26448:480::-;26646:45;;;26635:10;4293:55:1;;;26646:45:0;;;4275:74:1;4365:18;;;;4358:34;;;26646:45:0;;;;;;;;;;4248:18:1;;;;26646:45:0;;;;;;;;;;;;;26635:57;;-1:-1:-1;;;;26635:10:0;;;;:57;;26646:45;26635:57;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;26599:93;;;;26823:7;26822:8;:59;;;-1:-1:-1;26835:11:0;;:16;;;;:45;;;26867:4;26856:24;;;;;;;;;;;;:::i;:::-;26855:25;26835:45;26818:102;;;26890:30;;;;;;;;;;;;;;26818:102;26521:407;;26448:480;;;:::o;26936:525::-;27164:51;;;27153:10;9759:55:1;;;27164:51:0;;;9741:74:1;9851:55;;;9831:18;;;9824:83;9923:18;;;;9916:34;;;27164:51:0;;;;;;;;;;9714:18:1;;;;27164:51:0;;;;;;;;;;;;;27153:63;;-1:-1:-1;;;;27153:10:0;;;;:63;;27164:51;27153:63;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27117:99;;;;27352:7;27351:8;:59;;;-1:-1:-1;27364:11:0;;:16;;;;:45;;;27396:4;27385:24;;;;;;;;;;;;:::i;:::-;27384:25;27364:45;27347:106;;;27419:34;;;;;;;;;;;;;;27347:106;27027:434;;26936:525;;;;:::o;14:154:1:-;100:42;93:5;89:54;82:5;79:65;69:93;;158:1;155;148:12;69:93;14:154;:::o;173:247::-;232:6;285:2;273:9;264:7;260:23;256:32;253:52;;;301:1;298;291:12;253:52;340:9;327:23;359:31;384:5;359:31;:::i;:::-;409:5;173:247;-1:-1:-1;;;173:247:1:o;425:529::-;502:6;510;518;571:2;559:9;550:7;546:23;542:32;539:52;;;587:1;584;577:12;539:52;626:9;613:23;645:31;670:5;645:31;:::i;:::-;695:5;-1:-1:-1;752:2:1;737:18;;724:32;765:33;724:32;765:33;:::i;:::-;817:7;-1:-1:-1;876:2:1;861:18;;848:32;889:33;848:32;889:33;:::i;:::-;941:7;931:17;;;425:529;;;;;:::o;1382:367::-;1450:6;1458;1511:2;1499:9;1490:7;1486:23;1482:32;1479:52;;;1527:1;1524;1517:12;1479:52;1566:9;1553:23;1585:31;1610:5;1585:31;:::i;:::-;1635:5;1713:2;1698:18;;;;1685:32;;-1:-1:-1;;;1382:367:1:o;1999:771::-;2094:6;2102;2110;2118;2126;2179:3;2167:9;2158:7;2154:23;2150:33;2147:53;;;2196:1;2193;2186:12;2147:53;2235:9;2222:23;2254:31;2279:5;2254:31;:::i;:::-;2304:5;-1:-1:-1;2361:2:1;2346:18;;2333:32;2374:33;2333:32;2374:33;:::i;:::-;2426:7;-1:-1:-1;2485:2:1;2470:18;;2457:32;2498:33;2457:32;2498:33;:::i;:::-;1999:771;;;;-1:-1:-1;2550:7:1;;2630:2;2615:18;;2602:32;;-1:-1:-1;2733:3:1;2718:19;2705:33;;1999:771;-1:-1:-1;;1999:771:1:o;2957:118::-;3043:5;3036:13;3029:21;3022:5;3019:32;3009:60;;3065:1;3062;3055:12;3080:665;3163:6;3171;3179;3187;3240:3;3228:9;3219:7;3215:23;3211:33;3208:53;;;3257:1;3254;3247:12;3208:53;3296:9;3283:23;3315:31;3340:5;3315:31;:::i;:::-;3365:5;-1:-1:-1;3422:2:1;3407:18;;3394:32;3435:33;3394:32;3435:33;:::i;:::-;3487:7;-1:-1:-1;3546:2:1;3531:18;;3518:32;3559:33;3518:32;3559:33;:::i;:::-;3611:7;-1:-1:-1;3670:2:1;3655:18;;3642:32;3683:30;3642:32;3683:30;:::i;:::-;3080:665;;;;-1:-1:-1;3080:665:1;;-1:-1:-1;;3080:665:1:o;4403:245::-;4470:6;4523:2;4511:9;4502:7;4498:23;4494:32;4491:52;;;4539:1;4536;4529:12;4491:52;4571:9;4565:16;4590:28;4612:5;4590:28;:::i;4653:184::-;4705:77;4702:1;4695:88;4802:4;4799:1;4792:15;4826:4;4823:1;4816:15;4842:184;4894:77;4891:1;4884:88;4991:4;4988:1;4981:15;5015:4;5012:1;5005:15;5031:279;5096:9;;;5117:10;;;5114:190;;;5160:77;5157:1;5150:88;5261:4;5258:1;5251:15;5289:4;5286:1;5279:15;5114:190;5031:279;;;;:::o;5315:997::-;5569:4;5617:3;5606:9;5602:19;5648:6;5637:9;5630:25;5691:6;5686:2;5675:9;5671:18;5664:34;5734:3;5729:2;5718:9;5714:18;5707:31;5758:6;5793;5787:13;5824:6;5816;5809:22;5862:3;5851:9;5847:19;5840:26;;5901:2;5893:6;5889:15;5875:29;;5922:1;5932:218;5946:6;5943:1;5940:13;5932:218;;;6011:13;;6026:42;6007:62;5995:75;;6099:2;6125:15;;;;6090:12;;;;5968:1;5961:9;5932:218;;;-1:-1:-1;;6218:42:1;6206:55;;;;6201:2;6186:18;;6179:83;-1:-1:-1;;6293:3:1;6278:19;6271:35;6167:3;5315:997;-1:-1:-1;;;5315:997:1:o;6317:1224::-;6412:6;6465:2;6453:9;6444:7;6440:23;6436:32;6433:52;;;6481:1;6478;6471:12;6433:52;6514:9;6508:16;6547:18;6539:6;6536:30;6533:50;;;6579:1;6576;6569:12;6533:50;6602:22;;6655:4;6647:13;;6643:27;-1:-1:-1;6633:55:1;;6684:1;6681;6674:12;6633:55;6717:2;6711:9;6743:18;6735:6;6732:30;6729:56;;;6765:18;;:::i;:::-;6811:6;6808:1;6804:14;6847:2;6841:9;6906:66;6901:2;6897;6893:11;6889:84;6881:6;6877:97;7040:6;7028:10;7025:22;7004:18;6992:10;6989:34;6986:62;6983:88;;;7051:18;;:::i;:::-;7087:2;7080:22;7137;;;7187:2;7217:11;;;7213:20;;;7137:22;7175:15;;7245:19;;;7242:39;;;7277:1;7274;7267:12;7242:39;7309:2;7305;7301:11;7290:22;;7321:189;7337:6;7332:3;7329:15;7321:189;;;7427:10;;7450:18;;;7497:2;7354:12;;;;7427:10;;-1:-1:-1;7488:12:1;7321:189;;;-1:-1:-1;7529:6:1;6317:1224;-1:-1:-1;;;;;;6317:1224:1:o;8040:230::-;8110:6;8163:2;8151:9;8142:7;8138:23;8134:32;8131:52;;;8179:1;8176;8169:12;8131:52;-1:-1:-1;8224:16:1;;8040:230;-1:-1:-1;8040:230:1:o;8275:251::-;8345:6;8398:2;8386:9;8377:7;8373:23;8369:32;8366:52;;;8414:1;8411;8404:12;8366:52;8446:9;8440:16;8465:31;8490:5;8465:31;:::i;9233:301::-;9362:3;9400:6;9394:13;9446:6;9439:4;9431:6;9427:17;9422:3;9416:37;9508:1;9472:16;;9497:13;;;-1:-1:-1;9472:16:1;9233:301;-1:-1:-1;9233:301:1:o
Swarm Source
ipfs://61170840bd936c1f9f0aad0cc103505fe3fde9cbecad1ed0f737e726a92414ed
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in FRAX
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.