Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"src/integrations/farms/LevelFarm.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {FixedPointMathLib} from "@solmate/src/utils/FixedPointMathLib.sol";
import {Farm} from "@integrations/Farm.sol";
import {IFarm} from "@interfaces/IFarm.sol";
import {CoreRoles} from "@libraries/CoreRoles.sol";
import {Accounting} from "@finance/Accounting.sol";
import {IMaturityFarm} from "@interfaces/IMaturityFarm.sol";
import {MultiAssetFarm} from "@integrations/MultiAssetFarm.sol";
struct UserCooldown {
uint104 cooldownEnd;
uint152 underlyingAmount;
}
interface ISLVLUSD {
function unstake(address receiver) external;
function cooldownShares(uint256 shares) external;
function cooldowns(address user) external view returns (UserCooldown memory);
}
interface ILvlMinting {
function pendingRedemption(address user, address asset) external view returns (uint256);
function initiateRedeem(address asset, uint256 lvlUsdAmount, uint256 minAssetAmount)
external
returns (uint256, uint256);
function completeRedeem(address asset, address beneficiary) external returns (uint256);
}
/// @title LevelFarm
/// @notice This contract can hold USDC, lvlUSD, and slvlUSD.
/// @dev exit only, no new deposits as the protocol announced their shutdown.
contract LevelFarm is MultiAssetFarm, IMaturityFarm {
using SafeERC20 for IERC20;
using FixedPointMathLib for uint256;
address public constant _USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
address public constant _LVLUSD = 0x7C1156E515aA1A2E851674120074968C905aAF37;
address public constant _SLVLUSD = 0x4737D9b4592B40d51e110b94c9C043c6654067Ae;
address public constant _LVL_MINTING = 0x9136aB0294986267b71BeED86A75eeb3336d09E1;
constructor(address _core, address _accounting) MultiAssetFarm(_core, _USDC, _accounting) {}
function assets() public view virtual override(IFarm, MultiAssetFarm) returns (uint256) {
uint256 usdcBalance = IERC20(_USDC).balanceOf(address(this));
uint256 usdcPrice = Accounting(accounting).price(_USDC);
uint256 lvlusdBalance = IERC20(_LVLUSD).balanceOf(address(this));
uint256 lvlusdPrice = Accounting(accounting).price(_LVLUSD);
uint256 slvlusdBalance = IERC20(_SLVLUSD).balanceOf(address(this));
uint256 slvlusdPrice = Accounting(accounting).price(_SLVLUSD);
// add lvlUSD in the process of unstaking
lvlusdBalance += ISLVLUSD(_SLVLUSD).cooldowns(address(this)).underlyingAmount;
// add USDC in the process of redeeming
usdcBalance += ILvlMinting(_LVL_MINTING).pendingRedemption(address(this), _USDC);
usdcBalance += lvlusdBalance.mulDivDown(lvlusdPrice, usdcPrice);
usdcBalance += slvlusdBalance.mulDivDown(slvlusdPrice, usdcPrice);
return usdcBalance;
}
function assetTokens() public pure override returns (address[] memory) {
address[] memory tokens = new address[](3);
tokens[0] = _USDC;
tokens[1] = _LVLUSD;
tokens[2] = _SLVLUSD;
return tokens;
}
function isAssetSupported(address _asset) public pure override returns (bool) {
return _asset == _USDC || _asset == _LVLUSD || _asset == _SLVLUSD;
}
function maturity() public view virtual override returns (uint256) {
return 1759177354;
}
/// @notice Begin unstaking process of slvlUSD to lvlUSD.
function cooldownShares(uint256 _slvlusdToUnstake) external whenNotPaused {
ISLVLUSD(_SLVLUSD).cooldownShares(_slvlusdToUnstake);
}
/// @notice Complete the unstaking process of slvlUSD to lvlUSD.
function unstake() external whenNotPaused {
ISLVLUSD(_SLVLUSD).unstake(address(this));
}
/// @notice Begin redeeming lvlUSD to USDC.
function initiateRedeem(uint256 _lvlusdToRedeem) external whenNotPaused {
IERC20(_LVLUSD).approve(_LVL_MINTING, _lvlusdToRedeem);
ILvlMinting(_LVL_MINTING).initiateRedeem(_USDC, _lvlusdToRedeem, 0);
}
/// @notice Complete the redeeming process of lvlUSD to USDC.
function completeRedeem() external whenNotPaused {
ILvlMinting(_LVL_MINTING).completeRedeem(_USDC, address(this));
}
}
"
},
"lib/openzeppelin-contracts/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);
}
"
},
"lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
// bubble errors
if iszero(success) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
returnSize := returndatasize()
returnValue := mload(0)
}
if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
bool success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
returnSize := returndatasize()
returnValue := mload(0)
}
return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
}
}
"
},
"lib/solmate/src/utils/FixedPointMathLib.sol": {
"content": "// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLib {
/*//////////////////////////////////////////////////////////////
SIMPLIFIED FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
uint256 internal constant MAX_UINT256 = 2**256 - 1;
uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.
function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
}
function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
}
function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
}
function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
}
/*//////////////////////////////////////////////////////////////
LOW LEVEL FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
function mulDivDown(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
// Divide x * y by the denominator.
z := div(mul(x, y), denominator)
}
}
function mulDivUp(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
// If x * y modulo the denominator is strictly greater than 0,
// 1 is added to round up the division of x * y by the denominator.
z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
}
}
function rpow(
uint256 x,
uint256 n,
uint256 scalar
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
switch x
case 0 {
switch n
case 0 {
// 0 ** 0 = 1
z := scalar
}
default {
// 0 ** n = 0
z := 0
}
}
default {
switch mod(n, 2)
case 0 {
// If n is even, store scalar in z for now.
z := scalar
}
default {
// If n is odd, store x in z for now.
z := x
}
// Shifting right by 1 is like dividing by 2.
let half := shr(1, scalar)
for {
// Shift n right by 1 before looping to halve it.
n := shr(1, n)
} n {
// Shift n right by 1 each iteration to halve it.
n := shr(1, n)
} {
// Revert immediately if x ** 2 would overflow.
// Equivalent to iszero(eq(div(xx, x), x)) here.
if shr(128, x) {
revert(0, 0)
}
// Store x squared.
let xx := mul(x, x)
// Round to the nearest number.
let xxRound := add(xx, half)
// Revert if xx + half overflowed.
if lt(xxRound, xx) {
revert(0, 0)
}
// Set x to scaled xxRound.
x := div(xxRound, scalar)
// If n is even:
if mod(n, 2) {
// Compute z * x.
let zx := mul(z, x)
// If z * x overflowed:
if iszero(eq(div(zx, x), z)) {
// Revert if x is non-zero.
if iszero(iszero(x)) {
revert(0, 0)
}
}
// Round to the nearest number.
let zxRound := add(zx, half)
// Revert if zx + half overflowed.
if lt(zxRound, zx) {
revert(0, 0)
}
// Return properly scaled zxRound.
z := div(zxRound, scalar)
}
}
}
}
}
/*//////////////////////////////////////////////////////////////
GENERAL NUMBER UTILITIES
//////////////////////////////////////////////////////////////*/
function sqrt(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
let y := x // We start y at x, which will help us make our initial estimate.
z := 181 // The "correct" value is 1, but this saves a multiplication later.
// This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
// start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.
// We check y >= 2^(k + 8) but shift right by k bits
// each branch to ensure that if x >= 256, then y >= 256.
if iszero(lt(y, 0x10000000000000000000000000000000000)) {
y := shr(128, y)
z := shl(64, z)
}
if iszero(lt(y, 0x1000000000000000000)) {
y := shr(64, y)
z := shl(32, z)
}
if iszero(lt(y, 0x10000000000)) {
y := shr(32, y)
z := shl(16, z)
}
if iszero(lt(y, 0x1000000)) {
y := shr(16, y)
z := shl(8, z)
}
// Goal was to get z*z*y within a small factor of x. More iterations could
// get y in a tighter range. Currently, we will have y in [256, 256*2^16).
// We ensured y >= 256 so that the relative difference between y and y+1 is small.
// That's not possible if x < 256 but we can just verify those cases exhaustively.
// Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
// Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
// Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.
// For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
// (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.
// Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
// sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.
// There is no overflow risk here since y < 2^136 after the first branch above.
z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.
// Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
// If x+1 is a perfect square, the Babylonian method cycles between
// floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
// See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
// Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
// If you don't care whether the floor or ceil square root is returned, you can remove this statement.
z := sub(z, lt(div(x, z), z))
}
}
function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Mod x by y. Note this will return
// 0 instead of reverting if y is zero.
z := mod(x, y)
}
}
function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
// Divide x by y. Note this will return
// 0 instead of reverting if y is zero.
r := div(x, y)
}
}
function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Add 1 to x * y if x % y > 0. Note this will
// return 0 instead of reverting if y is zero.
z := add(gt(mod(x, y), 0), div(x, y))
}
}
}
"
},
"src/integrations/Farm.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {FixedPointMathLib} from "@solmate/src/utils/FixedPointMathLib.sol";
import {IFarm} from "@interfaces/IFarm.sol";
import {CoreRoles} from "@libraries/CoreRoles.sol";
import {CoreControlled} from "@core/CoreControlled.sol";
/// @notice InfiniFi Farm base contract
abstract contract Farm is CoreControlled, IFarm {
using FixedPointMathLib for uint256;
address public immutable assetToken;
/// @notice cap on the amount of assets that can be deposited into the farm
uint256 public cap;
/// @notice Max slippage for depositing and witdhrawing assets from the farm.
/// @dev Stored as a percentage with 18 decimals of precision, of the minimum
/// position size compared to the previous position size (so actually 1 - slippage).
/// @dev Set to 0 to disable slippage checks.
uint256 public maxSlippage;
error CapExceeded(uint256 newAmount, uint256 cap);
error SlippageTooHigh(uint256 minAssetsOut, uint256 assetsReceived);
event CapUpdated(uint256 newCap);
event MaxSlippageUpdated(uint256 newMaxSlippage);
constructor(address _core, address _assetToken) CoreControlled(_core) {
assetToken = _assetToken;
cap = type(uint256).max;
// default to 99.9999%
// most farms should not have deposits/withdrawals fees, unless explicitly
// implemented, and should at worst round against depositors which should
// only cause some wei of losses when our farms do a deposit/withdraw.
maxSlippage = 0.999999e18;
}
/// @notice set the deposit cap of the farm
function setCap(uint256 _newCap) external onlyCoreRole(CoreRoles.PROTOCOL_PARAMETERS) {
cap = _newCap;
emit CapUpdated(_newCap);
}
/// @notice set the max tolerated slippage for depositing and witdhrawing assets from the farm
function setMaxSlippage(uint256 _maxSlippage) external onlyCoreRole(CoreRoles.PROTOCOL_PARAMETERS) {
maxSlippage = _maxSlippage;
emit MaxSlippageUpdated(_maxSlippage);
}
// --------------------------------------------------------------------
// Accounting
// --------------------------------------------------------------------
function assets() public view virtual returns (uint256);
// --------------------------------------------------------------------
// Adapter logic
// --------------------------------------------------------------------
function maxDeposit() external view virtual returns (uint256) {
uint256 currentAssets = assets();
if (currentAssets >= cap) {
return 0;
}
return cap - currentAssets;
}
function deposit() external virtual onlyCoreRole(CoreRoles.FARM_MANAGER) whenNotPaused {
uint256 assetsToDeposit = ERC20(assetToken).balanceOf(address(this));
uint256 assetsBefore = assets();
if (assetsBefore + assetsToDeposit > cap) {
revert CapExceeded(assetsBefore + assetsToDeposit, cap);
}
_deposit(assetsToDeposit);
uint256 assetsAfter = assets();
uint256 assetsReceived = assetsAfter - assetsBefore;
// check slippage
uint256 minAssetsOut = assetsToDeposit.mulWadDown(maxSlippage);
require(assetsReceived >= minAssetsOut, SlippageTooHigh(minAssetsOut, assetsReceived));
emit AssetsUpdated(block.timestamp, assetsBefore, assetsAfter);
}
function _deposit(uint256 assetsToDeposit) internal virtual;
function withdraw(uint256 amount, address to) external virtual onlyCoreRole(CoreRoles.FARM_MANAGER) whenNotPaused {
uint256 assetsBefore = assets();
_withdraw(amount, to);
uint256 assetsAfter = assets();
uint256 assetsSpent = assetsBefore - assetsAfter;
uint256 minAssetsOut = assetsSpent.mulWadDown(maxSlippage);
require(amount >= minAssetsOut, SlippageTooHigh(minAssetsOut, amount));
emit AssetsUpdated(block.timestamp, assetsBefore, assetsAfter);
}
function _withdraw(uint256, address) internal virtual;
}
"
},
"src/interfaces/IFarm.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
/// @notice Interface for an InfiniFi Farm contract
interface IFarm {
/// @notice emitted when there is a deposit of withdrawal from the farm
event AssetsUpdated(uint256 timestamp, uint256 assetsBefore, uint256 assetsAfter);
// --------------------------------------------------------------------
// Accounting
// --------------------------------------------------------------------
/// @notice the cap of the farm
function cap() external view returns (uint256);
/// @notice the asset used by deposits and withdrawals in the farm
function assetToken() external view returns (address);
/// @notice the total assets in the farm, reported as a balance of asset()
function assets() external view returns (uint256);
// --------------------------------------------------------------------
// Adapter logic
// --------------------------------------------------------------------
/// @notice deposit all asset() held by the contract into the farm
function deposit() external;
/// @notice Returns the max deposit amount for the underlying protocol
function maxDeposit() external view returns (uint256);
/// @notice withdraw an amount of the asset() from the farm
/// @param amount Amount of assets to withdraw
/// @param to Address to receive the withdrawn assets
function withdraw(uint256 amount, address to) external;
/// @notice available number of assetToken() withdrawable instantly from the farm
function liquidity() external view returns (uint256);
}
"
},
"src/libraries/CoreRoles.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
/// @notice Holds a complete list of all roles which can be held by contracts inside the InfiniFi protocol.
library CoreRoles {
/// ----------- Core roles for access control --------------
/// @notice the all-powerful role. Controls all other roles and protocol functionality.
bytes32 internal constant GOVERNOR = keccak256("GOVERNOR");
/// @notice Can pause contracts in an emergency.
bytes32 internal constant PAUSE = keccak256("PAUSE");
/// @notice Can unpause contracts after an emergency.
bytes32 internal constant UNPAUSE = keccak256("UNPAUSE");
/// @notice can tweak protocol parameters
bytes32 internal constant PROTOCOL_PARAMETERS = keccak256("PROTOCOL_PARAMETERS");
/// @notice can manage minor roles
bytes32 internal constant MINOR_ROLES_MANAGER = keccak256("MINOR_ROLES_MANAGER");
/// ----------- User Flow Management -----------------------
/// @notice Granted to the user entry point of the system
bytes32 internal constant ENTRY_POINT = keccak256("ENTRY_POINT");
/// ----------- Token Management ---------------------------
/// @notice can mint DebtToken arbitrarily
bytes32 internal constant RECEIPT_TOKEN_MINTER = keccak256("RECEIPT_TOKEN_MINTER");
/// @notice can burn DebtToken tokens
bytes32 internal constant RECEIPT_TOKEN_BURNER = keccak256("RECEIPT_TOKEN_BURNER");
/// @notice can mint arbitrarily & burn held LockedPositionToken
bytes32 internal constant LOCKED_TOKEN_MANAGER = keccak256("LOCKED_TOKEN_MANAGER");
/// @notice can prevent transfers of LockedPositionToken
bytes32 internal constant TRANSFER_RESTRICTOR = keccak256("TRANSFER_RESTRICTOR");
/// ----------- Funds Management & Accounting --------------
/// @notice contract that can allocate funds between farms
bytes32 internal constant FARM_MANAGER = keccak256("FARM_MANAGER");
/// @notice addresses who can use the manual rebalancer
bytes32 internal constant MANUAL_REBALANCER = keccak256("MANUAL_REBALANCER");
/// @notice addresses who can use the periodic rebalancer
bytes32 internal constant PERIODIC_REBALANCER = keccak256("PERIODIC_REBALANCER");
/// @notice addresses who can move funds from farms to a safe address
bytes32 internal constant EMERGENCY_WITHDRAWAL = keccak256("EMERGENCY_WITHDRAWAL");
/// @notice addresses who can trigger swaps in Farms
bytes32 internal constant FARM_SWAP_CALLER = keccak256("FARM_SWAP_CALLER");
/// @notice can set oracles references within the system
bytes32 internal constant ORACLE_MANAGER = keccak256("ORACLE_MANAGER");
/// @notice trusted to report profit and losses in the system.
/// This role can be used to slash depositors in case of losses, and
/// can also deposit profits for distribution to end users.
bytes32 internal constant FINANCE_MANAGER = keccak256("FINANCE_MANAGER");
/// ----------- Timelock management ------------------------
/// The hashes are the same as OpenZeppelins's roles in TimelockController
/// @notice can propose new actions in timelocks
bytes32 internal constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE");
/// @notice can execute actions in timelocks after their delay
bytes32 internal constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");
/// @notice can cancel actions in timelocks
bytes32 internal constant CANCELLER_ROLE = keccak256("CANCELLER_ROLE");
}
"
},
"src/finance/Accounting.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {FixedPointMathLib} from "@solmate/src/utils/FixedPointMathLib.sol";
import {IFarm} from "@interfaces/IFarm.sol";
import {IOracle} from "@interfaces/IOracle.sol";
import {CoreRoles} from "@libraries/CoreRoles.sol";
import {FarmRegistry} from "@integrations/FarmRegistry.sol";
import {CoreControlled} from "@core/CoreControlled.sol";
import {FixedPriceOracle} from "@finance/oracles/FixedPriceOracle.sol";
/// @notice InfiniFi Accounting contract
contract Accounting is CoreControlled {
using FixedPointMathLib for uint256;
event PriceSet(uint256 indexed timestamp, address indexed asset, uint256 price);
event OracleSet(uint256 indexed timestamp, address indexed asset, address oracle);
/// @notice reference to the farm registry
address public immutable farmRegistry;
constructor(address _core, address _farmRegistry) CoreControlled(_core) {
farmRegistry = _farmRegistry;
}
/// @notice mapping from asset to oracle
mapping(address => address) public oracle;
/// @notice returns the price of an asset
function price(address _asset) external view returns (uint256) {
return IOracle(oracle[_asset]).price();
}
/// @notice set the oracle for an asset
function setOracle(address _asset, address _oracle) external onlyCoreRole(CoreRoles.ORACLE_MANAGER) {
oracle[_asset] = _oracle;
emit OracleSet(block.timestamp, _asset, _oracle);
}
/// -------------------------------------------------------------------------------------------
/// Reference token getters (e.g. USD for iUSD, ETH for iETH, ...)
/// @dev note that the "USD" token does not exist, it is just an abstract unit of account
/// used in the protocol to represent stablecoins pegged to USD, that allows to uniformly
/// account for a diverse reserve composed of USDC, DAI, FRAX, etc.
/// -------------------------------------------------------------------------------------------
/// @notice returns the sum of the value of all assets held on protocol contracts listed in the farm registry.
function totalAssetsValue() external view returns (uint256 _totalValue) {
address[] memory assets = FarmRegistry(farmRegistry).getEnabledAssets();
for (uint256 i = 0; i < assets.length; i++) {
uint256 assetPrice = IOracle(oracle[assets[i]]).price();
uint256 _assets = _calculateTotalAssets(FarmRegistry(farmRegistry).getAssetFarms(assets[i]));
_totalValue += _assets.mulWadDown(assetPrice);
}
}
/// @notice returns the sum of the value of all liquid assets held on protocol contracts listed in the farm registry.
/// @dev see totalAssetsValue()
function totalAssetsValueOf(uint256 _type) external view returns (uint256 _totalValue) {
address[] memory assets = FarmRegistry(farmRegistry).getEnabledAssets();
for (uint256 i = 0; i < assets.length; i++) {
uint256 assetPrice = IOracle(oracle[assets[i]]).price();
address[] memory assetFarms = FarmRegistry(farmRegistry).getAssetTypeFarms(assets[i], uint256(_type));
uint256 _assets = _calculateTotalAssets(assetFarms);
_totalValue += _assets.mulWadDown(assetPrice);
}
}
/// -------------------------------------------------------------------------------------------
/// Specific asset getters (e.g. USDC, DAI, ...)
/// -------------------------------------------------------------------------------------------
/// @notice returns the sum of the balance of all farms of a given asset.
function totalAssets(address _asset) external view returns (uint256) {
return _calculateTotalAssets(FarmRegistry(farmRegistry).getAssetFarms(_asset));
}
function totalAssetsOf(address _asset, uint256 _type) external view returns (uint256) {
return _calculateTotalAssets(FarmRegistry(farmRegistry).getAssetTypeFarms(_asset, uint256(_type)));
}
/// -------------------------------------------------------------------------------------------
/// Internal helpers
/// -------------------------------------------------------------------------------------------
function _calculateTotalAssets(address[] memory _farms) internal view returns (uint256 _totalAssets) {
uint256 length = _farms.length;
for (uint256 index = 0; index < length; index++) {
_totalAssets += IFarm(_farms[index]).assets();
}
}
}
"
},
"src/interfaces/IMaturityFarm.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {IFarm} from "@interfaces/IFarm.sol";
/// @notice Interface for an InfiniFi Farm contract that has a maturity date
/// @dev These farms represent illiquid farm/asset class
interface IMaturityFarm is IFarm {
/// @notice timestamp at which more funds can be made available for withdrawal
function maturity() external view returns (uint256);
}
"
},
"src/integrations/MultiAssetFarm.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {FixedPointMathLib} from "@solmate/src/utils/FixedPointMathLib.sol";
import {CoreRoles} from "@libraries/CoreRoles.sol";
import {Accounting} from "@finance/Accounting.sol";
import {Farm, IFarm} from "@integrations/Farm.sol";
/// @notice InfiniFi Farm that can hold multiple asset tokens.
abstract contract MultiAssetFarm is Farm {
using SafeERC20 for IERC20;
using FixedPointMathLib for uint256;
/// @notice reference to the accounting contract
address public immutable accounting;
error InvalidAsset(address asset);
error InvalidFarm(address farm);
constructor(address _core, address _assetToken, address _accounting) Farm(_core, _assetToken) {
accounting = _accounting;
}
/// @notice the asset tokens that the farm can hold.
/// @dev MUST include the assetToken of the farm.
/// @dev MUST only include tokens that can be freely airdropped to the farm
/// while being accounted properly in the assets() function.
function assetTokens() public view virtual returns (address[] memory);
/// @notice return true if the farm can hold the given asset token.
function isAssetSupported(address _asset) public view virtual returns (bool);
/// @dev note that there may be conversion fees between supported assets and the assetToken.
/// This is not reflected in the amount returned by assets().
function assets() public view virtual override returns (uint256) {
uint256 assetTokenBalance = IERC20(assetToken).balanceOf(address(this));
uint256 assetTokenPrice = Accounting(accounting).price(assetToken);
address[] memory supportedAssets = assetTokens();
for (uint256 i = 0; i < supportedAssets.length; i++) {
if (supportedAssets[i] == assetToken) continue;
uint256 balance = IERC20(supportedAssets[i]).balanceOf(address(this));
uint256 price = Accounting(accounting).price(supportedAssets[i]);
assetTokenBalance += balance.mulDivDown(price, assetTokenPrice);
}
return assetTokenBalance;
}
/// @notice Current liquidity of the farm is the held reference assetToken.
function liquidity() public view override returns (uint256) {
return IERC20(assetToken).balanceOf(address(this));
}
/// @dev Deposit does nothing, assetTokens are just held on this farm.
/// @dev There should be other functions to do conversions between the assetTokens or deploying
/// the funds to a productive yield source.
function _deposit(uint256) internal view virtual override {}
function deposit() external virtual override onlyCoreRole(CoreRoles.FARM_MANAGER) whenNotPaused {
uint256 currentAssets = assets();
if (currentAssets > cap) {
revert CapExceeded(currentAssets, cap);
}
_deposit(0);
/// @dev note that in airdrops we do not know the amount of assets before the deposit,
/// therefore we emit an event that contains twice the assets after the deposit.
emit AssetsUpdated(block.timestamp, currentAssets, currentAssets);
}
/// @dev Withdrawal can only handle the reference assetToken (i.e. the liquidity()).
/// @dev There should be other functions to do conversions between the assetTokens or pulling
/// the funds out of a productive yield source.
function _withdraw(uint256 _amount, address _to) internal virtual override {
IERC20(assetToken).safeTransfer(_to, _amount);
}
/// @notice withdraw the reference assetToken.
function withdraw(uint256 amount, address to)
external
virtual
override
onlyCoreRole(CoreRoles.FARM_MANAGER)
whenNotPaused
{
uint256 assetsBefore = assets();
_withdraw(amount, to);
emit AssetsUpdated(block.timestamp, assetsBefore, assetsBefore - amount);
}
/// @notice function used to withdraw any supported assetTokens.
function withdrawSecondaryAsset(address _asset, uint256 _amount, address _to)
external
onlyCoreRole(CoreRoles.FARM_MANAGER)
whenNotPaused
{
require(isAssetSupported(_asset) && _asset != assetToken, InvalidAsset(_asset));
uint256 assetsBefore = assets();
IERC20(_asset).safeTransfer(_to, _amount);
uint256 assetsAfter = assets();
emit AssetsUpdated(block.timestamp, assetsBefore, assetsAfter);
}
}
"
},
"lib/openzeppelin-contracts/contracts/interfaces/IERC1363.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";
/**
* @title IERC1363
* @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
*
* Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
* after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
*/
interface IERC1363 is IERC20, IERC165 {
/*
* Note: the ERC-165 identifier for this interface is 0xb0202a11.
* 0xb0202a11 ===
* bytes4(keccak256('transferAndCall(address,uint256)')) ^
* bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
* bytes4(keccak256('approveAndCall(address,uint256)')) ^
* bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
*/
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format, sent in call to `spender`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}
"
},
"lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.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}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* 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 ERC-20
* applications.
*/
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* Both 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 returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual 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 default value returned by this function, unless
* it's 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 returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual 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 `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` 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 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Skips emitting an {Approval} event indicating an allowance update. This is not
* required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
*
* 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 `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` 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.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` 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.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
* true using the following override:
*
* ```solidity
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner`'s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance < type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}
"
},
"src/core/CoreControlled.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol";
import {CoreRoles} from "@libraries/CoreRoles.sol";
import {InfiniFiCore} from "@core/InfiniFiCore.sol";
/// @notice Defines some modifiers and utilities around interacting with Core
abstract contract CoreControlled is Pausable {
error UnderlyingCallReverted(bytes returnData);
/// @notice emitted when the reference to core is updated
event CoreUpdate(address indexed oldCore, address indexed newCore);
/// @notice reference to Core
InfiniFiCore private _core;
constructor(address coreAddress) {
_core = InfiniFiCore(coreAddress);
}
/// @notice named onlyCoreRole to prevent collision with OZ onlyRole modifier
modifier onlyCoreRole(bytes32 role) {
require(_core.hasRole(role, msg.sender), "UNAUTHORIZED");
_;
}
/// @notice address of the Core contract referenced
function core() public view returns (InfiniFiCore) {
return _core;
}
/// @notice WARNING CALLING THIS FUNCTION CAN POTENTIALLY
/// BRICK A CONTRACT IF CORE IS SET INCORRECTLY
/// @notice set new reference to core
/// only callable by governor
/// @param newCore to reference
function setCore(address newCore) external onlyCoreRole(CoreRoles.GOVE
Submitted on: 2025-09-26 16:41:38
Comments
Log in to comment.
No comments yet.