// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms.
*
* Roles are referred to by their `bytes4` identifier. These are expected to be the
* signatures for all the functions in the contract. Special roles should be exposed
* in the external API and be unique:
*
* ```
* bytes4 public constant ROOT = 0x00000000;
* ```
*
* Roles represent restricted access to a function call. For that purpose, use {auth}:
*
* ```
* function foo() public auth {
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `ROOT`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {setRoleAdmin}.
*
* WARNING: The `ROOT` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it.
*/contractAccessControl{
structRoleData {
mapping (address=>bool) members;
bytes4 adminRole;
}
mapping (bytes4=> RoleData) private _roles;
bytes4publicconstant ROOT =0x00000000;
bytes4publicconstant ROOT4146650865 =0x00000000; // Collision protection for ROOT, test with ROOT12007226833()bytes4publicconstant LOCK =0xFFFFFFFF; // Used to disable further permissioning of a functionbytes4publicconstant LOCK8605463013 =0xFFFFFFFF; // Collision protection for LOCK, test with LOCK10462387368()/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role
*
* `ROOT` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/eventRoleAdminChanged(bytes4indexed role, bytes4indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call.
*/eventRoleGranted(bytes4indexed role, addressindexed account, addressindexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/eventRoleRevoked(bytes4indexed role, addressindexed account, addressindexed sender);
/**
* @dev Give msg.sender the ROOT role and create a LOCK role with itself as the admin role and no members.
* Calling setRoleAdmin(msg.sig, LOCK) means no one can grant that msg.sig role anymore.
*/constructor () {
_grantRole(ROOT, msg.sender); // Grant ROOT to msg.sender
_setRoleAdmin(LOCK, LOCK); // Create the LOCK role by setting itself as its own admin, creating an independent role tree
}
/**
* @dev Each function in the contract has its own role, identified by their msg.sig signature.
* ROOT can give and remove access to each function, lock any further access being granted to
* a specific action, or even create other roles to delegate admin control over a function.
*/modifierauth() {
require (_hasRole(msg.sig, msg.sender), "Access denied");
_;
}
/**
* @dev Allow only if the caller has been granted the admin role of `role`.
*/modifieradmin(bytes4 role) {
require (_hasRole(_getRoleAdmin(role), msg.sender), "Only admin");
_;
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/functionhasRole(bytes4 role, address account) externalviewreturns (bool) {
return _hasRole(role, account);
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/functiongetRoleAdmin(bytes4 role) externalviewreturns (bytes4) {
return _getRoleAdmin(role);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
* If ``role``'s admin role is not `adminRole` emits a {RoleAdminChanged} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/functionsetRoleAdmin(bytes4 role, bytes4 adminRole) externalvirtualadmin(role) {
_setRoleAdmin(role, adminRole);
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/functiongrantRole(bytes4 role, address account) externalvirtualadmin(role) {
_grantRole(role, account);
}
/**
* @dev Grants all of `role` in `roles` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - For each `role` in `roles`, the caller must have ``role``'s admin role.
*/functiongrantRoles(bytes4[] memory roles, address account) externalvirtual{
for (uint256 i =0; i < roles.length; i++) {
require (_hasRole(_getRoleAdmin(roles[i]), msg.sender), "Only admin");
_grantRole(roles[i], account);
}
}
/**
* @dev Sets LOCK as ``role``'s admin role. LOCK has no members, so this disables admin management of ``role``.
* Emits a {RoleAdminChanged} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/functionlockRole(bytes4 role) externalvirtualadmin(role) {
_setRoleAdmin(role, LOCK);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/functionrevokeRole(bytes4 role, address account) externalvirtualadmin(role) {
_revokeRole(role, account);
}
/**
* @dev Revokes all of `role` in `roles` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - For each `role` in `roles`, the caller must have ``role``'s admin role.
*/functionrevokeRoles(bytes4[] memory roles, address account) externalvirtual{
for (uint256 i =0; i < roles.length; i++) {
require (_hasRole(_getRoleAdmin(roles[i]), msg.sender), "Only admin");
_revokeRole(roles[i], account);
}
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/functionrenounceRole(bytes4 role, address account) externalvirtual{
require(account ==msg.sender, "Renounce only for self");
_revokeRole(role, account);
}
function_hasRole(bytes4 role, address account) internalviewreturns (bool) {
return _roles[role].members[account];
}
function_getRoleAdmin(bytes4 role) internalviewreturns (bytes4) {
return _roles[role].adminRole;
}
function_setRoleAdmin(bytes4 role, bytes4 adminRole) internalvirtual{
if (_getRoleAdmin(role) != adminRole) {
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, adminRole);
}
}
function_grantRole(bytes4 role, address account) internal{
if (!_hasRole(role, account)) {
_roles[role].members[account] =true;
emit RoleGranted(role, account, msg.sender);
}
}
function_revokeRole(bytes4 role, address account) internal{
if (_hasRole(role, account)) {
_roles[role].members[account] =false;
emit RoleRevoked(role, account, msg.sender);
}
}
}
Contract Source Code
File 2 of 19: AddressStringUtil.sol
// SPDX-License-Identifier: MITpragmasolidity >=0.5.0;libraryAddressStringUtil{
// converts an address to the uppercase hex string, extracting only len bytes (up to 20, multiple of 2)functiontoAsciiString(address addr, uint256 len) internalpurereturns (stringmemory) {
require(len %2==0&& len >0&& len <=40, "AddressStringUtil: INVALID_LEN");
bytesmemory s =newbytes(len);
uint256 addrNum =uint256(uint160(addr));
for (uint256 ii =0; ii < len ; ii +=2) {
uint8 b =uint8(addrNum >> (4* (38- ii)));
s[ii] = char(b >>4);
s[ii +1] = char(b &0x0f);
}
returnstring(s);
}
// hi and lo are only 4 bits and between 0 and 16// this method converts those values to the unicode/ascii code point for the hex representation// uses upper case for the charactersfunctionchar(uint8 b) privatepurereturns (bytes1 c) {
if (b <10) {
returnbytes1(b +0x30);
} else {
returnbytes1(b +0x37);
}
}
}
Contract Source Code
File 3 of 19: CastU256U128.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.0;libraryCastU256U128{
/// @dev Safely cast an uint256 to an uint128functionu128(uint256 x) internalpurereturns (uint128 y) {
require (x <=type(uint128).max, "Cast overflow");
y =uint128(x);
}
}
Contract Source Code
File 4 of 19: CastU256U32.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.0;libraryCastU256U32{
/// @dev Safely cast an uint256 to an u32functionu32(uint256 x) internalpurereturns (uint32 y) {
require (x <=type(uint32).max, "Cast overflow");
y =uint32(x);
}
}
Contract Source Code
File 5 of 19: Constants.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity 0.8.6;contractConstants{
bytes32 CHI ="CHI";
bytes32 RATE ="RATE";
bytes6 ETH ="00";
}
Contract Source Code
File 6 of 19: ERC20.sol
// SPDX-License-Identifier: MIT// Inspired on token.sol from DappHub. Natspec adpated from OpenZeppelin.pragmasolidity ^0.8.0;import"./IERC20Metadata.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* We have followed general OpenZeppelin guidelines: functions revert instead
* of returning `false` on failure. This behavior is nonetheless conventional
* and does not conflict with the expectations of ERC20 applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Calls to {transferFrom} do not check for allowance if the caller is the owner
* of the funds. This allows to reduce the number of approvals that are necessary.
*
* Finally, {transferFrom} does not decrease the allowance if it is set to
* type(uint256).max. This reduces the gas costs without any likely impact.
*/contractERC20isIERC20Metadata{
uint256internal _totalSupply;
mapping (address=>uint256) internal _balanceOf;
mapping (address=>mapping (address=>uint256)) internal _allowance;
stringpublicoverride name ="???";
stringpublicoverride symbol ="???";
uint8publicoverride decimals =18;
/**
* @dev Sets the values for {name}, {symbol} and {decimals}.
*/constructor(stringmemory name_, stringmemory symbol_, uint8 decimals_) {
name = name_;
symbol = symbol_;
decimals = decimals_;
}
/**
* @dev See {IERC20-totalSupply}.
*/functiontotalSupply() externalviewvirtualoverridereturns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/functionbalanceOf(address guy) externalviewvirtualoverridereturns (uint256) {
return _balanceOf[guy];
}
/**
* @dev See {IERC20-allowance}.
*/functionallowance(address owner, address spender) externalviewvirtualoverridereturns (uint256) {
return _allowance[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*/functionapprove(address spender, uint wad) externalvirtualoverridereturns (bool) {
return _setAllowance(msg.sender, spender, wad);
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - the caller must have a balance of at least `wad`.
*/functiontransfer(address dst, uint wad) externalvirtualoverridereturns (bool) {
return _transfer(msg.sender, dst, wad);
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* Requirements:
*
* - `src` must have a balance of at least `wad`.
* - the caller is not `src`, it must have allowance for ``src``'s tokens of at least
* `wad`.
*//// if_succeeds {:msg "TransferFrom - decrease allowance"} msg.sender != src ==> old(_allowance[src][msg.sender]) >= wad;functiontransferFrom(address src, address dst, uint wad) externalvirtualoverridereturns (bool) {
_decreaseAllowance(src, wad);
return _transfer(src, dst, wad);
}
/**
* @dev Moves tokens `wad` from `src` to `dst`.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `src` must have a balance of at least `amount`.
*//// if_succeeds {:msg "Transfer - src decrease"} old(_balanceOf[src]) >= _balanceOf[src];/// if_succeeds {:msg "Transfer - dst increase"} _balanceOf[dst] >= old(_balanceOf[dst]);/// if_succeeds {:msg "Transfer - supply"} old(_balanceOf[src]) + old(_balanceOf[dst]) == _balanceOf[src] + _balanceOf[dst];function_transfer(address src, address dst, uint wad) internalvirtualreturns (bool) {
require(_balanceOf[src] >= wad, "ERC20: Insufficient balance");
unchecked { _balanceOf[src] = _balanceOf[src] - wad; }
_balanceOf[dst] = _balanceOf[dst] + wad;
emit Transfer(src, dst, wad);
returntrue;
}
/**
* @dev Sets the allowance granted to `spender` by `owner`.
*
* Emits an {Approval} event indicating the updated allowance.
*/function_setAllowance(address owner, address spender, uint wad) internalvirtualreturns (bool) {
_allowance[owner][spender] = wad;
emit Approval(owner, spender, wad);
returntrue;
}
/**
* @dev Decreases the allowance granted to the caller by `src`, unless src == msg.sender or _allowance[src][msg.sender] == MAX
*
* Emits an {Approval} event indicating the updated allowance, if the allowance is updated.
*
* Requirements:
*
* - `spender` must have allowance for the caller of at least
* `wad`, unless src == msg.sender
*//// if_succeeds {:msg "Decrease allowance - underflow"} old(_allowance[src][msg.sender]) <= _allowance[src][msg.sender];function_decreaseAllowance(address src, uint wad) internalvirtualreturns (bool) {
if (src !=msg.sender) {
uint256 allowed = _allowance[src][msg.sender];
if (allowed !=type(uint).max) {
require(allowed >= wad, "ERC20: Insufficient approval");
unchecked { _setAllowance(src, msg.sender, allowed - wad); }
}
}
returntrue;
}
/** @dev Creates `wad` tokens and assigns them to `dst`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*//// if_succeeds {:msg "Mint - balance overflow"} old(_balanceOf[dst]) >= _balanceOf[dst];/// if_succeeds {:msg "Mint - supply overflow"} old(_totalSupply) >= _totalSupply;function_mint(address dst, uint wad) internalvirtualreturns (bool) {
_balanceOf[dst] = _balanceOf[dst] + wad;
_totalSupply = _totalSupply + wad;
emit Transfer(address(0), dst, wad);
returntrue;
}
/**
* @dev Destroys `wad` tokens from `src`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `src` must have at least `wad` tokens.
*//// if_succeeds {:msg "Burn - balance underflow"} old(_balanceOf[src]) <= _balanceOf[src];/// if_succeeds {:msg "Burn - supply underflow"} old(_totalSupply) <= _totalSupply;function_burn(address src, uint wad) internalvirtualreturns (bool) {
unchecked {
require(_balanceOf[src] >= wad, "ERC20: Insufficient balance");
_balanceOf[src] = _balanceOf[src] - wad;
_totalSupply = _totalSupply - wad;
emit Transfer(src, address(0), wad);
}
returntrue;
}
}
Contract Source Code
File 7 of 19: ERC20Permit.sol
// SPDX-License-Identifier: MIT// Adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/53516bc555a454862470e7860a9b5254db4d00f5/contracts/token/ERC20/ERC20Permit.solpragmasolidity ^0.8.0;import"./ERC20.sol";
import"./IERC2612.sol";
/**
* @dev Extension of {ERC20} that allows token holders to use their tokens
* without sending any transactions by setting {IERC20-allowance} with a
* signature using the {permit} method, and then spend them via
* {IERC20-transferFrom}.
*
* The {permit} signature mechanism conforms to the {IERC2612} interface.
*/abstractcontractERC20PermitisERC20, IERC2612{
mapping (address=>uint256) publicoverride nonces;
bytes32publicimmutable PERMIT_TYPEHASH =keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
bytes32privateimmutable _DOMAIN_SEPARATOR;
uint256publicimmutable deploymentChainId;
constructor(stringmemory name_, stringmemory symbol_, uint8 decimals_) ERC20(name_, symbol_, decimals_) {
deploymentChainId =block.chainid;
_DOMAIN_SEPARATOR = _calculateDomainSeparator(block.chainid);
}
/// @dev Calculate the DOMAIN_SEPARATOR.function_calculateDomainSeparator(uint256 chainId) privateviewreturns (bytes32) {
returnkeccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256(bytes(version())),
chainId,
address(this)
)
);
}
/// @dev Return the DOMAIN_SEPARATOR.functionDOMAIN_SEPARATOR() externalviewreturns (bytes32) {
returnblock.chainid== deploymentChainId ? _DOMAIN_SEPARATOR : _calculateDomainSeparator(block.chainid);
}
/// @dev Setting the version as a function so that it can be overridenfunctionversion() publicpurevirtualreturns(stringmemory) { return"1"; }
/**
* @dev See {IERC2612-permit}.
*
* In cases where the free option is not a concern, deadline can simply be
* set to uint(-1), so it should be seen as an optional parameter
*/functionpermit(address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) externalvirtualoverride{
require(deadline >=block.timestamp, "ERC20Permit: expired deadline");
bytes32 hashStruct =keccak256(
abi.encode(
PERMIT_TYPEHASH,
owner,
spender,
amount,
nonces[owner]++,
deadline
)
);
bytes32 hash =keccak256(
abi.encodePacked(
"\x19\x01",
block.chainid== deploymentChainId ? _DOMAIN_SEPARATOR : _calculateDomainSeparator(block.chainid),
hashStruct
)
);
address signer =ecrecover(hash, v, r, s);
require(
signer !=address(0) && signer == owner,
"ERC20Permit: invalid signature"
);
_setAllowance(owner, spender, amount);
}
}
Contract Source Code
File 8 of 19: FYToken.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity 0.8.6;import"erc3156/contracts/interfaces/IERC3156FlashBorrower.sol";
import"erc3156/contracts/interfaces/IERC3156FlashLender.sol";
import"@yield-protocol/utils-v2/contracts/token/ERC20Permit.sol";
import"@yield-protocol/utils-v2/contracts/token/SafeERC20Namer.sol";
import"@yield-protocol/vault-interfaces/IFYToken.sol";
import"@yield-protocol/vault-interfaces/IJoin.sol";
import"@yield-protocol/vault-interfaces/IOracle.sol";
import"@yield-protocol/utils-v2/contracts/access/AccessControl.sol";
import"@yield-protocol/utils-v2/contracts/math/WMul.sol";
import"@yield-protocol/utils-v2/contracts/math/WDiv.sol";
import"@yield-protocol/utils-v2/contracts/cast/CastU256U128.sol";
import"@yield-protocol/utils-v2/contracts/cast/CastU256U32.sol";
import"./constants/Constants.sol";
contractFYTokenisIFYToken, IERC3156FlashLender, AccessControl(), ERC20Permit, Constants{
usingWMulforuint256;
usingWDivforuint256;
usingCastU256U128foruint256;
usingCastU256U32foruint256;
eventPoint(bytes32indexed param, address value);
eventFlashFeeFactorSet(uint256indexed fee);
eventSeriesMatured(uint256 chiAtMaturity);
eventRedeemed(addressindexedfrom, addressindexed to, uint256 amount, uint256 redeemed);
uint256constant CHI_NOT_SET =type(uint256).max;
uint256constantinternal MAX_TIME_TO_MATURITY =126144000; // seconds in four yearsbytes32constantinternal FLASH_LOAN_RETURN =keccak256("ERC3156FlashBorrower.onFlashLoan");
uint256constant FLASH_LOANS_DISABLED =type(uint256).max;
uint256public flashFeeFactor = FLASH_LOANS_DISABLED; // Fee on flash loans, as a percentage in fixed point with 18 decimals. Flash loans disabled by default by overflow from `flashFee`.
IOracle public oracle; // Oracle for the savings rate.
IJoin public join; // Source of redemption funds.addresspublicimmutableoverride underlying;
bytes6publicimmutable underlyingId; // Needed to access the oracleuint256publicimmutableoverride maturity;
uint256public chiAtMaturity = CHI_NOT_SET; // Spot price (exchange rate) between the base and an interest accruing token at maturity constructor(bytes6 underlyingId_,
IOracle oracle_, // Underlying vs its interest-bearing version
IJoin join_,
uint256 maturity_,
stringmemory name,
stringmemory symbol
) ERC20Permit(name, symbol, SafeERC20Namer.tokenDecimals(address(IJoin(join_).asset()))) { // The join asset is this fyToken's underlying, from which we inherit the decimalsuint256 now_ =block.timestamp;
require(
maturity_ > now_ &&
maturity_ < now_ + MAX_TIME_TO_MATURITY &&
maturity_ <type(uint32).max,
"Invalid maturity"
);
underlyingId = underlyingId_;
join = join_;
maturity = maturity_;
underlying =address(IJoin(join_).asset());
oracle = oracle_;
}
modifierafterMaturity() {
require(
uint32(block.timestamp) >= maturity,
"Only after maturity"
);
_;
}
modifierbeforeMaturity() {
require(
uint32(block.timestamp) < maturity,
"Only before maturity"
);
_;
}
/// @dev Point to a different Oracle or Joinfunctionpoint(bytes32 param, address value) externalauth{
if (param =="oracle") oracle = IOracle(value);
elseif (param =="join") join = IJoin(value);
elserevert("Unrecognized parameter");
emit Point(param, value);
}
/// @dev Set the flash loan fee factorfunctionsetFlashFeeFactor(uint256 flashFeeFactor_)
externalauth{
flashFeeFactor = flashFeeFactor_;
emit FlashFeeFactorSet(flashFeeFactor_);
}
/// @dev Mature the fyToken by recording the chi./// If called more than once, it will revert.functionmature()
externaloverrideafterMaturity{
require (chiAtMaturity == CHI_NOT_SET, "Already matured");
_mature();
}
/// @dev Mature the fyToken by recording the chi.function_mature()
privatereturns (uint256 _chiAtMaturity)
{
(_chiAtMaturity,) = oracle.get(underlyingId, CHI, 0); // The value returned is an accumulator, it doesn't need an input amount
chiAtMaturity = _chiAtMaturity;
emit SeriesMatured(_chiAtMaturity);
}
/// @dev Retrieve the chi accrual since maturity, maturing if necessary.functionaccrual()
externalafterMaturityreturns (uint256)
{
return _accrual();
}
/// @dev Retrieve the chi accrual since maturity, maturing if necessary./// Note: Call only after checking we are past maturityfunction_accrual()
privatereturns (uint256 accrual_)
{
if (chiAtMaturity == CHI_NOT_SET) { // After maturity, but chi not yet recorded. Let's record it, and accrual is then 1.
_mature();
} else {
(uint256 chi,) = oracle.get(underlyingId, CHI, 0); // The value returned is an accumulator, it doesn't need an input amount
accrual_ = chi.wdiv(chiAtMaturity);
}
accrual_ = accrual_ >=1e18 ? accrual_ : 1e18; // The accrual can't be below 1 (with 18 decimals)
}
/// @dev Burn fyToken after maturity for an amount that increases according to `chi`/// If `amount` is 0, the contract will redeem instead the fyToken balance of this contract. Useful for batches.functionredeem(address to, uint256 amount)
externaloverrideafterMaturityreturns (uint256 redeemed)
{
uint256 amount_ = (amount ==0) ? _balanceOf[address(this)] : amount;
_burn(msg.sender, amount_);
redeemed = amount_.wmul(_accrual());
join.exit(to, redeemed.u128());
emit Redeemed(msg.sender, to, amount_, redeemed);
}
/// @dev Mint fyToken providing an equal amount of underlying to the protocolfunctionmintWithUnderlying(address to, uint256 amount)
externaloverridebeforeMaturity{
_mint(to, amount);
join.join(msg.sender, amount.u128());
}
/// @dev Mint fyTokens.functionmint(address to, uint256 amount)
externaloverridebeforeMaturityauth{
_mint(to, amount);
}
/// @dev Burn fyTokens. The user needs to have either transferred the tokens to this contract, or have approved this contract to take them. functionburn(addressfrom, uint256 amount)
externaloverrideauth{
_burn(from, amount);
}
/// @dev Burn fyTokens. /// Any tokens locked in this contract will be burned first and subtracted from the amount to burn from the user's wallet./// This feature allows someone to transfer fyToken to this contract to enable a `burn`, potentially saving the cost of `approve` or `permit`.function_burn(addressfrom, uint256 amount)
internaloverridereturns (bool)
{
// First use any tokens locked in this contractuint256 available = _balanceOf[address(this)];
if (available >= amount) {
returnsuper._burn(address(this), amount);
} else {
if (available >0 ) super._burn(address(this), available);
unchecked { _decreaseAllowance(from, amount - available); }
unchecked { returnsuper._burn(from, amount - available); }
}
}
/**
* @dev From ERC-3156. The amount of currency available to be lended.
* @param token The loan currency. It must be a FYDai contract.
* @return The amount of `token` that can be borrowed.
*/functionmaxFlashLoan(address token)
externalviewoverridebeforeMaturityreturns (uint256)
{
return token ==address(this) ? type(uint256).max- _totalSupply : 0;
}
/**
* @dev From ERC-3156. The fee to be charged for a given loan.
* @param token The loan currency. It must be the asset.
* @param amount The amount of tokens lent.
* @return The amount of `token` to be charged for the loan, on top of the returned principal.
*/functionflashFee(address token, uint256 amount)
externalviewoverridereturns (uint256)
{
require(token ==address(this), "Unsupported currency");
return _flashFee(amount);
}
/**
* @dev The fee to be charged for a given loan.
* @param amount The amount of tokens lent.
* @return The amount of `token` to be charged for the loan, on top of the returned principal.
*/function_flashFee(uint256 amount) internalviewreturns (uint256) {
return amount.wmul(flashFeeFactor);
}
/**
* @dev From ERC-3156. Loan `amount` fyDai to `receiver`, which needs to return them plus fee to this contract within the same transaction.
* Note that if the initiator and the borrower are the same address, no approval is needed for this contract to take the principal + fee from the borrower.
* If the borrower transfers the principal + fee to this contract, they will be burnt here instead of pulled from the borrower.
* @param receiver The contract receiving the tokens, needs to implement the `onFlashLoan(address user, uint256 amount, uint256 fee, bytes calldata)` interface.
* @param token The loan currency. Must be a fyDai contract.
* @param amount The amount of tokens lent.
* @param data A data parameter to be passed on to the `receiver` for any custom use.
*/functionflashLoan(IERC3156FlashBorrower receiver, address token, uint256 amount, bytesmemory data)
externaloverridebeforeMaturityreturns(bool)
{
require(token ==address(this), "Unsupported currency");
_mint(address(receiver), amount);
uint128 fee = _flashFee(amount).u128();
require(receiver.onFlashLoan(msg.sender, token, amount, fee, data) == FLASH_LOAN_RETURN, "Non-compliant borrower");
_burn(address(receiver), amount + fee);
returntrue;
}
}
Contract Source Code
File 9 of 19: IERC20.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/interfaceIERC20{
/**
* @dev Returns the amount of tokens in existence.
*/functiontotalSupply() externalviewreturns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/functionbalanceOf(address account) externalviewreturns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransfer(address recipient, uint256 amount) externalreturns (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.
*/functionallowance(address owner, address spender) externalviewreturns (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.
*/functionapprove(address spender, uint256 amount) externalreturns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransferFrom(address sender, address recipient, uint256 amount) externalreturns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/eventTransfer(addressindexedfrom, addressindexed 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.
*/eventApproval(addressindexed owner, addressindexed spender, uint256 value);
}
Contract Source Code
File 10 of 19: IERC20Metadata.sol
// SPDX-License-Identifier: MIT// Taken from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/extensions/IERC20Metadata.solpragmasolidity ^0.8.0;import"./IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*/interfaceIERC20MetadataisIERC20{
/**
* @dev Returns the name of the token.
*/functionname() externalviewreturns (stringmemory);
/**
* @dev Returns the symbol of the token.
*/functionsymbol() externalviewreturns (stringmemory);
/**
* @dev Returns the decimals places of the token.
*/functiondecimals() externalviewreturns (uint8);
}
Contract Source Code
File 11 of 19: IERC2612.sol
// SPDX-License-Identifier: MIT// Code adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2237/pragmasolidity ^0.8.0;/**
* @dev Interface of the ERC2612 standard as defined in the EIP.
*
* Adds the {permit} method, which can be used to change one's
* {IERC20-allowance} without having to send a transaction, by signing a
* message. This allows users to spend tokens without having to hold Ether.
*
* See https://eips.ethereum.org/EIPS/eip-2612.
*/interfaceIERC2612{
/**
* @dev Sets `amount` as the allowance of `spender` over `owner`'s tokens,
* given `owner`'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*/functionpermit(address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
/**
* @dev Returns the current ERC2612 nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/functionnonces(address owner) externalviewreturns (uint256);
}
Contract Source Code
File 12 of 19: IERC3156FlashBorrower.sol
// SPDX-License-Identifier: MITpragmasolidity >=0.6.0 <0.9.0;interfaceIERC3156FlashBorrower{
/**
* @dev Receive a flash loan.
* @param initiator The initiator of the loan.
* @param token The loan currency.
* @param amount The amount of tokens lent.
* @param fee The additional amount of tokens to repay.
* @param data Arbitrary data structure, intended to contain user-defined parameters.
* @return The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan"
*/functiononFlashLoan(address initiator,
address token,
uint256 amount,
uint256 fee,
bytescalldata data
) externalreturns (bytes32);
}
Contract Source Code
File 13 of 19: IERC3156FlashLender.sol
// SPDX-License-Identifier: MITpragmasolidity >=0.6.0 <0.9.0;import"./IERC3156FlashBorrower.sol";
interfaceIERC3156FlashLender{
/**
* @dev The amount of currency available to be lended.
* @param token The loan currency.
* @return The amount of `token` that can be borrowed.
*/functionmaxFlashLoan(address token
) externalviewreturns (uint256);
/**
* @dev The fee to be charged for a given loan.
* @param token The loan currency.
* @param amount The amount of tokens lent.
* @return The amount of `token` to be charged for the loan, on top of the returned principal.
*/functionflashFee(address token,
uint256 amount
) externalviewreturns (uint256);
/**
* @dev Initiate a flash loan.
* @param receiver The receiver of the tokens in the loan, and the receiver of the callback.
* @param token The loan currency.
* @param amount The amount of tokens lent.
* @param data Arbitrary data structure, intended to contain user-defined parameters.
*/functionflashLoan(
IERC3156FlashBorrower receiver,
address token,
uint256 amount,
bytescalldata data
) externalreturns (bool);
}
Contract Source Code
File 14 of 19: IFYToken.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;import"@yield-protocol/utils-v2/contracts/token/IERC20.sol";
interfaceIFYTokenisIERC20{
/// @dev Asset that is returned on redemption.functionunderlying() externalviewreturns (address);
/// @dev Unix time at which redemption of fyToken for underlying are possiblefunctionmaturity() externalviewreturns (uint256);
/// @dev Record price data at maturityfunctionmature() external;
/// @dev Mint fyToken providing an equal amount of underlying to the protocolfunctionmintWithUnderlying(address to, uint256 amount) external;
/// @dev Burn fyToken after maturity for an amount of underlying.functionredeem(address to, uint256 amount) externalreturns (uint256);
/// @dev Mint fyToken./// This function can only be called by other Yield contracts, not users directly./// @param to Wallet to mint the fyToken in./// @param fyTokenAmount Amount of fyToken to mint.functionmint(address to, uint256 fyTokenAmount) external;
/// @dev Burn fyToken./// This function can only be called by other Yield contracts, not users directly./// @param from Wallet to burn the fyToken from./// @param fyTokenAmount Amount of fyToken to burn.functionburn(addressfrom, uint256 fyTokenAmount) external;
}
Contract Source Code
File 15 of 19: IJoin.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;import"@yield-protocol/utils-v2/contracts/token/IERC20.sol";
interfaceIJoin{
/// @dev asset managed by this contractfunctionasset() externalviewreturns (address);
/// @dev Add tokens to this contract.functionjoin(address user, uint128 wad) externalreturns (uint128);
/// @dev Remove tokens to this contract.functionexit(address user, uint128 wad) externalreturns (uint128);
}
Contract Source Code
File 16 of 19: IOracle.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;interfaceIOracle{
/**
* @notice Doesn't refresh the price, but returns the latest value available without doing any transactional operations:
* @return value in wei
*/functionpeek(bytes32 base,
bytes32 quote,
uint256 amount
) externalviewreturns (uint256 value, uint256 updateTime);
/**
* @notice Does whatever work or queries will yield the most up-to-date price, and returns it.
* @return value in wei
*/functionget(bytes32 base,
bytes32 quote,
uint256 amount
) externalreturns (uint256 value, uint256 updateTime);
}
Contract Source Code
File 17 of 19: SafeERC20Namer.sol
// SPDX-License-Identifier: MITpragmasolidity >=0.5.0;import"../token/IERC20Metadata.sol";
import"../utils/AddressStringUtil.sol";
// produces token descriptors from inconsistent or absent ERC20 symbol implementations that can return string or bytes32// this library will always produce a string symbol to represent the tokenlibrarySafeERC20Namer{
functionbytes32ToString(bytes32 x) privatepurereturns (stringmemory) {
bytesmemory bytesString =newbytes(32);
uint256 charCount =0;
for (uint256 j =0; j <32; j++) {
bytes1 char = x[j];
if (char !=0) {
bytesString[charCount] = char;
charCount++;
}
}
bytesmemory bytesStringTrimmed =newbytes(charCount);
for (uint256 j =0; j < charCount; j++) {
bytesStringTrimmed[j] = bytesString[j];
}
returnstring(bytesStringTrimmed);
}
// assumes the data is in position 2functionparseStringData(bytesmemory b) privatepurereturns (stringmemory) {
uint256 charCount =0;
// first parse the charCount out of the datafor (uint256 i =32; i <64; i++) {
charCount <<=8;
charCount +=uint8(b[i]);
}
bytesmemory bytesStringTrimmed =newbytes(charCount);
for (uint256 i =0; i < charCount; i++) {
bytesStringTrimmed[i] = b[i +64];
}
returnstring(bytesStringTrimmed);
}
// uses a heuristic to produce a token name from the address// the heuristic returns the full hex of the address string in upper casefunctionaddressToName(address token) privatepurereturns (stringmemory) {
return AddressStringUtil.toAsciiString(token, 40);
}
// uses a heuristic to produce a token symbol from the address// the heuristic returns the first 6 hex of the address string in upper casefunctionaddressToSymbol(address token) privatepurereturns (stringmemory) {
return AddressStringUtil.toAsciiString(token, 6);
}
// calls an external view token contract method that returns a symbol or name, and parses the output into a stringfunctioncallAndParseStringReturn(address token, bytes4 selector) privateviewreturns (stringmemory) {
(bool success, bytesmemory data) = token.staticcall(abi.encodeWithSelector(selector));
// if not implemented, or returns empty data, return empty stringif (!success || data.length==0) {
return"";
}
// bytes32 data always has length 32if (data.length==32) {
bytes32 decoded =abi.decode(data, (bytes32));
return bytes32ToString(decoded);
} elseif (data.length>64) {
returnabi.decode(data, (string));
}
return"";
}
// attempts to extract the token symbol. if it does not implement symbol, returns a symbol derived from the addressfunctiontokenSymbol(address token) publicviewreturns (stringmemory) {
stringmemory symbol = callAndParseStringReturn(token, IERC20Metadata.symbol.selector);
if (bytes(symbol).length==0) {
// fallback to 6 uppercase hex of addressreturn addressToSymbol(token);
}
return symbol;
}
// attempts to extract the token name. if it does not implement name, returns a name derived from the addressfunctiontokenName(address token) publicviewreturns (stringmemory) {
stringmemory name = callAndParseStringReturn(token, IERC20Metadata.name.selector);
if (bytes(name).length==0) {
// fallback to full hex of addressreturn addressToName(token);
}
return name;
}
/// @notice Provides a safe ERC20.decimals version which returns '18' as fallback value./// @param token The address of the ERC-20 token contract./// @return (uint8) Token decimals.functiontokenDecimals(address token) publicviewreturns (uint8) {
(bool success, bytesmemory data) = token.staticcall(abi.encodeWithSelector(IERC20Metadata.decimals.selector));
return success && data.length==32 ? abi.decode(data, (uint8)) : 18;
}
}
Contract Source Code
File 18 of 19: WDiv.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.0;libraryWDiv{ // Fixed point arithmetic in 18 decimal units// Taken from https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol/// @dev Divide an amount by a fixed point factor with 18 decimalsfunctionwdiv(uint256 x, uint256 y) internalpurereturns (uint256 z) {
z = (x *1e18) / y;
}
}
Contract Source Code
File 19 of 19: WMul.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity ^0.8.0;libraryWMul{
// Taken from https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol/// @dev Multiply an amount by a fixed point factor with 18 decimals, rounds down.functionwmul(uint256 x, uint256 y) internalpurereturns (uint256 z) {
z = x * y;
unchecked { z /=1e18; }
}
}