Description:
ERC20 token contract with Factory, Oracle capabilities. Standard implementation for fungible tokens on Ethereum.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC-20 standard.
*/
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);
}
"
},
"@openzeppelin/contracts/token/ERC20/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
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 value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` 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 value) external returns (bool);
}
"
},
"contracts/interfaces/IAppOracle.sol": {
"content": "// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.28;
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
interface IAppOracle {
event OracleUpdated(address indexed token, address indexed oracle, uint256 maxStaleness);
event FloorPriceUpdated(uint256 oldPrice, uint256 newPrice);
event PriceFetched(
address indexed token, uint256 amount, uint256 rzrAmount, uint256 usdAmount, uint256 lastUpdatedAt
);
// Errors
error OracleNotFound(address token);
error OracleAlreadyExists(address token);
error OracleInactive(address token);
error InvalidOracleAddress();
error InvalidTokenAddress();
/// @notice Initializes the AppOracle contract
/// @dev This function is only callable once
/// @param _authority The address of the authority contract
/// @param _app The address of the app contract
function initialize(address _authority, address _app) external;
/**
* @notice Update the oracle for a token
* @param token The token address
* @param oracle The oracle contract
*/
function updateOracle(address token, address oracle, uint256 maxStaleness) external;
/**
* @notice Get the price for a token
* @param token The token address
* @return rzrAmount The token price in RZR
* @return usdAmount The token price in USD
* @return lastUpdatedAt The timestamp of the last update
*/
function getPrice(address token)
external
view
returns (uint256 rzrAmount, uint256 usdAmount, uint256 lastUpdatedAt);
/**
* @notice Get the price for a token in RZR for an amount
* @param token The token address
* @param amount The amount of the token
* @return rzrAmount The token price in RZR for the amount
* @return usdAmount The token price in USD for the amount
* @return lastUpdatedAt The timestamp of the last update
*/
function getPriceForAmount(address token, uint256 amount)
external
view
returns (uint256 rzrAmount, uint256 usdAmount, uint256 lastUpdatedAt);
/**
* @notice Get the price for a token in RZR for an amount in the floor price
* @param token The token address
* @param amount The amount of the token
* @return rzrAmount The token price in RZR for the amount
* @return usdAmount The token price in USD for the amount
* @return lastUpdatedAt The timestamp of the last update
*/
function getPriceForAmountInFloor(address token, uint256 amount)
external
view
returns (uint256 rzrAmount, uint256 usdAmount, uint256 lastUpdatedAt);
/**
* @notice Get the floor price for RZR
* @return price The RZR floor price
*/
function getTokenPrice() external view returns (uint256);
/**
* @notice Set the floor price for RZR
* @param newFloorPrice The new RZR price
*/
function setTokenPrice(uint256 newFloorPrice) external;
}
"
},
"contracts/interfaces/IOracleV2.sol": {
"content": "// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.28;
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
interface IOracleV2 {
/// @notice Get the asset of the oracle
/// @return asset The address of the asset
function asset() external view returns (IERC20Metadata);
/// @notice Get the price of an asset for a given amount
/// @dev If the asset is an LP token, the RZR and USD amounts are the amounts of RZR and USD in the LP
/// @param amount The amount of the asset to get the price of
/// @return rzrAssets The amount of RZR assets
/// @return usdAssets The amount of USD assets
/// @return lastUpdatedAt The timestamp of the last update
function getPriceForAmount(uint256 amount)
external
view
returns (uint256 rzrAssets, uint256 usdAssets, uint256 lastUpdatedAt);
}
"
},
"contracts/periphery/ui-helpers/UserRoleChecker.sol": {
"content": "// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.28;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../../interfaces/IAppOracle.sol";
import "../../interfaces/IOracleV2.sol";
/// @title UserRoleChecker
/// @notice A UI helper contract that provides read functions
/// to check various user roles and conditions with detailed metrics
contract UserRoleChecker {
/// @notice The app oracle
IAppOracle public immutable appOracle;
/// @notice The spot oracle
IOracleV2 public immutable spotOracle;
struct CheckIfHoldingParams {
IERC20 token;
IERC20[] forwarders;
}
struct CheckIfHoldingMultipleParams {
CheckIfHoldingParams[] params;
}
/**
* @notice Constructor
* @param _appOracle The address of the app oracle
* @param _spotOracle The address of the spot oracle
*/
constructor(address _appOracle, address _spotOracle) {
appOracle = IAppOracle(_appOracle);
spotOracle = IOracleV2(_spotOracle);
}
/**
* @notice Check if a user is holding a specific set of tokens
* @param user The address of the user to check
* @param token The token to check
* @param forwarders The addresses of the forwarders to check
* @return isHolding Whether the user is holding the tokens
* @return totalUsdValue The total USD value of the tokens
* @return rzrValue The total RZR value of the tokens
*/
function checkIfHolding(address user, IERC20 token, IERC20[] memory forwarders) public view returns (bool isHolding, uint256 totalUsdValue, uint256 rzrValue) {
uint256 spotPrice = spotPrice();
uint256 balance = token.balanceOf(user);
for (uint256 j = 0; j < forwarders.length; j++) {
balance += forwarders[j].balanceOf(user);
}
(uint256 rzrAmount, uint256 usdAmount,) = appOracle.getPriceForAmount(address(token), balance);
totalUsdValue += usdAmount + (rzrAmount * spotPrice / 1e18);
rzrValue += rzrAmount;
isHolding = totalUsdValue > 0;
}
/**
* @notice Check if a user is holding a specific set of tokens
* @param user The address of the user to check
* @param tokens The addresses of the tokens to check
* @param forwarders The addresses of the forwarders to check
* @return isHolding Whether the user is holding the tokens
* @return totalUsdValue The total USD value of the tokens
* @return rzrValue The total RZR value of the tokens
*/
function checkIfHoldingMultiple(address user, IERC20[] memory tokens, IERC20[][] memory forwarders) public view returns (bool isHolding, uint256 totalUsdValue, uint256 rzrValue) {
for (uint256 i = 0; i < tokens.length; i++) {
(bool _isHolding, uint256 _usdAmount, uint256 _rzrAmount) = checkIfHolding(user, tokens[i], forwarders[i]);
isHolding = isHolding || _isHolding;
totalUsdValue += _usdAmount;
rzrValue += _rzrAmount;
}
}
/**
* @notice Check if a user is holding a specific set of tokens
* @param user The address of the user to check
* @param data The data to decode
* @return isHolding Whether the user is holding the tokens
* @return totalUsdValue The total USD value of the tokens
* @return rzrValue The total RZR value of the tokens
*/
function checkIfHoldingWithData(address user, bytes memory data) public view returns (bool isHolding, uint256 totalUsdValue, uint256 rzrValue) {
CheckIfHoldingMultipleParams memory params = abi.decode(data, (CheckIfHoldingMultipleParams));
for (uint256 i = 0; i < params.params.length; i++) {
(bool _isHolding, uint256 _usdAmount, uint256 _rzrAmount) = checkIfHolding(user, params.params[i].token, params.params[i].forwarders);
isHolding = isHolding || _isHolding;
totalUsdValue += _usdAmount;
rzrValue += _rzrAmount;
}
}
/**
* @notice Get the USD value of a user's holdings
* @param user The address of the user to check
* @param data The data to decode
* @return totalUsdValue The total USD value of the user's holdings
*/
function getUsdValue(address user, bytes memory data) public view returns (uint256 totalUsdValue) {
CheckIfHoldingMultipleParams memory params = abi.decode(data, (CheckIfHoldingMultipleParams));
for (uint256 i = 0; i < params.params.length; i++) {
(, uint256 _usdAmount, ) = checkIfHolding(user, params.params[i].token, params.params[i].forwarders);
totalUsdValue += _usdAmount;
}
}
/**
* @notice Get the RZR value of a user's holdings
* @param user The address of the user to check
* @param data The data to decode
* @return totalRzrValue The total RZR value of the user's holdings
*/
function getRzrValue(address user, bytes memory data) public view returns (uint256 totalRzrValue) {
CheckIfHoldingMultipleParams memory params = abi.decode(data, (CheckIfHoldingMultipleParams));
for (uint256 i = 0; i < params.params.length; i++) {
(, , uint256 _rzrAmount) = checkIfHolding(user, params.params[i].token, params.params[i].forwarders);
totalRzrValue += _rzrAmount;
}
}
/**
* @notice Get the spot price
* @return spotPrice The spot price
*/
function spotPrice() public view returns (uint256) {
(,uint256 spotPrice,) = spotOracle.getPriceForAmount(1e18);
return spotPrice;
}
}
"
}
},
"settings": {
"optimizer": {
"enabled": true,
"runs": 2000
},
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"metadata": {
"useLiteralContent": true
}
}
}}
Submitted on: 2025-09-27 11:51:04
Comments
Log in to comment.
No comments yet.