¡El código fuente de este contrato está verificado!
Metadatos del Contrato
Compilador
0.8.21+commit.d9974bed
Idioma
Solidity
Código Fuente del Contrato
Archivo 1 de 11: Context.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)pragmasolidity ^0.8.0;/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/abstractcontractContext{
function_msgSender() internalviewvirtualreturns (address) {
returnmsg.sender;
}
function_msgData() internalviewvirtualreturns (bytescalldata) {
returnmsg.data;
}
}
Código Fuente del Contrato
Archivo 2 de 11: ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.abstractcontractERC20{
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/eventTransfer(addressindexedfrom, addressindexed to, uint256 amount);
eventApproval(addressindexed owner, addressindexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/stringpublic name;
stringpublic symbol;
uint8publicimmutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/uint256public totalSupply;
mapping(address=>uint256) public balanceOf;
mapping(address=>mapping(address=>uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/uint256internalimmutable INITIAL_CHAIN_ID;
bytes32internalimmutable INITIAL_DOMAIN_SEPARATOR;
mapping(address=>uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/constructor(stringmemory _name,
stringmemory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID =block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/functionapprove(address spender, uint256 amount) publicvirtualreturns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
returntrue;
}
functiontransfer(address to, uint256 amount) publicvirtualreturns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user// balances can't exceed the max uint256 value.unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
returntrue;
}
functiontransferFrom(addressfrom,
address to,
uint256 amount
) publicvirtualreturns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.if (allowed !=type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user// balances can't exceed the max uint256 value.unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
returntrue;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/functionpermit(address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) publicvirtual{
require(deadline >=block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing// the owner's nonce which cannot realistically overflow.unchecked {
address recoveredAddress =ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress !=address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
functionDOMAIN_SEPARATOR() publicviewvirtualreturns (bytes32) {
returnblock.chainid== INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
functioncomputeDomainSeparator() internalviewvirtualreturns (bytes32) {
returnkeccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/function_mint(address to, uint256 amount) internalvirtual{
totalSupply += amount;
// Cannot overflow because the sum of all user// balances can't exceed the max uint256 value.unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function_burn(addressfrom, uint256 amount) internalvirtual{
balanceOf[from] -= amount;
// Cannot underflow because a user's balance// will never be larger than the total supply.unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
Código Fuente del Contrato
Archivo 3 de 11: ERC4626.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;import {ERC20} from"../tokens/ERC20.sol";
import {SafeTransferLib} from"../utils/SafeTransferLib.sol";
import {FixedPointMathLib} from"../utils/FixedPointMathLib.sol";
/// @notice Minimal ERC4626 tokenized Vault implementation./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/mixins/ERC4626.sol)abstractcontractERC4626isERC20{
usingSafeTransferLibforERC20;
usingFixedPointMathLibforuint256;
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/eventDeposit(addressindexed caller, addressindexed owner, uint256 assets, uint256 shares);
eventWithdraw(addressindexed caller,
addressindexed receiver,
addressindexed owner,
uint256 assets,
uint256 shares
);
/*//////////////////////////////////////////////////////////////
IMMUTABLES
//////////////////////////////////////////////////////////////*/
ERC20 publicimmutable asset;
constructor(
ERC20 _asset,
stringmemory _name,
stringmemory _symbol
) ERC20(_name, _symbol, _asset.decimals()) {
asset = _asset;
}
/*//////////////////////////////////////////////////////////////
DEPOSIT/WITHDRAWAL LOGIC
//////////////////////////////////////////////////////////////*/functiondeposit(uint256 assets, address receiver) publicvirtualreturns (uint256 shares) {
// Check for rounding error since we round down in previewDeposit.require((shares = previewDeposit(assets)) !=0, "ZERO_SHARES");
// Need to transfer before minting or ERC777s could reenter.
asset.safeTransferFrom(msg.sender, address(this), assets);
_mint(receiver, shares);
emit Deposit(msg.sender, receiver, assets, shares);
afterDeposit(assets, shares);
}
functionmint(uint256 shares, address receiver) publicvirtualreturns (uint256 assets) {
assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up.// Need to transfer before minting or ERC777s could reenter.
asset.safeTransferFrom(msg.sender, address(this), assets);
_mint(receiver, shares);
emit Deposit(msg.sender, receiver, assets, shares);
afterDeposit(assets, shares);
}
functionwithdraw(uint256 assets,
address receiver,
address owner
) publicvirtualreturns (uint256 shares) {
shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up.if (msg.sender!= owner) {
uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.if (allowed !=type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
}
beforeWithdraw(assets, shares);
_burn(owner, shares);
emit Withdraw(msg.sender, receiver, owner, assets, shares);
asset.safeTransfer(receiver, assets);
}
functionredeem(uint256 shares,
address receiver,
address owner
) publicvirtualreturns (uint256 assets) {
if (msg.sender!= owner) {
uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.if (allowed !=type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
}
// Check for rounding error since we round down in previewRedeem.require((assets = previewRedeem(shares)) !=0, "ZERO_ASSETS");
beforeWithdraw(assets, shares);
_burn(owner, shares);
emit Withdraw(msg.sender, receiver, owner, assets, shares);
asset.safeTransfer(receiver, assets);
}
/*//////////////////////////////////////////////////////////////
ACCOUNTING LOGIC
//////////////////////////////////////////////////////////////*/functiontotalAssets() publicviewvirtualreturns (uint256);
functionconvertToShares(uint256 assets) publicviewvirtualreturns (uint256) {
uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.return supply ==0 ? assets : assets.mulDivDown(supply, totalAssets());
}
functionconvertToAssets(uint256 shares) publicviewvirtualreturns (uint256) {
uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.return supply ==0 ? shares : shares.mulDivDown(totalAssets(), supply);
}
functionpreviewDeposit(uint256 assets) publicviewvirtualreturns (uint256) {
return convertToShares(assets);
}
functionpreviewMint(uint256 shares) publicviewvirtualreturns (uint256) {
uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.return supply ==0 ? shares : shares.mulDivUp(totalAssets(), supply);
}
functionpreviewWithdraw(uint256 assets) publicviewvirtualreturns (uint256) {
uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.return supply ==0 ? assets : assets.mulDivUp(supply, totalAssets());
}
functionpreviewRedeem(uint256 shares) publicviewvirtualreturns (uint256) {
return convertToAssets(shares);
}
/*//////////////////////////////////////////////////////////////
DEPOSIT/WITHDRAWAL LIMIT LOGIC
//////////////////////////////////////////////////////////////*/functionmaxDeposit(address) publicviewvirtualreturns (uint256) {
returntype(uint256).max;
}
functionmaxMint(address) publicviewvirtualreturns (uint256) {
returntype(uint256).max;
}
functionmaxWithdraw(address owner) publicviewvirtualreturns (uint256) {
return convertToAssets(balanceOf[owner]);
}
functionmaxRedeem(address owner) publicviewvirtualreturns (uint256) {
return balanceOf[owner];
}
/*//////////////////////////////////////////////////////////////
INTERNAL HOOKS LOGIC
//////////////////////////////////////////////////////////////*/functionbeforeWithdraw(uint256 assets, uint256 shares) internalvirtual{}
functionafterDeposit(uint256 assets, uint256 shares) internalvirtual{}
}
Código Fuente del Contrato
Archivo 4 de 11: FixedPointMathLib.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=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)libraryFixedPointMathLib{
/*//////////////////////////////////////////////////////////////
SIMPLIFIED FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/uint256internalconstant MAX_UINT256 =2**256-1;
uint256internalconstant WAD =1e18; // The scalar of ETH and most ERC20s.functionmulWadDown(uint256 x, uint256 y) internalpurereturns (uint256) {
return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
}
functionmulWadUp(uint256 x, uint256 y) internalpurereturns (uint256) {
return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
}
functiondivWadDown(uint256 x, uint256 y) internalpurereturns (uint256) {
return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
}
functiondivWadUp(uint256 x, uint256 y) internalpurereturns (uint256) {
return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
}
/*//////////////////////////////////////////////////////////////
LOW LEVEL FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/functionmulDivDown(uint256 x,
uint256 y,
uint256 denominator
) internalpurereturns (uint256 z) {
/// @solidity memory-safe-assemblyassembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))ifiszero(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)
}
}
functionmulDivUp(uint256 x,
uint256 y,
uint256 denominator
) internalpurereturns (uint256 z) {
/// @solidity memory-safe-assemblyassembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))ifiszero(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))
}
}
functionrpow(uint256 x,
uint256 n,
uint256 scalar
) internalpurereturns (uint256 z) {
/// @solidity memory-safe-assemblyassembly {
switch x
case0 {
switch n
case0 {
// 0 ** 0 = 1
z := scalar
}
default {
// 0 ** n = 0
z :=0
}
}
default {
switchmod(n, 2)
case0 {
// 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.ifshr(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.iflt(xxRound, xx) {
revert(0, 0)
}
// Set x to scaled xxRound.
x :=div(xxRound, scalar)
// If n is even:ifmod(n, 2) {
// Compute z * x.let zx :=mul(z, x)
// If z * x overflowed:ifiszero(eq(div(zx, x), z)) {
// Revert if x is non-zero.ifiszero(iszero(x)) {
revert(0, 0)
}
}
// Round to the nearest number.let zxRound :=add(zx, half)
// Revert if zx + half overflowed.iflt(zxRound, zx) {
revert(0, 0)
}
// Return properly scaled zxRound.
z :=div(zxRound, scalar)
}
}
}
}
}
/*//////////////////////////////////////////////////////////////
GENERAL NUMBER UTILITIES
//////////////////////////////////////////////////////////////*/functionsqrt(uint256 x) internalpurereturns (uint256 z) {
/// @solidity memory-safe-assemblyassembly {
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.ifiszero(lt(y, 0x10000000000000000000000000000000000)) {
y :=shr(128, y)
z :=shl(64, z)
}
ifiszero(lt(y, 0x1000000000000000000)) {
y :=shr(64, y)
z :=shl(32, z)
}
ifiszero(lt(y, 0x10000000000)) {
y :=shr(32, y)
z :=shl(16, z)
}
ifiszero(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))
}
}
functionunsafeMod(uint256 x, uint256 y) internalpurereturns (uint256 z) {
/// @solidity memory-safe-assemblyassembly {
// Mod x by y. Note this will return// 0 instead of reverting if y is zero.
z :=mod(x, y)
}
}
functionunsafeDiv(uint256 x, uint256 y) internalpurereturns (uint256 r) {
/// @solidity memory-safe-assemblyassembly {
// Divide x by y. Note this will return// 0 instead of reverting if y is zero.
r :=div(x, y)
}
}
functionunsafeDivUp(uint256 x, uint256 y) internalpurereturns (uint256 z) {
/// @solidity memory-safe-assemblyassembly {
// 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))
}
}
}
Código Fuente del Contrato
Archivo 5 de 11: IERC20.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)pragmasolidity ^0.8.0;/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/interfaceIERC20{
/**
* @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);
/**
* @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 `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransfer(address to, 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 `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransferFrom(addressfrom, address to, uint256 amount) externalreturns (bool);
}
Código Fuente del Contrato
Archivo 6 de 11: IERC20Metadata.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)pragmasolidity ^0.8.0;import"../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/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);
}
Código Fuente del Contrato
Archivo 7 de 11: LinearRewardsErc4626.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity ^0.8.21;// ====================================================================// | ______ _______ |// | / _____________ __ __ / ____(_____ ____ _____ ________ |// | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |// | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |// | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |// | |// ====================================================================// ======================== LinearRewardsErc4626 ======================// ====================================================================// Frax Finance: https://github.com/FraxFinanceimport { ERC20, ERC4626 } from"solmate/mixins/ERC4626.sol";
import { SafeCastLib } from"solmate/utils/SafeCastLib.sol";
/// @title LinearRewardsErc4626/// @notice An ERC4626 Vault implementation with linear rewardsabstractcontractLinearRewardsErc4626isERC4626{
usingSafeCastLibfor*;
/// @notice The precision of all integer calculationsuint256publicconstant PRECISION =1e18;
/// @notice The rewards cycle length in secondsuint256publicimmutable REWARDS_CYCLE_LENGTH;
/// @notice Information about the current rewards cyclestructRewardsCycleData {
uint40 cycleEnd; // Timestamp of the end of the current rewards cycleuint40 lastSync; // Timestamp of the last time the rewards cycle was synceduint216 rewardCycleAmount; // Amount of rewards to be distributed in the current cycle
}
/// @notice The rewards cycle data, stored in a single word to save gas
RewardsCycleData public rewardsCycleData;
/// @notice The timestamp of the last time rewards were distributeduint256public lastRewardsDistribution;
/// @notice The total amount of assets that have been distributed and depositeduint256public storedTotalAssets;
/// @notice The precision of the underlying assetuint256publicimmutable UNDERLYING_PRECISION;
/// @param _underlying The erc20 asset deposited/// @param _name The name of the vault/// @param _symbol The symbol of the vault/// @param _rewardsCycleLength The length of the rewards cycle in secondsconstructor(
ERC20 _underlying,
stringmemory _name,
stringmemory _symbol,
uint256 _rewardsCycleLength
) ERC4626(_underlying, _name, _symbol) {
REWARDS_CYCLE_LENGTH = _rewardsCycleLength;
UNDERLYING_PRECISION =10** _underlying.decimals();
// initialize rewardsCycleEnd value// NOTE: normally distribution of rewards should be done prior to _syncRewards but in this case we know there are no users or rewards yet.
_syncRewards();
// initialize lastRewardsDistribution value
_distributeRewards();
}
functionpricePerShare() externalviewreturns (uint256 _pricePerShare) {
_pricePerShare = convertToAssets(UNDERLYING_PRECISION);
}
/// @notice The ```calculateRewardsToDistribute``` function calculates the amount of rewards to distribute based on the rewards cycle data and the time elapsed/// @param _rewardsCycleData The rewards cycle data/// @param _deltaTime The time elapsed since the last rewards distribution/// @return _rewardToDistribute The amount of rewards to distributefunctioncalculateRewardsToDistribute(
RewardsCycleData memory _rewardsCycleData,
uint256 _deltaTime
) publicviewvirtualreturns (uint256 _rewardToDistribute) {
_rewardToDistribute =
(_rewardsCycleData.rewardCycleAmount * _deltaTime) /
(_rewardsCycleData.cycleEnd - _rewardsCycleData.lastSync);
}
/// @notice The ```previewDistributeRewards``` function is used to preview the rewards distributed at the top of the block/// @return _rewardToDistribute The amount of underlying to distributefunctionpreviewDistributeRewards() publicviewvirtualreturns (uint256 _rewardToDistribute) {
// Cache state for gas savings
RewardsCycleData memory _rewardsCycleData = rewardsCycleData;
uint256 _lastRewardsDistribution = lastRewardsDistribution;
uint40 _timestamp =block.timestamp.safeCastTo40();
// Calculate the delta time, but only include up to the cycle end in case we are passed ituint256 _deltaTime = _timestamp > _rewardsCycleData.cycleEnd
? _rewardsCycleData.cycleEnd - _lastRewardsDistribution
: _timestamp - _lastRewardsDistribution;
// Calculate the rewards to distribute
_rewardToDistribute = calculateRewardsToDistribute({
_rewardsCycleData: _rewardsCycleData,
_deltaTime: _deltaTime
});
}
/// @notice The ```distributeRewards``` function distributes the rewards once per block/// @return _rewardToDistribute The amount of underlying to distributefunction_distributeRewards() internalvirtualreturns (uint256 _rewardToDistribute) {
_rewardToDistribute = previewDistributeRewards();
// Only write to state/emit if we actually distribute rewardsif (_rewardToDistribute !=0) {
storedTotalAssets += _rewardToDistribute;
emit DistributeRewards({ rewardsToDistribute: _rewardToDistribute });
}
lastRewardsDistribution =block.timestamp;
}
/// @notice The ```previewSyncRewards``` function returns the updated rewards cycle data without updating the state/// @return _newRewardsCycleData The updated rewards cycle datafunctionpreviewSyncRewards() publicviewvirtualreturns (RewardsCycleData memory _newRewardsCycleData) {
RewardsCycleData memory _rewardsCycleData = rewardsCycleData;
uint256 _timestamp =block.timestamp;
// Only sync if the previous cycle has endedif (_timestamp <= _rewardsCycleData.cycleEnd) return _rewardsCycleData;
// Calculate rewards for next cycleuint256 _newRewards = asset.balanceOf(address(this)) - storedTotalAssets;
// Calculate the next cycle end, this keeps cycles at the same time regardless of when sync is calleduint40 _cycleEnd = (((_timestamp + REWARDS_CYCLE_LENGTH) / REWARDS_CYCLE_LENGTH) * REWARDS_CYCLE_LENGTH)
.safeCastTo40();
// This block prevents big jumps in rewards rate in case the sync happens near the end of the cycleif (_cycleEnd - _timestamp < REWARDS_CYCLE_LENGTH /40) {
_cycleEnd += REWARDS_CYCLE_LENGTH.safeCastTo40();
}
// Write return values
_rewardsCycleData.rewardCycleAmount = _newRewards.safeCastTo216();
_rewardsCycleData.lastSync = _timestamp.safeCastTo40();
_rewardsCycleData.cycleEnd = _cycleEnd;
return _rewardsCycleData;
}
/// @notice The ```_syncRewards``` function is used to update the rewards cycle datafunction_syncRewards() internalvirtual{
RewardsCycleData memory _rewardsCycleData = previewSyncRewards();
if (
block
.timestamp
// If true, then preview shows a rewards should be processed
.safeCastTo40() ==
_rewardsCycleData.lastSync &&// Ensures that we don't write to state twice in the same block
rewardsCycleData.lastSync != _rewardsCycleData.lastSync
) {
rewardsCycleData = _rewardsCycleData;
emit SyncRewards({
cycleEnd: _rewardsCycleData.cycleEnd,
lastSync: _rewardsCycleData.lastSync,
rewardCycleAmount: _rewardsCycleData.rewardCycleAmount
});
}
}
/// @notice The ```syncRewardsAndDistribution``` function is used to update the rewards cycle data and distribute rewards/// @dev rewards must be distributed before the cycle is syncedfunctionsyncRewardsAndDistribution() publicvirtual{
_distributeRewards();
_syncRewards();
}
/// @notice The ```totalAssets``` function returns the total assets available in the vault/// @dev This function simulates the rewards that will be distributed at the top of the block/// @return _totalAssets The total assets available in the vaultfunctiontotalAssets() publicviewvirtualoverridereturns (uint256 _totalAssets) {
uint256 _rewardToDistribute = previewDistributeRewards();
_totalAssets = storedTotalAssets + _rewardToDistribute;
}
functionafterDeposit(uint256 amount, uint256 shares) internalvirtualoverride{
storedTotalAssets += amount;
}
/// @notice The ```deposit``` function allows a user to mint shares by depositing underlying/// @param _assets The amount of underlying to deposit/// @param _receiver The address to send the shares to/// @return _shares The amount of shares mintedfunctiondeposit(uint256 _assets, address _receiver) publicoverridereturns (uint256 _shares) {
syncRewardsAndDistribution();
_shares =super.deposit({ assets: _assets, receiver: _receiver });
}
/// @notice The ```mint``` function allows a user to mint a given number of shares/// @param _shares The amount of shares to mint/// @param _receiver The address to send the shares to/// @return _assets The amount of underlying depositedfunctionmint(uint256 _shares, address _receiver) publicoverridereturns (uint256 _assets) {
syncRewardsAndDistribution();
_assets =super.mint({ shares: _shares, receiver: _receiver });
}
functionbeforeWithdraw(uint256 amount, uint256 shares) internalvirtualoverride{
storedTotalAssets -= amount;
}
/// @notice The ```withdraw``` function allows a user to withdraw a given amount of underlying/// @param _assets The amount of underlying to withdraw/// @param _receiver The address to send the underlying to/// @param _owner The address of the owner of the shares/// @return _shares The amount of shares burnedfunctionwithdraw(uint256 _assets, address _receiver, address _owner) publicoverridereturns (uint256 _shares) {
syncRewardsAndDistribution();
_shares =super.withdraw({ assets: _assets, receiver: _receiver, owner: _owner });
}
/// @notice The ```redeem``` function allows a user to redeem their shares for underlying/// @param _shares The amount of shares to redeem/// @param _receiver The address to send the underlying to/// @param _owner The address of the owner of the shares/// @return _assets The amount of underlying redeemedfunctionredeem(uint256 _shares, address _receiver, address _owner) publicoverridereturns (uint256 _assets) {
syncRewardsAndDistribution();
_assets =super.redeem({ shares: _shares, receiver: _receiver, owner: _owner });
}
/// @notice The ```depositWithSignature``` function allows a user to use signed approvals to deposit/// @param _assets The amount of underlying to deposit/// @param _receiver The address to send the shares to/// @param _deadline The deadline for the signature/// @param _approveMax Whether or not to approve the maximum amount/// @param _v The v value of the signature/// @param _r The r value of the signature/// @param _s The s value of the signature/// @return _shares The amount of shares mintedfunctiondepositWithSignature(uint256 _assets,
address _receiver,
uint256 _deadline,
bool _approveMax,
uint8 _v,
bytes32 _r,
bytes32 _s
) externalreturns (uint256 _shares) {
uint256 _amount = _approveMax ? type(uint256).max : _assets;
asset.permit({
owner: msg.sender,
spender: address(this),
value: _amount,
deadline: _deadline,
v: _v,
r: _r,
s: _s
});
_shares = (deposit({ _assets: _assets, _receiver: _receiver }));
}
//==============================================================================// Events//==============================================================================/// @notice The ```SyncRewards``` event is emitted when the rewards cycle is synced/// @param cycleEnd The timestamp of the end of the current rewards cycle/// @param lastSync The timestamp of the last time the rewards cycle was synced/// @param rewardCycleAmount The amount of rewards to be distributed in the current cycleeventSyncRewards(uint40 cycleEnd, uint40 lastSync, uint216 rewardCycleAmount);
/// @notice The ```DistributeRewards``` event is emitted when rewards are distributed to storedTotalAssets/// @param rewardsToDistribute The amount of rewards that were distributedeventDistributeRewards(uint256 rewardsToDistribute);
}
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;import {ERC20} from"../tokens/ERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer./// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.librarySafeTransferLib{
/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/functionsafeTransferETH(address to, uint256 amount) internal{
bool success;
/// @solidity memory-safe-assemblyassembly {
// Transfer the ETH and store if it succeeded or not.
success :=call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
/*//////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/functionsafeTransferFrom(
ERC20 token,
addressfrom,
address to,
uint256 amount
) internal{
bool success;
/// @solidity memory-safe-assemblyassembly {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success :=and(
// Set success to whether the call reverted, if not we check it either// returned exactly 1 (can't just be non-zero data), or had no return data.or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.// Counterintuitively, this call must be positioned second to the or() call in the// surrounding and() call or else returndatasize() will be zero during the computation.call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
)
}
require(success, "TRANSFER_FROM_FAILED");
}
functionsafeTransfer(
ERC20 token,
address to,
uint256 amount
) internal{
bool success;
/// @solidity memory-safe-assemblyassembly {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success :=and(
// Set success to whether the call reverted, if not we check it either// returned exactly 1 (can't just be non-zero data), or had no return data.or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.// Counterintuitively, this call must be positioned second to the or() call in the// surrounding and() call or else returndatasize() will be zero during the computation.call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "TRANSFER_FAILED");
}
functionsafeApprove(
ERC20 token,
address to,
uint256 amount
) internal{
bool success;
/// @solidity memory-safe-assemblyassembly {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success :=and(
// Set success to whether the call reverted, if not we check it either// returned exactly 1 (can't just be non-zero data), or had no return data.or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.// Counterintuitively, this call must be positioned second to the or() call in the// surrounding and() call or else returndatasize() will be zero during the computation.call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "APPROVE_FAILED");
}
}
Código Fuente del Contrato
Archivo 10 de 11: StakedFrax.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity ^0.8.21;// ====================================================================// | ______ _______ |// | / _____________ __ __ / ____(_____ ____ _____ ________ |// | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |// | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |// | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |// | |// ====================================================================// ============================ StakedFrax ============================// ====================================================================// Frax Finance: https://github.com/FraxFinanceimport { Timelock2Step } from"frax-std/access-control/v2/Timelock2Step.sol";
import { IERC20 } from"@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { SafeCastLib } from"solmate/utils/SafeCastLib.sol";
import { LinearRewardsErc4626, ERC20 } from"./LinearRewardsErc4626.sol";
/// @title Staked Frax/// @notice A ERC4626 Vault implementation with linear rewards, rewards can be cappedcontractStakedFraxisLinearRewardsErc4626, Timelock2Step{
usingSafeCastLibfor*;
/// @notice The maximum amount of rewards that can be distributed per second per 1e18 assetuint256public maxDistributionPerSecondPerAsset;
/// @param _underlying The erc20 asset deposited/// @param _name The name of the vault/// @param _symbol The symbol of the vault/// @param _rewardsCycleLength The length of the rewards cycle in seconds/// @param _maxDistributionPerSecondPerAsset The maximum amount of rewards that can be distributed per second per 1e18 asset/// @param _timelockAddress The address of the timelock/owner contractconstructor(
IERC20 _underlying,
stringmemory _name,
stringmemory _symbol,
uint32 _rewardsCycleLength,
uint256 _maxDistributionPerSecondPerAsset,
address _timelockAddress
)
LinearRewardsErc4626(ERC20(address(_underlying)), _name, _symbol, _rewardsCycleLength)
Timelock2Step(_timelockAddress)
{
maxDistributionPerSecondPerAsset = _maxDistributionPerSecondPerAsset;
}
/// @notice The ```SetMaxDistributionPerSecondPerAsset``` event is emitted when the maxDistributionPerSecondPerAsset is set/// @param oldMax The old maxDistributionPerSecondPerAsset value/// @param newMax The new maxDistributionPerSecondPerAsset valueeventSetMaxDistributionPerSecondPerAsset(uint256 oldMax, uint256 newMax);
/// @notice The ```setMaxDistributionPerSecondPerAsset``` function sets the maxDistributionPerSecondPerAsset/// @dev This function can only be called by the timelock, caps the value to type(uint64).max/// @param _maxDistributionPerSecondPerAsset The maximum amount of rewards that can be distributed per second per 1e18 assetfunctionsetMaxDistributionPerSecondPerAsset(uint256 _maxDistributionPerSecondPerAsset) external{
_requireSenderIsTimelock();
syncRewardsAndDistribution();
// NOTE: prevents bricking the contract via overflowif (_maxDistributionPerSecondPerAsset >type(uint64).max) {
_maxDistributionPerSecondPerAsset =type(uint64).max;
}
emit SetMaxDistributionPerSecondPerAsset({
oldMax: maxDistributionPerSecondPerAsset,
newMax: _maxDistributionPerSecondPerAsset
});
maxDistributionPerSecondPerAsset = _maxDistributionPerSecondPerAsset;
}
/// @notice The ```calculateRewardsToDistribute``` function calculates the amount of rewards to distribute based on the rewards cycle data and the time passed/// @param _rewardsCycleData The rewards cycle data/// @param _deltaTime The time passed since the last rewards distribution/// @return _rewardToDistribute The amount of rewards to distributefunctioncalculateRewardsToDistribute(
RewardsCycleData memory _rewardsCycleData,
uint256 _deltaTime
) publicviewoverridereturns (uint256 _rewardToDistribute) {
_rewardToDistribute =super.calculateRewardsToDistribute({
_rewardsCycleData: _rewardsCycleData,
_deltaTime: _deltaTime
});
// Cap rewardsuint256 _maxDistribution = (maxDistributionPerSecondPerAsset * _deltaTime * storedTotalAssets) / PRECISION;
if (_rewardToDistribute > _maxDistribution) {
_rewardToDistribute = _maxDistribution;
}
}
}
Código Fuente del Contrato
Archivo 11 de 11: Timelock2Step.sol
// SPDX-License-Identifier: ISCpragmasolidity >=0.8.0;// ====================================================================// | ______ _______ |// | / _____________ __ __ / ____(_____ ____ _____ ________ |// | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |// | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |// | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |// | |// ====================================================================// ========================== Timelock2Step ===========================// ====================================================================// Frax Finance: https://github.com/FraxFinance// Primary Author// Drake Evans: https://github.com/DrakeEvans// Reviewers// Dennis: https://github.com/denett// ====================================================================/// @title Timelock2Step/// @author Drake Evans (Frax Finance) https://github.com/drakeevans/// @dev Inspired by OpenZeppelin's Ownable2Step contract/// @notice An abstract contract which contains 2-step transfer and renounce logic for a timelock addressabstractcontractTimelock2Step{
/// @notice The pending timelock addressaddresspublic pendingTimelockAddress;
/// @notice The current timelock addressaddresspublic timelockAddress;
constructor(address _timelockAddress) {
timelockAddress = _timelockAddress;
}
// ============================================================================================// Functions: External Functions// ============================================================================================/// @notice The ```transferTimelock``` function initiates the timelock transfer/// @dev Must be called by the current timelock/// @param _newTimelock The address of the nominated (pending) timelockfunctiontransferTimelock(address _newTimelock) externalvirtual{
_requireSenderIsTimelock();
_transferTimelock(_newTimelock);
}
/// @notice The ```acceptTransferTimelock``` function completes the timelock transfer/// @dev Must be called by the pending timelockfunctionacceptTransferTimelock() externalvirtual{
_requireSenderIsPendingTimelock();
_acceptTransferTimelock();
}
/// @notice The ```renounceTimelock``` function renounces the timelock after setting pending timelock to current timelock/// @dev Pending timelock must be set to current timelock before renouncing, creating a 2-step renounce processfunctionrenounceTimelock() externalvirtual{
_requireSenderIsTimelock();
_requireSenderIsPendingTimelock();
_transferTimelock(address(0));
_setTimelock(address(0));
}
// ============================================================================================// Functions: Internal Actions// ============================================================================================/// @notice The ```_transferTimelock``` function initiates the timelock transfer/// @dev This function is to be implemented by a public function/// @param _newTimelock The address of the nominated (pending) timelockfunction_transferTimelock(address _newTimelock) internal{
pendingTimelockAddress = _newTimelock;
emit TimelockTransferStarted(timelockAddress, _newTimelock);
}
/// @notice The ```_acceptTransferTimelock``` function completes the timelock transfer/// @dev This function is to be implemented by a public functionfunction_acceptTransferTimelock() internal{
pendingTimelockAddress =address(0);
_setTimelock(msg.sender);
}
/// @notice The ```_setTimelock``` function sets the timelock address/// @dev This function is to be implemented by a public function/// @param _newTimelock The address of the new timelockfunction_setTimelock(address _newTimelock) internal{
emit TimelockTransferred(timelockAddress, _newTimelock);
timelockAddress = _newTimelock;
}
// ============================================================================================// Functions: Internal Checks// ============================================================================================/// @notice The ```_isTimelock``` function checks if _address is current timelock address/// @param _address The address to check against the timelock/// @return Whether or not msg.sender is current timelock addressfunction_isTimelock(address _address) internalviewreturns (bool) {
return _address == timelockAddress;
}
/// @notice The ```_requireIsTimelock``` function reverts if _address is not current timelock address/// @param _address The address to check against the timelockfunction_requireIsTimelock(address _address) internalview{
if (!_isTimelock(_address)) revert AddressIsNotTimelock(timelockAddress, _address);
}
/// @notice The ```_requireSenderIsTimelock``` function reverts if msg.sender is not current timelock address/// @dev This function is to be implemented by a public functionfunction_requireSenderIsTimelock() internalview{
_requireIsTimelock(msg.sender);
}
/// @notice The ```_isPendingTimelock``` function checks if the _address is pending timelock address/// @dev This function is to be implemented by a public function/// @param _address The address to check against the pending timelock/// @return Whether or not _address is pending timelock addressfunction_isPendingTimelock(address _address) internalviewreturns (bool) {
return _address == pendingTimelockAddress;
}
/// @notice The ```_requireIsPendingTimelock``` function reverts if the _address is not pending timelock address/// @dev This function is to be implemented by a public function/// @param _address The address to check against the pending timelockfunction_requireIsPendingTimelock(address _address) internalview{
if (!_isPendingTimelock(_address)) revert AddressIsNotPendingTimelock(pendingTimelockAddress, _address);
}
/// @notice The ```_requirePendingTimelock``` function reverts if msg.sender is not pending timelock address/// @dev This function is to be implemented by a public functionfunction_requireSenderIsPendingTimelock() internalview{
_requireIsPendingTimelock(msg.sender);
}
// ============================================================================================// Functions: Events// ============================================================================================/// @notice The ```TimelockTransferStarted``` event is emitted when the timelock transfer is initiated/// @param previousTimelock The address of the previous timelock/// @param newTimelock The address of the new timelockeventTimelockTransferStarted(addressindexed previousTimelock, addressindexed newTimelock);
/// @notice The ```TimelockTransferred``` event is emitted when the timelock transfer is completed/// @param previousTimelock The address of the previous timelock/// @param newTimelock The address of the new timelockeventTimelockTransferred(addressindexed previousTimelock, addressindexed newTimelock);
// ============================================================================================// Functions: Errors// ============================================================================================/// @notice Emitted when timelock is transferrederrorAddressIsNotTimelock(address timelockAddress, address actualAddress);
/// @notice Emitted when pending timelock is transferrederrorAddressIsNotPendingTimelock(address pendingTimelockAddress, address actualAddress);
}