Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
GMigration
Compiler Version
v0.8.10+commit.fc410830
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: AGPLv3 pragma solidity 0.8.10; import {ERC20} from "ERC20.sol"; import {Ownable} from "Ownable.sol"; import {SafeTransferLib} from "SafeTransferLib.sol"; import {ICurve3Pool} from "ICurve3Pool.sol"; import {Constants} from "Constants.sol"; import {Errors} from "Errors.sol"; import {GTranche} from "GTranche.sol"; import {GVault} from "GVault.sol"; import {SeniorTranche} from "SeniorTranche.sol"; // ________ ________ ________ // |\ ____\|\ __ \|\ __ \ // \ \ \___|\ \ \|\ \ \ \|\ \ // \ \ \ __\ \ _ _\ \ \\\ \ // \ \ \|\ \ \ \\ \\ \ \\\ \ // \ \_______\ \__\\ _\\ \_______\ // \|_______|\|__|\|__|\|_______| // gro protocol: https://github.com/groLabs/GSquared /// @title GMigration /// @notice Responsible for migrating funds from old gro protocol to the new gro protocol /// this contract converts stables to 3crv and then deposits into the new GVault which in turn /// is deposited into the gTranche. contract GMigration is Ownable, Constants { using SafeTransferLib for ERC20; address constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F; address constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; address constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7; address constant THREE_POOL = 0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7; address constant THREE_POOL_TOKEN = 0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490; address constant PWRD = 0xF0a93d4994B3d98Fb5e3A2F90dBc2d69073Cb86b; GVault immutable gVault; bool IsGTrancheSet; GTranche public gTranche; uint256 public seniorTrancheDollarAmount; constructor(GVault _gVault) { gVault = _gVault; } /// @notice Set address of gTranche /// @dev Needs to be set after deploying gTranche /// @param _gTranche address of gTranche function setGTranche(GTranche _gTranche) external onlyOwner { if (IsGTrancheSet) { revert Errors.TrancheAlreadySet(); } gTranche = _gTranche; IsGTrancheSet = true; } /// @notice Migrates funds from old gro-protocol to new gro-protocol /// @dev assumes gMigration has all stables from old gro protocol /// @param minAmountThreeCRV minimum amount of 3crv expected from swapping all stables function prepareMigration( uint256 minAmountThreeCRV, uint256 minAmountShares ) external onlyOwner { if (!IsGTrancheSet) { revert Errors.TrancheNotSet(); } // read senior tranche value before migration seniorTrancheDollarAmount = SeniorTranche(PWRD).totalAssets(); uint256 DAI_BALANCE = ERC20(DAI).balanceOf(address(this)); uint256 USDC_BALANCE = ERC20(USDC).balanceOf(address(this)); uint256 USDT_BALANCE = ERC20(USDT).balanceOf(address(this)); // approve three pool ERC20(DAI).safeApprove(THREE_POOL, DAI_BALANCE); ERC20(USDC).safeApprove(THREE_POOL, USDC_BALANCE); ERC20(USDT).safeApprove(THREE_POOL, USDT_BALANCE); // swap for 3crv ICurve3Pool(THREE_POOL).add_liquidity( [DAI_BALANCE, USDC_BALANCE, USDT_BALANCE], minAmountThreeCRV ); //check 3crv amount received uint256 depositAmount = ERC20(THREE_POOL_TOKEN).balanceOf( address(this) ); // approve 3crv for GVault ERC20(THREE_POOL_TOKEN).safeApprove(address(gVault), depositAmount); // deposit into GVault uint256 shareAmount = gVault.deposit(depositAmount, address(this)); if (shareAmount < minAmountShares) revert Errors.InsufficientShares(); // approve gVaultTokens for gTranche ERC20(address(gVault)).safeApprove(address(gTranche), shareAmount); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. /// @author Solmate (https://github.com/Rari-Capital/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. abstract contract ERC20 { /*/////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Transfer(address indexed from, address indexed to, uint256 amount); event Approval( address indexed owner, address indexed spender, uint256 amount ); /*/////////////////////////////////////////////////////////////// METADATA STORAGE //////////////////////////////////////////////////////////////*/ string public name; string public symbol; uint8 public immutable decimals; /*/////////////////////////////////////////////////////////////// ERC20 STORAGE //////////////////////////////////////////////////////////////*/ uint256 public totalSupply; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; /*/////////////////////////////////////////////////////////////// EIP-2612 STORAGE //////////////////////////////////////////////////////////////*/ bytes32 public constant PERMIT_TYPEHASH = keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ); uint256 internal immutable INITIAL_CHAIN_ID; bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; mapping(address => uint256) public nonces; /*/////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor( string memory _name, string memory _symbol, uint8 _decimals ) { name = _name; symbol = _symbol; decimals = _decimals; INITIAL_CHAIN_ID = block.chainid; INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); } /*/////////////////////////////////////////////////////////////// ERC20 LOGIC //////////////////////////////////////////////////////////////*/ function approve(address spender, uint256 amount) public virtual returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } function transfer(address to, uint256 amount) public virtual returns (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); return true; } function transferFrom( address from, address to, uint256 amount ) public virtual returns (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); return true; } /*/////////////////////////////////////////////////////////////// EIP-2612 LOGIC //////////////////////////////////////////////////////////////*/ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual { require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); // Unchecked because the only math done is incrementing // the owner's nonce which cannot realistically overflow. unchecked { bytes32 digest = keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(), keccak256( abi.encode( PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline ) ) ) ); address recoveredAddress = ecrecover(digest, v, r, s); require( recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER" ); allowance[recoveredAddress][spender] = value; } emit Approval(owner, spender, value); } function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); } function computeDomainSeparator() internal view virtual returns (bytes32) { return keccak256( 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) internal virtual { 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(address from, uint256 amount) internal virtual { 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); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/Ownable.sol) pragma solidity ^0.8.0; import "Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _transferOwnership(_msgSender()); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(owner() == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^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. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; import {ERC20} from "ERC20.sol"; /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol) /// @author Modified from Gnosis (https://github.com/gnosis/gp-v2-contracts/blob/main/src/contracts/libraries/GPv2SafeERC20.sol) /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer. library SafeTransferLib { /*/////////////////////////////////////////////////////////////// ETH OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferETH(address to, uint256 amount) internal { bool callStatus; assembly { // Transfer the ETH and store if it succeeded or not. callStatus := call(gas(), to, amount, 0, 0, 0, 0) } require(callStatus, "ETH_TRANSFER_FAILED"); } /*/////////////////////////////////////////////////////////////// ERC20 OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferFrom( ERC20 token, address from, address to, uint256 amount ) internal { bool callStatus; assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata to memory piece by piece: mstore( freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000 ) // Begin with the function selector. mstore( add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff) ) // Mask and append the "from" argument. mstore( add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff) ) // Mask and append the "to" argument. mstore(add(freeMemoryPointer, 68), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value. // Call the token and store if it succeeded or not. // We use 100 because the calldata length is 4 + 32 * 3. callStatus := call(gas(), token, 0, freeMemoryPointer, 100, 0, 0) } require( didLastOptionalReturnCallSucceed(callStatus), "TRANSFER_FROM_FAILED" ); } function safeTransfer( ERC20 token, address to, uint256 amount ) internal { bool callStatus; assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata to memory piece by piece: mstore( freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000 ) // Begin with the function selector. mstore( add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff) ) // Mask and append the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value. // Call the token and store if it succeeded or not. // We use 68 because the calldata length is 4 + 32 * 2. callStatus := call(gas(), token, 0, freeMemoryPointer, 68, 0, 0) } require( didLastOptionalReturnCallSucceed(callStatus), "TRANSFER_FAILED" ); } function safeApprove( ERC20 token, address to, uint256 amount ) internal { bool callStatus; assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata to memory piece by piece: mstore( freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000 ) // Begin with the function selector. mstore( add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff) ) // Mask and append the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value. // Call the token and store if it succeeded or not. // We use 68 because the calldata length is 4 + 32 * 2. callStatus := call(gas(), token, 0, freeMemoryPointer, 68, 0, 0) } require(didLastOptionalReturnCallSucceed(callStatus), "APPROVE_FAILED"); } /*/////////////////////////////////////////////////////////////// INTERNAL HELPER LOGIC //////////////////////////////////////////////////////////////*/ function didLastOptionalReturnCallSucceed(bool callStatus) private pure returns (bool success) { assembly { // Get how many bytes the call returned. let returnDataSize := returndatasize() // If the call reverted: if iszero(callStatus) { // Copy the revert message into memory. returndatacopy(0, 0, returnDataSize) // Revert with the same message. revert(0, returnDataSize) } switch returnDataSize case 32 { // Copy the return data into memory. returndatacopy(0, 0, returnDataSize) // Set success to whether it returned true. success := iszero(iszero(mload(0))) } case 0 { // There was no return data. success := 1 } default { // It returned some malformed input. success := 0 } } } }
// SPDX-License-Identifier: AGPLv3 pragma solidity 0.8.10; /// Curve 3pool interface interface ICurve3Pool { function get_virtual_price() external view returns (uint256); function add_liquidity( uint256[3] calldata _deposit_amounts, uint256 _min_mint_amount ) external; function remove_liquidity_one_coin( uint256 _token_amount, int128 i, uint256 min_amount ) external; function balanceOf(address account) external view returns (uint256); }
// SPDX-License-Identifier: AGPLv3 pragma solidity 0.8.10; contract Constants { uint8 public constant N_COINS = 3; uint8 public constant DEFAULT_DECIMALS = 18; // GToken and Controller use these decimals uint256 public constant DEFAULT_DECIMALS_FACTOR = uint256(10)**DEFAULT_DECIMALS; uint8 public constant CHAINLINK_PRICE_DECIMALS = 8; uint256 public constant CHAINLINK_PRICE_DECIMAL_FACTOR = uint256(10)**CHAINLINK_PRICE_DECIMALS; uint8 public constant PERCENTAGE_DECIMALS = 4; uint256 public constant PERCENTAGE_DECIMAL_FACTOR = uint256(10)**PERCENTAGE_DECIMALS; uint256 public constant CURVE_RATIO_DECIMALS = 6; uint256 public constant CURVE_RATIO_DECIMALS_FACTOR = uint256(10)**CURVE_RATIO_DECIMALS; }
// SPDX-License-Identifier: AGPLv3 pragma solidity 0.8.10; library Errors { // Common error AlreadyMigrated(); // 0xca1c3cbc error AmountIsZero(); // 0x43ad20fc error ChainLinkFeedStale(); //0x3bc80ea6 error IndexTooHigh(); // 0xfbf22ac0 error IncorrectSweepToken(); // 0x25371b04 error LTMinAmountExpected(); //less than 0x3d93e699 error NotEnoughBalance(); // 0xad3a8b9e error ZeroAddress(); //0xd92e233d error MinDeposit(); //0x11bcd830 // GMigration error TrancheAlreadySet(); //0xe8ce7222 error TrancheNotSet(); //0xc7896cf2 // GTranche error UtilisationTooHigh(); // 0x01dbe4de error MsgSenderNotTranche(); // 0x7cda3092 error NoAssets(); // 0x5373815f // GVault error InsufficientShares(); // 0x39996567 error InsufficientAssets(); // 0x96d80433 error IncorrectStrategyAccounting(); //0x7b6d99a5 error IncorrectVaultOnStrategy(); //0x7408aa63 error OverDepositLimit(); //0xbf41e3d0 error StrategyActive(); // 0xebb33d91 error StrategyNotActive(); // 0xdc974a98 error StrategyDebtNotZero(); // 0x332c333c error StrategyLossTooHigh(); // 0xa9aba8bd error VaultDebtRatioTooHigh(); //0xf6f34eca error VaultFeeTooHigh(); //0xb6659cb6 error ZeroAssets(); //0x32d971dc error ZeroShares(); //0x9811e0c7 //Whitelist error NotInWhitelist(); // 0x5b0aa2ba }
// SPDX-License-Identifier: AGPLv3 pragma solidity 0.8.10; import {Ownable} from "Ownable.sol"; import {IGTranche} from "IGTranche.sol"; import {IOracle} from "IOracle.sol"; import {IPnL} from "IPnL.sol"; import {ERC4626} from "ERC4626.sol"; import {Errors} from "Errors.sol"; import {FixedTokensCurve} from "FixedTokensCurve.sol"; import {GMigration} from "GMigration.sol"; import {IGToken} from "IGToken.sol"; // ________ ________ ________ // |\ ____\|\ __ \|\ __ \ // \ \ \___|\ \ \|\ \ \ \|\ \ // \ \ \ __\ \ _ _\ \ \\\ \ // \ \ \|\ \ \ \\ \\ \ \\\ \ // \ \_______\ \__\\ _\\ \_______\ // \|_______|\|__|\|__|\|_______| // gro protocol: https://github.com/groLabs/GSquared /// @title GTranche /// @notice GTranche - Lego block for handling tranching /// /// ############################################### /// GTranche Specification /// ############################################### /// /// The GTranche provides a novel way for insurance to be implemented on the blockchain, /// allowing for users who seek a safer yield opportunity (senior tranche) to do so by /// providing part of their deposit as leverage for an insurer (junior tranche). Which /// is done by distributing parts of the yield generated by underlying tokens based /// on the demand for insurance (utilisation ratio). /// This version of the tranche takes advantage of the new tokenized vault standard /// (https://eips.ethereum.org/EIPS/eip-4626) and acts as a wrapper for 4626 token in /// order to generate and distribute yield. /// /// This contract is one part of two required to define a tranche: /// 1) GTranche module - defines a set of tokens, and handles accounting /// and yield distribution between the Senior and Junior tranche. /// 2) oracle/relation module - defines the relation between the tokens in /// the tranche /// /// The following logic is covered in the GTranche contract: /// - Deposit: /// - User deposits takes an EIP-4626 token and evaluates it to a common denominator, /// which indicates the value of their deposit and the number of tranche tokens /// that get minted /// - Withdrawal: /// - User withdrawals takes tranche tokens and evaluates their value to EIP-4626 tokens, /// which indicates the number of tokens that should be returned to the user /// on withdrawal /// - PnL: /// - User interactions evaluates the latest price per share of the underlying /// 4626 compatible tokens, effectively handling front-running of gains/losses. /// Its important that the underlying EIP-4626 cannot be price manipulated, as this /// would break the pnl functionality of this contract. contract GTranche is IGTranche, FixedTokensCurve, Ownable { /*////////////////////////////////////////////////////////////// CONSTANTS & IMMUTABLES //////////////////////////////////////////////////////////////*/ // Module defining relations between underlying assets IOracle public immutable oracle; // Migration contract GMigration private immutable gMigration; uint256 public constant minDeposit = 1e18; /*////////////////////////////////////////////////////////////// STORAGE VARIABLES & TYPES //////////////////////////////////////////////////////////////*/ // SENIOR / JUNIOR Tranche uint256 public utilisationThreshold = 10000; IPnL public pnl; // migration state bool public hasMigratedFromOldTranche; bool public hasMigrated; address newGTranche; /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event LogNewDeposit( address indexed sender, address indexed recipient, uint256 amount, uint256 index, bool indexed tranche, uint256 calcAmount ); event LogNewWithdrawal( address indexed sender, address indexed recipient, uint256 amount, uint256 index, bool indexed tranche, uint256 yieldTokenAmounts, uint256 calcAmount ); event LogNewUtilisationThreshold(uint256 newThreshold); event LogNewPnL(int256 profit, int256 loss); event LogMigration( uint256 JuniorTrancheBalance, uint256 SeniorTrancheBalance, uint256[] YieldTokenBalances ); event LogSetNewPnLLogic(address pnl); event LogMigrationPrepared(address newGTranche); event LogMigrationFinished(address newGTranche); constructor( address[] memory _yieldTokens, address[2] memory _trancheTokens, IOracle _oracle, GMigration _gMigration ) FixedTokensCurve(_yieldTokens, _trancheTokens) { oracle = _oracle; gMigration = _gMigration; } /*////////////////////////////////////////////////////////////// SETTERS //////////////////////////////////////////////////////////////*/ /// @notice Set the threshold for when utilisation will prohibit deposit /// from the senior tranche, or withdrawals for the junior /// @param _newThreshold target utilisation threshold function setUtilisationThreshold(uint256 _newThreshold) external onlyOwner { utilisationThreshold = _newThreshold; emit LogNewUtilisationThreshold(_newThreshold); } /// @notice Set the pnl logic of the tranche /// @param _pnl new pnl logic function setPnL(IPnL _pnl) external onlyOwner { pnl = _pnl; emit LogSetNewPnLLogic(address(_pnl)); } /*////////////////////////////////////////////////////////////// DEPOSIT/WITHDRAW LOGIC //////////////////////////////////////////////////////////////*/ /// @notice Handles deposit logic for GTranche: /// User deposits underlying yield tokens which values get calculated /// to a common denominator used to price the tranches in. This operation /// relies on the existence of a relation/oracle module that allows this /// contract to establish a relation between the underlying yield tokens. /// Any unearned profit will be realized before the tokens are minted, /// effectively stopping the user from front-running profit. /// @param _amount amount of yield token user deposits /// @param _index index of yield token deposited /// @param _tranche tranche user wishes to go into /// @param _recipient recipient of tranche tokens /// @return trancheAmount amount of tranche tokens minted /// @return calcAmount value of tranche token in common denominator (USD) /// @dev this function will revert if a senior tranche deposit makes the utilisation /// exceed the utilisation ratio function deposit( uint256 _amount, uint256 _index, bool _tranche, address _recipient ) external override returns (uint256 trancheAmount, uint256 calcAmount) { ERC4626 token = getYieldToken(_index); token.transferFrom(msg.sender, address(this), _amount); IGToken trancheToken = getTrancheToken(_tranche); uint256 factor; uint256 trancheUtilisation; // update value of current tranches - this prevents front-running of profits (trancheUtilisation, calcAmount, factor) = updateDistribution( _amount, _index, _tranche, false ); if (calcAmount < minDeposit) { revert("GTranche: deposit amount too low"); } if (_tranche && trancheUtilisation > utilisationThreshold) { revert Errors.UtilisationTooHigh(); } tokenBalances[_index] += _amount; trancheToken.mint(_recipient, factor, calcAmount); emit LogNewDeposit( msg.sender, _recipient, _amount, _index, _tranche, calcAmount ); if (_tranche) trancheAmount = calcAmount; else trancheAmount = (calcAmount * factor) / DEFAULT_FACTOR; } /// @notice Handles withdrawal logic: /// User redeems an amount of tranche token for underlying yield tokens, any loss/profit /// will be realized before the tokens are burned, effectively stopping the user from /// front-running losses or lose out on gains when redeeming /// @param _amount amount of tranche tokens to redeem /// @param _index index of yield token the user wishes to withdraw /// @param _tranche tranche user wishes to redeem /// @param _recipient recipient of the yield tokens /// @return yieldTokenAmounts amount of underlying tokens withdrawn /// @return calcAmount value of tranche token in common denominator (USD) /// @dev this function will revert if a senior tranche deposit makes the utilisation function withdraw( uint256 _amount, uint256 _index, bool _tranche, address _recipient ) external override returns (uint256 yieldTokenAmounts, uint256 calcAmount) { IGToken trancheToken = getTrancheToken(_tranche); if (_amount > trancheToken.balanceOf(msg.sender)) { revert Errors.NotEnoughBalance(); } ERC4626 token = getYieldToken(_index); uint256 factor; // = _calcFactor(_tranche); uint256 trancheUtilisation; // update value of current tranches - this prevents front-running of losses (trancheUtilisation, calcAmount, factor) = updateDistribution( _amount, _index, _tranche, true ); if (!_tranche && trancheUtilisation > utilisationThreshold) { revert Errors.UtilisationTooHigh(); } yieldTokenAmounts = _calcTokenAmount(_index, calcAmount, false); tokenBalances[_index] -= yieldTokenAmounts; trancheToken.burn(msg.sender, factor, calcAmount); token.transfer(_recipient, yieldTokenAmounts); emit LogNewWithdrawal( msg.sender, _recipient, _amount, _index, _tranche, yieldTokenAmounts, calcAmount ); return (yieldTokenAmounts, calcAmount); } /*////////////////////////////////////////////////////////////// CORE LOGIC //////////////////////////////////////////////////////////////*/ /// @notice Get the current utilisation ratio of the tranche in BP function utilisation() external view returns (uint256) { (uint256[NO_OF_TRANCHES] memory _totalValue, , ) = pnlDistribution(); if (_totalValue[1] == 0) return 0; return _totalValue[0] > 0 ? (_totalValue[1] * DEFAULT_DECIMALS) / (_totalValue[0]) : type(uint256).max; } /// @notice Update the current assets in the Junior/Senior tranche by /// taking the change in value of the underlying yield token into account since /// the previous interaction and distributing these based on the profit /// distribution curve. /// @param _amount value of deposit/withdrawal /// @param _index index of yield token /// @param _tranche senior or junior tranche being deposited/withdrawn /// @param _withdraw withdrawal or deposit /// @return trancheUtilisation current utilisation of the two tranches (senior / junior) /// @return calcAmount value of tranche token in common denominator (USD) /// @return factor factor applied to the tranche token function updateDistribution( uint256 _amount, uint256 _index, bool _tranche, bool _withdraw ) internal returns ( uint256 trancheUtilisation, uint256 calcAmount, uint256 factor ) { ( uint256[NO_OF_TRANCHES] memory _totalValue, int256 profit, int256 loss ) = _pnlDistribution(); factor = _tranche ? _calcFactor(_tranche, _totalValue[1]) : _calcFactor(_tranche, _totalValue[0]); if (_withdraw) { calcAmount = _tranche ? _amount : _calcTrancheValue(_tranche, _amount, factor, _totalValue[0]); if (_tranche) _totalValue[1] -= calcAmount; else _totalValue[0] -= calcAmount; } else { calcAmount = _calcTokenValue(_index, _amount, true); if (_tranche) _totalValue[1] += calcAmount; else _totalValue[0] += calcAmount; } trancheBalances[SENIOR_TRANCHE_ID] = _totalValue[1]; trancheBalances[JUNIOR_TRANCHE_ID] = _totalValue[0]; if (_totalValue[1] == 0) trancheUtilisation = 0; else trancheUtilisation = _totalValue[0] > 0 ? (_totalValue[1] * DEFAULT_DECIMALS) / (_totalValue[0]) : type(uint256).max; emit LogNewTrancheBalance(_totalValue, trancheUtilisation); emit LogNewPnL(profit, loss); return (trancheUtilisation, calcAmount, factor); } /// @notice View of current asset distribution function pnlDistribution() public view returns ( uint256[NO_OF_TRANCHES] memory newTrancheBalances, int256 profit, int256 loss ) { int256[NO_OF_TRANCHES] memory _trancheBalances; int256 totalValue = int256(_calcUnifiedValue()); _trancheBalances[0] = int256(trancheBalances[JUNIOR_TRANCHE_ID]); _trancheBalances[1] = int256(trancheBalances[SENIOR_TRANCHE_ID]); int256 lastTotal = _trancheBalances[0] + _trancheBalances[1]; if (lastTotal > totalValue) { unchecked { loss = lastTotal - totalValue; } int256[NO_OF_TRANCHES] memory losses = pnl.distributeLoss( loss, _trancheBalances ); _trancheBalances[0] -= losses[0]; _trancheBalances[1] -= losses[1]; } else { unchecked { profit = totalValue - lastTotal; } int256[NO_OF_TRANCHES] memory profits = pnl.distributeProfit( profit, _trancheBalances ); _trancheBalances[0] += profits[0]; _trancheBalances[1] += profits[1]; } newTrancheBalances[0] = uint256(_trancheBalances[0]); newTrancheBalances[1] = uint256(_trancheBalances[1]); } /// @notice Calculate the changes in underlying token value and distribute profit function _pnlDistribution() internal returns ( uint256[NO_OF_TRANCHES] memory newTrancheBalances, int256 profit, int256 loss ) { int256[NO_OF_TRANCHES] memory _trancheBalances; int256 totalValue = int256(_calcUnifiedValue()); _trancheBalances[0] = int256(trancheBalances[JUNIOR_TRANCHE_ID]); _trancheBalances[1] = int256(trancheBalances[SENIOR_TRANCHE_ID]); int256 lastTotal = _trancheBalances[0] + _trancheBalances[1]; if (lastTotal > totalValue) { unchecked { loss = lastTotal - totalValue; } int256[NO_OF_TRANCHES] memory losses = pnl.distributeAssets( true, loss, _trancheBalances ); _trancheBalances[0] -= losses[0]; _trancheBalances[1] -= losses[1]; } else { unchecked { profit = totalValue - lastTotal; } int256[NO_OF_TRANCHES] memory profits = pnl.distributeAssets( false, profit, _trancheBalances ); _trancheBalances[0] += profits[0]; _trancheBalances[1] += profits[1]; } newTrancheBalances[0] = uint256(_trancheBalances[0]); newTrancheBalances[1] = uint256(_trancheBalances[1]); } /*////////////////////////////////////////////////////////////// Price/Value logic //////////////////////////////////////////////////////////////*/ /// @notice Calculate the price of the underlying yield token /// @param _index index of yield token /// @param _amount amount of yield tokens /// @param _deposit is the transaction a deposit or a withdrawal function _calcTokenValue( uint256 _index, uint256 _amount, bool _deposit ) internal view returns (uint256) { return oracle.getSinglePrice( _index, getYieldTokenValue(_index, _amount), _deposit ); } /// @notice Calculate the number of yield token for the given amount /// @param _index index of yield token /// @param _amount amount to convert to yield tokens /// @param _deposit is the transaction a deposit or a withdrawal function _calcTokenAmount( uint256 _index, uint256 _amount, bool _deposit ) internal view returns (uint256) { return getYieldTokenAmount( _index, oracle.getTokenAmount(_index, _amount, _deposit) ); } /// @notice Calculate the value of all underlying yield tokens function _calcUnifiedValue() internal view returns (uint256 totalValue) { uint256[NO_OF_TOKENS] memory yieldTokenValues = getYieldTokenValues(); uint256[] memory tokenValues = new uint256[](NO_OF_TOKENS); for (uint256 i; i < NO_OF_TOKENS; ++i) { tokenValues[i] = yieldTokenValues[i]; } totalValue = oracle.getTotalValue(tokenValues); } /*////////////////////////////////////////////////////////////// Migration LOGIC //////////////////////////////////////////////////////////////*/ /// @notice Migrates funds from the old gro protocol /// @dev Can only be run once and is and intermediary step to move assets /// from gro-protocol to GSquared. This function is ultimately going to /// be removed from newer iterations of this smart contract as it serves /// no purpose for new tranches. function migrateFromOldTranche() external onlyOwner { if (hasMigratedFromOldTranche) { revert Errors.AlreadyMigrated(); } // only one token in the initial version of the GTranche uint256 token_index = NO_OF_TOKENS - 1; ERC4626 token = getYieldToken(token_index); uint256[] memory yieldTokenShares = new uint256[](NO_OF_TOKENS); uint256 _shares = token.balanceOf(address(gMigration)); yieldTokenShares[token_index] = _shares; uint256 seniorDollarAmount = gMigration.seniorTrancheDollarAmount(); // calculate yield token shares for seniorDollarAmount uint256 seniorShares = _calcTokenAmount(0, seniorDollarAmount, true); // get the amount of shares per tranche uint256 juniorShares = _shares - seniorShares; // calculate $ value of each tranche uint256 juniorValue = _calcTokenValue(0, juniorShares, true); uint256 seniorValue = _calcTokenValue(0, seniorShares, true); // update tranche $ balances trancheBalances[SENIOR_TRANCHE_ID] += seniorValue; trancheBalances[JUNIOR_TRANCHE_ID] += juniorValue; // update yield token balances tokenBalances[0] += _shares; hasMigratedFromOldTranche = true; token.transferFrom(address(gMigration), address(this), _shares); updateDistribution(0, 0, true, false); emit LogMigration(juniorValue, seniorValue, yieldTokenShares); } /// @notice Set the target for the migration /// @dev This should be kept behind a timelock as the address could be any EOA /// which could drain funds. This function should ultimately be removed /// @param _newGTranche address of new GTranche function prepareMigration(address _newGTranche) external onlyOwner { newGTranche = _newGTranche; emit LogMigrationPrepared(_newGTranche); } /// @notice Transfer funds and update Tranches values /// @dev Updates the state of the tranche post migration. /// This function should ultimately be removed function finalizeMigration() external override { if (msg.sender != newGTranche) revert Errors.MsgSenderNotTranche(); if (hasMigrated) { revert Errors.AlreadyMigrated(); } ERC4626 token; for (uint256 index; index < NO_OF_TOKENS; index++) { token = getYieldToken(index); token.transfer(msg.sender, token.balanceOf(address(this))); tokenBalances[index] = token.balanceOf(address(this)); } updateDistribution(0, 0, true, false); emit LogMigrationFinished(msg.sender); } /// @notice Migrate assets from old GTranche to new GTranche /// @dev Assumes same mapping of yield tokens but you can have more at increased indexes /// in the new tranche. This function should be behind a timelock. /// @param _oldGTranche address of the old GTranche function migrate(address _oldGTranche) external onlyOwner { GTranche oldTranche = GTranche(_oldGTranche); uint256 oldSeniorTrancheBalance = oldTranche.trancheBalances(true); uint256 oldJuniorTrancheBalance = oldTranche.trancheBalances(false); trancheBalances[SENIOR_TRANCHE_ID] += oldSeniorTrancheBalance; trancheBalances[JUNIOR_TRANCHE_ID] += oldJuniorTrancheBalance; uint256[] memory yieldTokenBalances = new uint256[]( oldTranche.NO_OF_TOKENS() ); oldTranche.finalizeMigration(); uint256 oldBalance; uint256 currentBalance; for (uint256 index = 0; index < NO_OF_TOKENS; index++) { ERC4626 token = getYieldToken(index); oldBalance = tokenBalances[index]; currentBalance = token.balanceOf(address(this)); tokenBalances[index] = currentBalance; yieldTokenBalances[index] = currentBalance - oldBalance; } updateDistribution(0, 0, true, false); hasMigrated = true; emit LogMigration( trancheBalances[JUNIOR_TRANCHE_ID], trancheBalances[SENIOR_TRANCHE_ID], yieldTokenBalances ); } /*////////////////////////////////////////////////////////////// Legacy logic (GTokens) //////////////////////////////////////////////////////////////*/ // Current BASE of legacy GVT (Junior tranche token) uint256 internal constant JUNIOR_INIT_BASE = 5000000000000000; /// @notice This function exists to support the older versions of the GToken /// return value of underlying token based on caller function gTokenTotalAssets() external view returns (uint256) { (uint256[NO_OF_TRANCHES] memory _totalValue, , ) = pnlDistribution(); if (msg.sender == JUNIOR_TRANCHE) return _totalValue[0]; else if (msg.sender == SENIOR_TRANCHE) return _totalValue[1]; else return _totalValue[0] + _totalValue[1]; } /// @notice calculate the number of tokens for the given amount /// @param _tranche junior or senior tranche /// @param _amount amount to transform to tranche tokens /// @param _factor factor applied to tranche token /// @param _total total value in tranche function _calcTrancheValue( bool _tranche, uint256 _amount, uint256 _factor, uint256 _total ) internal view returns (uint256 amount) { if (_factor == 0) revert Errors.NoAssets(); amount = (_amount * DEFAULT_FACTOR) / _factor; if (amount > _total) return _total; return amount; } /// @notice calculate the tranches factor /// @param _tranche junior or senior tranche /// @param _totalAssets total value in tranche /// @return factor factor to be applied to tranche /// @dev The factor is used to either determine the value of the tranche /// or the number of tokens to be issued for a given amount function _calcFactor(bool _tranche, uint256 _totalAssets) internal view returns (uint256 factor) { IGToken trancheToken = getTrancheToken(_tranche); uint256 init_base = _tranche ? DEFAULT_FACTOR : JUNIOR_INIT_BASE; uint256 supply = trancheToken.totalSupplyBase(); if (supply == 0) { return init_base; } if (_totalAssets > 0) { return (supply * DEFAULT_FACTOR) / _totalAssets; } } }
// SPDX-License-Identifier: AGPLv3 pragma solidity 0.8.10; interface IGTranche { function deposit( uint256 _amount, uint256 _index, bool _tranche, address recipient ) external returns (uint256, uint256); function withdraw( uint256 _amount, uint256 _index, bool _tranche, address recipient ) external returns (uint256, uint256); function finalizeMigration() external; function utilisationThreshold() external view returns (uint256); }
// SPDX-License-Identifier: AGPLv3 pragma solidity 0.8.10; interface IOracle { function getSwappingPrice( uint256 _i, uint256 _j, uint256 _amount, bool _deposit ) external view returns (uint256); function getSinglePrice( uint256 _i, uint256 _amount, bool _deposit ) external view returns (uint256); function getTokenAmount( uint256 _i, uint256 _amount, bool _deposit ) external view returns (uint256); function getTotalValue(uint256[] memory _amount) external view returns (uint256); }
// SPDX-License-Identifier: AGPLv3 pragma solidity 0.8.10; /// @title IPnL /// @notice PnL interface for a dsitribution module with two tranches interface IPnL { function distributeAssets( bool _loss, int256 _amount, int256[2] calldata _trancheBalances ) external returns (int256[2] memory amounts); function distributeLoss(int256 _amount, int256[2] calldata _trancheBalances) external view returns (int256[2] memory loss); function distributeProfit( int256 _amount, int256[2] calldata _trancheBalances ) external view returns (int256[2] memory profit); }
// SPDX-License-Identifier: MIT // Adpated from OZ Draft Implementation pragma solidity 0.8.10; import {ERC20} from "ERC20.sol"; abstract contract ERC4626 is ERC20 { event Deposit( address indexed caller, address indexed owner, uint256 assets, uint256 shares ); event Withdraw( address indexed caller, address indexed receiver, address indexed owner, uint256 assets, uint256 shares ); /** * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing. * * - MUST be an ERC-20 token contract. * - MUST NOT revert. */ function asset() external view virtual returns (ERC20); /** * @dev Returns the total amount of the underlying asset that is “managed” by Vault. * * - SHOULD include any compounding that occurs from yield. * - MUST be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT revert. */ function totalAssets() external view virtual returns (uint256 totalManagedAssets); /** * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal * scenario where all the conditions are met. * * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT show any variations depending on the caller. * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. * - MUST NOT revert. * * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and * from. */ function convertToShares(uint256 assets) external view virtual returns (uint256 shares); /** * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal * scenario where all the conditions are met. * * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT show any variations depending on the caller. * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. * - MUST NOT revert. * * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and * from. */ function convertToAssets(uint256 shares) external view virtual returns (uint256 assets); /** * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver, * through a deposit call. * * - MUST return a limited value if receiver is subject to some deposit limit. * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited. * - MUST NOT revert. */ function maxDeposit(address receiver) external view virtual returns (uint256 maxAssets); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given * current on-chain conditions. * * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit * call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called * in the same transaction. * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the * deposit would be accepted, regardless if the user has enough tokens approved, etc. * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by depositing. */ function previewDeposit(uint256 assets) external view virtual returns (uint256 shares); /** * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens. * * - MUST emit the Deposit event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * deposit execution, and are accounted for during deposit. * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not * approving enough underlying tokens to the Vault contract, etc). * * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. */ function deposit(uint256 assets, address receiver) external virtual returns (uint256 shares); /** * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call. * - MUST return a limited value if receiver is subject to some mint limit. * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted. * - MUST NOT revert. */ function maxMint(address receiver) external view virtual returns (uint256 maxShares); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given * current on-chain conditions. * * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call * in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the * same transaction. * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint * would be accepted, regardless if the user has enough tokens approved, etc. * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by minting. */ function previewMint(uint256 shares) external view virtual returns (uint256 assets); /** * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens. * * - MUST emit the Deposit event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint * execution, and are accounted for during mint. * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not * approving enough underlying tokens to the Vault contract, etc). * * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. */ function mint(uint256 shares, address receiver) external virtual returns (uint256 assets); /** * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the * Vault, through a withdraw call. * * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. * - MUST NOT revert. */ function maxWithdraw(address owner) external view virtual returns (uint256 maxAssets); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, * given current on-chain conditions. * * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw * call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if * called * in the same transaction. * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though * the withdrawal would be accepted, regardless if the user has enough shares, etc. * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by depositing. */ function previewWithdraw(uint256 assets) external view virtual returns (uint256 shares); /** * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver. * * - MUST emit the Withdraw event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * withdraw execution, and are accounted for during withdraw. * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner * not having enough shares, etc). * * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. * Those methods should be performed separately. */ function withdraw( uint256 assets, address receiver, address owner ) external virtual returns (uint256 shares); /** * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault, * through a redeem call. * * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock. * - MUST NOT revert. */ function maxRedeem(address owner) external view virtual returns (uint256 maxShares); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, * given current on-chain conditions. * * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call * in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the * same transaction. * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the * redemption would be accepted, regardless if the user has enough shares, etc. * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by redeeming. */ function previewRedeem(uint256 shares) external view virtual returns (uint256 assets); /** * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver. * * - MUST emit the Withdraw event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * redeem execution, and are accounted for during redeem. * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner * not having enough shares, etc). * * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed. * Those methods should be performed separately. */ function redeem( uint256 shares, address receiver, address owner ) external virtual returns (uint256 assets); }
// SPDX-License-Identifier: AGPLv3 pragma solidity 0.8.10; import {Errors} from "Errors.sol"; import {ERC4626} from "ERC4626.sol"; import {IGToken} from "IGToken.sol"; // ________ ________ ________ // |\ ____\|\ __ \|\ __ \ // \ \ \___|\ \ \|\ \ \ \|\ \ // \ \ \ __\ \ _ _\ \ \\\ \ // \ \ \|\ \ \ \\ \\ \ \\\ \ // \ \_______\ \__\\ _\\ \_______\ // \|_______|\|__|\|__|\|_______| // gro protocol: https://github.com/groLabs/GSquared /// @title FixedTokensCurve /// @notice Token definition contract /// /// ############################################### /// GTranche Tokens specification /// ############################################### /// /// This contract allows us to modify the underpinnings of the tranche /// without having to worry about changing the core logic. The implementation /// beneath supports 3 underlying EIP-4626 compatible tokens, but this contract /// can be modified to use any combination. /// Tranche Tokens: /// - One Senior and one Junior tranche, this should be left unchanged /// Yield Tokens /// - Define one address var. and one decimal var. /// per asset in the tranche /// - Modify the getYieldtoken and getYieldtokenDecimal functions /// to reflect the number of tokens defined above. /// - updated NO_OF_TOKENS to match above number /// /// Disclaimer: /// The tranche has only been tested with EIP-4626 compatible tokens, /// but should in theory be able to work with any tokens as long as /// custom logic is supplied in the getYieldTokenValue function. /// The core logic that defines the relationship between the underlying /// assets in the tranche is defined outside the scope of this contract /// (see oracle/relation module). Also note that this contract assumes /// that the 4626 token has the same decimals as its underlying token, /// this is not guaranteed by EIP-4626 and would have to be modified in /// case these to values deviate, but for the purpose of the token this /// version intends to operate on, this is held true. contract FixedTokensCurve { /*////////////////////////////////////////////////////////////// CONSTANTS & IMMUTABLES //////////////////////////////////////////////////////////////*/ uint256 internal constant DEFAULT_DECIMALS = 10_000; uint256 internal constant DEFAULT_FACTOR = 1_000_000_000_000_000_000; // Tranches uint256 public constant NO_OF_TRANCHES = 2; bool internal constant JUNIOR_TRANCHE_ID = false; bool internal constant SENIOR_TRANCHE_ID = true; // Yield tokens - 1 address + 1 decimal per token uint256 public constant NO_OF_TOKENS = 1; address internal immutable FIRST_TOKEN; uint256 internal immutable FIRST_TOKEN_DECIMALS; address internal immutable JUNIOR_TRANCHE; address internal immutable SENIOR_TRANCHE; /*////////////////////////////////////////////////////////////// STORAGE VARIABLES & TYPES //////////////////////////////////////////////////////////////*/ // Accounting for total amount of yield tokens in the contract uint256[NO_OF_TOKENS] public tokenBalances; // Accounting for the total "value" (as defined in the oracle/relation module) // of the tranches: True => Senior Tranche, False => Junior Tranche mapping(bool => uint256) public trancheBalances; /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event LogNewTrancheBalance( uint256[NO_OF_TRANCHES] balances, uint256 _utilisation ); /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor(address[] memory _yieldTokens, address[2] memory _trancheTokens) { FIRST_TOKEN = _yieldTokens[0]; FIRST_TOKEN_DECIMALS = 10**ERC4626(_yieldTokens[0]).decimals(); JUNIOR_TRANCHE = _trancheTokens[0]; SENIOR_TRANCHE = _trancheTokens[1]; } /*////////////////////////////////////////////////////////////// GETTERS //////////////////////////////////////////////////////////////*/ /// @notice Get the underlying yield token by index /// @param _index index of desired token /// @dev this function needs to be modified if the number of tokens is changed /// @return yieldToken tranches underlying yield token at index function getYieldToken(uint256 _index) public view returns (ERC4626 yieldToken) { if (_index >= NO_OF_TOKENS) { revert Errors.IndexTooHigh(); } return ERC4626(FIRST_TOKEN); } /// @notice Get the underlying yield tokens decimals by index /// @param _index index of desired token /// @dev this function needs to be modified if the number of tokens is changed /// @return decimals token decimals function getYieldTokenDecimals(uint256 _index) public view returns (uint256 decimals) { if (_index >= NO_OF_TOKENS) { revert Errors.IndexTooHigh(); } return FIRST_TOKEN_DECIMALS; } /// @notice Get the underlying tranche token by id (bool) /// @param _tranche boolean representation of tranche token /// @return trancheToken senior or junior tranche function getTrancheToken(bool _tranche) public view returns (IGToken trancheToken) { if (_tranche) return IGToken(SENIOR_TRANCHE); return IGToken(JUNIOR_TRANCHE); } /// @notice Get values of all underlying yield tokens /// @dev this function needs to be modified if the number of tokens is changed /// @return values Amount of underlying tokens of yield tokens function getYieldTokenValues() public view returns (uint256[NO_OF_TOKENS] memory values) { values[0] = getYieldTokenValue(0, tokenBalances[0]); } /// @notice Get the amount of yield tokens /// @param _index index of desired token /// @param _amount amount (common denominator) that we want /// to convert to yield tokens /// @return get amount of yield tokens from amount /// @dev Note that this contract assumes that the underlying decimals /// of the 4626 token and its yieldtoken is the same, which /// isnt guaranteed by EIP-4626. The _amount variable is denoted in the /// precision of the common denominator (1E18), return value is denoted in /// the yield tokens decimals function getYieldTokenAmount(uint256 _index, uint256 _amount) internal view returns (uint256) { return getYieldToken(_index).convertToShares(_amount); } /// @notice Get the value of a yield token in its underlying token /// @param _index index of desired token /// @param _amount amount of yield token that we want to convert /// @dev Note that this contract assumes that the underlying decimals /// of the 4626 token and its yieldtoken is the same, which /// isnt guaranteed by EIP-4626. The _amount variable is denoted in the /// precision of the yield token, return value is denoted in the precision /// of the common denominator (1E18) function getYieldTokenValue(uint256 _index, uint256 _amount) internal view returns (uint256) { return (getYieldToken(_index).convertToAssets(_amount) * DEFAULT_FACTOR) / getYieldTokenDecimals(_index); } }
// SPDX-License-Identifier: AGPLv3 pragma solidity 0.8.10; interface IGToken { function mint( address recipient, uint256 factor, uint256 amount ) external; function burn( address recipient, uint256 factor, uint256 amount ) external; function totalSupplyBase() external view returns (uint256); function factor() external view returns (uint256); function factor(uint256 amount) external view returns (uint256); function balanceOf(address user) external view returns (uint256); }
// SPDX-License-Identifier: AGPLv3 pragma solidity 0.8.10; import {ERC20} from "ERC20.sol"; import {SafeTransferLib} from "SafeTransferLib.sol"; import {Math} from "Math.sol"; import {Ownable} from "Ownable.sol"; import {ReentrancyGuard} from "ReentrancyGuard.sol"; import {IStrategy} from "IStrategy.sol"; import {ERC4626} from "ERC4626.sol"; import {Constants} from "Constants.sol"; import {Errors} from "Errors.sol"; import {StrategyQueue} from "StrategyQueue.sol"; // ________ ________ ________ // |\ ____\|\ __ \|\ __ \ // \ \ \___|\ \ \|\ \ \ \|\ \ // \ \ \ __\ \ _ _\ \ \\\ \ // \ \ \|\ \ \ \\ \\ \ \\\ \ // \ \_______\ \__\\ _\\ \_______\ // \|_______|\|__|\|__|\|_______| // gro protocol: https://github.com/groLabs/GSquared /// @notice GVault - Gro protocol stand alone vault for generating yield /// @title GVault /// @notice Gro protocol stand alone vault for generating yield on /// stablecoins following the EIP-4626 Standard contract GVault is Constants, ERC4626, StrategyQueue, Ownable, ReentrancyGuard { using SafeTransferLib for ERC20; /*////////////////////////////////////////////////////////////// CONSTANTS & IMMUTABLES //////////////////////////////////////////////////////////////*/ // Underlying token ERC20 public immutable override asset; uint256 public immutable minDeposit; /*////////////////////////////////////////////////////////////// STORAGE VARIABLES & TYPES //////////////////////////////////////////////////////////////*/ struct StrategyParams { bool active; uint256 debtRatio; uint256 lastReport; uint256 totalDebt; uint256 totalGain; uint256 totalLoss; } mapping(address => StrategyParams) public strategies; uint256 public vaultAssets; // Slow release of profit uint256 public lockedProfit; uint256 public releaseTime; uint256 public vaultDebtRatio; uint256 public vaultTotalDebt; uint256 public lastReport; // Vault fee address public feeCollector; uint256 public vaultFee; /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ // Strategy events event LogStrategyHarvestReport( address indexed strategy, uint256 gain, uint256 loss, uint256 debtPaid, uint256 debtAdded, uint256 lockedProfit, uint256 lockedProfitBeforeLoss ); event LogStrategyTotalChanges( address indexed strategy, uint256 totalGain, uint256 totalLoss, uint256 totalDebt ); event LogWithdrawalFromStrategy( uint48 strategyId, uint256 strategyDebt, uint256 totalVaultDebt, uint256 lossFromStrategyWithdrawal ); // Vault events event LogNewDebtRatio( address indexed strategy, uint256 debtRatio, uint256 vaultDebtRatio ); event LogNewReleaseFactor(uint256 factor); event LogNewVaultFee(uint256 vaultFee); event LogNewfeeCollector(address feeCollector); /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor(ERC20 _asset) ERC20( string(abi.encodePacked("Gro ", _asset.symbol(), " Vault")), string(abi.encodePacked("gro", _asset.symbol())), _asset.decimals() ) { asset = _asset; minDeposit = 10**_asset.decimals(); // 24 hours release window in seconds releaseTime = 86400; } /*////////////////////////////////////////////////////////////// GETTERS //////////////////////////////////////////////////////////////*/ /// @notice Get number of strategies in underlying vault /// @return number of strategies in the withdrawal queue function getNoOfStrategies() external view returns (uint256) { return noOfStrategies(); } /// @notice Helper function for strategy to get debt from vault function getStrategyDebt() external view returns (uint256) { return strategies[msg.sender].totalDebt; } /// @notice Get total invested in strategy /// @param _index index of strategy /// @return amount of total debt the strategies have to the GVault function getStrategyDebt(uint256 _index) external view returns (uint256 amount) { return strategies[nodes[_index].strategy].totalDebt; } /// @notice Helper function for strategy to get harvest data from vault function getStrategyData() external view returns ( bool, uint256, uint256 ) { StrategyParams storage stratData = strategies[msg.sender]; return (stratData.active, stratData.totalDebt, stratData.lastReport); } /*////////////////////////////////////////////////////////////// SETTERS //////////////////////////////////////////////////////////////*/ /// @notice Set contract that will receive vault fees /// @param _feeCollector address of feeCollector contract function setFeeCollector(address _feeCollector) external onlyOwner { feeCollector = _feeCollector; emit LogNewfeeCollector(_feeCollector); } /// @notice Set fee that is reduced from strategy yields when harvests are called /// @param _fee new strategy fee function setVaultFee(uint256 _fee) external onlyOwner { if (_fee >= 3000) revert Errors.VaultFeeTooHigh(); vaultFee = _fee; emit LogNewVaultFee(_fee); } /// @notice Set how quickly profits are released /// @param _time how quickly profits are released in seconds function setProfitRelease(uint256 _time) external onlyOwner { releaseTime = _time; emit LogNewReleaseFactor(_time); } /*////////////////////////////////////////////////////////////// DEPOSIT/WITHDRAW LOGIC //////////////////////////////////////////////////////////////*/ /// @notice Deposit assets into the GVault /// @param _assets user deposit amount /// @param _receiver Address receiving the shares /// @return shares the number of shares minted during the deposit function deposit(uint256 _assets, address _receiver) external override nonReentrant returns (uint256 shares) { // Check for rounding error since we round down in previewDeposit. if (_assets < minDeposit) revert Errors.MinDeposit(); if ((shares = previewDeposit(_assets)) == 0) revert Errors.ZeroShares(); asset.safeTransferFrom(msg.sender, address(this), _assets); vaultAssets += _assets; _mint(_receiver, shares); emit Deposit(msg.sender, _receiver, _assets, shares); return shares; } /// @notice Request shares to be minted by depositing assets into the GVault /// @param _shares Amount of shares to be minted /// @param _receiver Address receiving the shares /// @return assets the number of asset tokens deposited during the mint of the /// vault shares function mint(uint256 _shares, address _receiver) external override nonReentrant returns (uint256 assets) { // Check for rounding error in previewMint. if ((assets = previewMint(_shares)) < minDeposit) revert Errors.MinDeposit(); asset.safeTransferFrom(msg.sender, address(this), assets); vaultAssets += assets; _mint(_receiver, _shares); emit Deposit(msg.sender, _receiver, assets, _shares); return assets; } /// @notice withdraw assets from the GVault /// @param _assets the amount of want token the caller wants to withdraw /// @param _receiver address receiving the asset token /// @param _owner address that owns the 4626 shares that will be burnt /// @param _minAmount minAmount of assets to return /// @return shares the number of shares burnt during the withdrawal function withdraw( uint256 _assets, address _receiver, address _owner, uint256 _minAmount ) external nonReentrant returns (uint256 shares) { return _withdraw(_assets, _receiver, _owner, _minAmount); } /// @notice withdraw assets from the GVault /// @param _assets the amount of want token the caller wants to withdraw /// @param _receiver address receiving the asset token /// @param _owner address that owns the 4626 shares that will be burnt /// @return shares the number of shares burnt during the withdrawal function withdraw( uint256 _assets, address _receiver, address _owner ) external override nonReentrant returns (uint256 shares) { return _withdraw(_assets, _receiver, _owner, 0); } /// @notice Internal helper function for withdrawal - called by EIP-4626 standard withdraw function /// or custom withdraw function with minAmount. function _withdraw( uint256 _assets, address _receiver, address _owner, uint256 _minAmount ) internal returns (uint256 shares) { if (_assets == 0) revert Errors.ZeroAssets(); shares = previewWithdraw(_assets); if (shares > balanceOf[_owner]) revert Errors.InsufficientShares(); 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; } uint256 vaultBalance; (_assets, vaultBalance) = beforeWithdraw(_assets); if (_assets < _minAmount) revert Errors.InsufficientAssets(); _burn(_owner, shares); asset.safeTransfer(_receiver, _assets); vaultAssets = vaultBalance - _assets; emit Withdraw(msg.sender, _receiver, _owner, _assets, shares); return shares; } /// @notice Redeem GVault shares for the equivalent amount of assets /// @param _shares the number of vault shares the caller wants to burn /// @param _receiver the address that will receive the asset tokens /// @param _owner the owner of the shares that will be burnt /// @return assets the amount of asset tokens sent to the receiver function redeem( uint256 _shares, address _receiver, address _owner ) external override nonReentrant returns (uint256 assets) { if (_shares == 0) revert Errors.ZeroShares(); if (_shares > balanceOf[_owner]) revert Errors.InsufficientShares(); 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; } assets = convertToAssets(_shares); uint256 vaultBalance; (assets, vaultBalance) = beforeWithdraw(assets); _burn(_owner, _shares); asset.safeTransfer(_receiver, assets); vaultAssets = vaultBalance - assets; emit Withdraw(msg.sender, _receiver, _owner, assets, _shares); return assets; } /*////////////////////////////////////////////////////////////// DEPOSIT/WITHDRAWAL LIMIT LOGIC //////////////////////////////////////////////////////////////*/ /// @notice The maximum amount a user can deposit into the vault function maxDeposit(address) public view override returns (uint256 maxAssets) { return type(uint256).max - convertToAssets(totalSupply); } /// @notice Simulate the shares issued for a given deposit /// @param _assets number of asset tokens being deposited /// @return shares number of shares issued for the number of assets provided function previewDeposit(uint256 _assets) public view override returns (uint256 shares) { return convertToShares(_assets); } /// @notice maximum number of shares that can be minted function maxMint(address) public view override returns (uint256 maxShares) { return type(uint256).max - totalSupply; } /// @notice Simulate the number of assets required to mint a specific number of shares /// @param _shares number of shares to mint /// @return assets number of assets required to issue the shares inputted function previewMint(uint256 _shares) public view override returns (uint256 assets) { uint256 _totalSupply = totalSupply; // Saves an extra SLOAD if _totalSupply is non-zero. return _totalSupply == 0 ? _shares : Math.ceilDiv((_shares * _freeFunds()), _totalSupply); } /// @notice maximum amount of asset tokens the owner can withdraw /// @param _owner address of the owner of the GVault Shares /// @return maxAssets maximum amount of asset tokens the owner can withdraw function maxWithdraw(address _owner) public view override returns (uint256 maxAssets) { return convertToAssets(balanceOf[_owner]); } /// @notice return the amount of shares that would be burned for a given number of assets /// @param _assets number of assert tokens to withdraw /// @return shares burnt during withdrawal function previewWithdraw(uint256 _assets) public view override returns (uint256 shares) { uint256 freeFunds_ = _freeFunds(); // Saves an extra SLOAD if _freeFunds is non-zero. return freeFunds_ == 0 ? _assets : Math.ceilDiv(_assets * totalSupply, freeFunds_); } /// @notice maximum number of shares the owner can redeem /// @param _owner address for the owner of the GVault shares /// @return maxShares number of GVault shares the owner has function maxRedeem(address _owner) public view override returns (uint256 maxShares) { return balanceOf[_owner]; } /// @notice Returns the amount of assets that can be redeemed with the shares /// @param _shares the number of shares the caller wants to redeem /// @return assets the number of asset tokens the caller would receive function previewRedeem(uint256 _shares) public view override returns (uint256 assets) { return convertToAssets(_shares); } /*////////////////////////////////////////////////////////////// VAULT ACCOUNTING LOGIC //////////////////////////////////////////////////////////////*/ /// @notice Calculate system total assets including estimated profits function totalAssets() external view override returns (uint256) { return _estimatedTotalAssets(); } /// @notice Calculate system total assets excluding estimated profits function realizedTotalAssets() external view returns (uint256) { return _totalAssets(); } /// @notice Value of asset in shares /// @param _assets amount of asset to convert to shares function convertToShares(uint256 _assets) public view override returns (uint256 shares) { uint256 freeFunds_ = _freeFunds(); // Saves an extra SLOAD if _freeFunds is non-zero. return freeFunds_ == 0 ? _assets : (_assets * totalSupply) / freeFunds_; } /// @notice Value of shares in underlying asset /// @param _shares amount of shares to convert to tokens function convertToAssets(uint256 _shares) public view override returns (uint256 assets) { uint256 _totalSupply = totalSupply; // Saves an extra SLOAD if _totalSupply is non-zero. return _totalSupply == 0 ? _shares : ((_shares * _freeFunds()) / _totalSupply); } /// @notice Gives the price for a single Vault share. /// @return The value of a single share. function getPricePerShare() external view returns (uint256) { return convertToAssets(10**decimals); } /*////////////////////////////////////////////////////////////// STRATEGY LOGIC //////////////////////////////////////////////////////////////*/ /// @notice Number of active strategies in the vaultAdapter function noOfStrategies() internal view returns (uint256) { return strategyQueue.totalNodes; } /// @notice Update the debtRatio of a specific strategy /// @param _strategy target strategy /// @param _debtRatio new debt ratio function setDebtRatio(address _strategy, uint256 _debtRatio) external onlyOwner { if (!strategies[_strategy].active) revert Errors.StrategyNotActive(); _setDebtRatio(_strategy, _debtRatio); } /// @notice Add a new strategy to the vault adapter /// @param _strategy target strategy to add /// @param _debtRatio target debtRatio of strategy function addStrategy(address _strategy, uint256 _debtRatio) external onlyOwner { if (_strategy == ZERO_ADDRESS) revert Errors.ZeroAddress(); if (strategies[_strategy].active) revert Errors.StrategyActive(); if (address(this) != IStrategy(_strategy).vault()) revert Errors.IncorrectVaultOnStrategy(); StrategyParams storage newStrat = strategies[_strategy]; newStrat.active = true; _setDebtRatio(_strategy, _debtRatio); newStrat.lastReport = block.timestamp; _push(_strategy); } /// @notice remove existing strategy from vault by revoking and removing /// from the withdrawal queue /// @param _strategy address of old strategy /// @dev Should be called when all the debt has been paid back to the vault function removeStrategy(address _strategy) external onlyOwner { if (!strategies[_strategy].active) revert Errors.StrategyNotActive(); _revokeStrategy(_strategy); _removeStrategy(_strategy); } /// @notice remove strategy from the withdrawal queue /// @param _strategy address of strategy to remove function _removeStrategy(address _strategy) internal { if (strategies[_strategy].active) revert Errors.StrategyActive(); if (strategies[_strategy].totalDebt > 0) revert Errors.StrategyDebtNotZero(); _pop(_strategy); } /// @notice Remove strategy from vault adapter function revokeStrategy() external { if (!strategies[msg.sender].active) revert Errors.StrategyNotActive(); _revokeStrategy(msg.sender); } /// @notice Move the strategy to a new position /// @param _strategy Target strategy to move /// @param _pos desired position of strategy /// @dev if the _pos value is >= number of strategies in the queue, /// the strategy will be moved to the tail position function moveStrategy(address _strategy, uint256 _pos) external onlyOwner { uint256 currentPos = getStrategyPositions(_strategy); uint256 _strategyId = strategyId[_strategy]; if (currentPos > _pos) move(uint48(_strategyId), uint48(currentPos - _pos), false); else move(uint48(_strategyId), uint48(_pos - currentPos), true); } /// @notice Check how much credits are available for the strategy /// @param _strategy Target strategy function creditAvailable(address _strategy) external view returns (uint256) { return _creditAvailable(_strategy); } /// @notice Same as above but called by the streategy function creditAvailable() external view returns (uint256) { return _creditAvailable(msg.sender); } /// @notice Amount of debt the strategy has to pay back to the vault at next harvest /// @param _strategy target strategy /// @return amount of debt the strategy has to pay back and the current debt ratio of the strategy function excessDebt(address _strategy) external view returns (uint256, uint256) { return _excessDebt(_strategy); } /// @notice Helper function to get strategy's total debt to the vault /// @dev here to simplify strategy's life when trying to get the totalDebt function strategyDebt() external view returns (uint256) { return strategies[msg.sender].totalDebt; } /// @notice Report back any gains/losses from a (strategy) harvest, vault adapter /// calls back debt or gives out more credit to the strategy depending on available /// credit and the strategies current position. /// @param _gain Strategy gains from latest harvest /// @param _loss Strategy losses from latest harvest /// @param _debtPayment Amount strategy can pay back to vault /// @param _emergency Flag to indicate if the harvest was an emergency harvest function report( uint256 _gain, uint256 _loss, uint256 _debtPayment, bool _emergency ) external returns (uint256) { StrategyParams storage _strategy = strategies[msg.sender]; if (!_strategy.active) revert Errors.StrategyNotActive(); if (asset.balanceOf(msg.sender) < _debtPayment) revert Errors.IncorrectStrategyAccounting(); if (_loss > 0) { _reportLoss(msg.sender, _loss); } if (_gain > 0) { _strategy.totalGain += _gain; _strategy.totalDebt += _gain; vaultTotalDebt += _gain; } if (_emergency) { _revokeStrategy(msg.sender); } (uint256 debt, ) = _excessDebt(msg.sender); uint256 debtPayment = Math.min(_debtPayment, debt); if (debtPayment > 0) { _strategy.totalDebt = _strategy.totalDebt - debtPayment; vaultTotalDebt -= debtPayment; debt -= debtPayment; } uint256 credit = _creditAvailable(msg.sender); if (credit > 0) { _strategy.totalDebt += credit; vaultTotalDebt += credit; } uint256 totalAvailable = debtPayment; if (totalAvailable < credit) { asset.safeTransfer(msg.sender, credit - totalAvailable); vaultAssets -= credit - totalAvailable; } else if (totalAvailable > credit) { asset.safeTransferFrom( msg.sender, address(this), totalAvailable - credit ); vaultAssets += totalAvailable - credit; } // Profit is locked and gradually released per block // this computes current locked profit and replace with // the sum of the current and the new profit uint256 lockedProfitBeforeLoss = _calculateLockedProfit() + _calcFees(_gain); // Store how much loss remains after locked profit is removed, // here only for logging purposes if (lockedProfitBeforeLoss > _loss) { lockedProfit = lockedProfitBeforeLoss - _loss; } else { lockedProfit = 0; } lastReport = block.timestamp; _strategy.lastReport = lastReport; if (_emergency) { _removeStrategy(msg.sender); } emit LogStrategyHarvestReport( msg.sender, _gain, _loss, debtPayment, credit, lockedProfit, lockedProfitBeforeLoss ); emit LogStrategyTotalChanges( msg.sender, _strategy.totalGain, _strategy.totalLoss, _strategy.totalDebt ); return credit; } /*////////////////////////////////////////////////////////////// INTERNAL HOOKS //////////////////////////////////////////////////////////////*/ /// @notice Runs before any withdraw function mainly to ensure vault has enough assets /// @param _assets Amount of assets to withdraw /// @return Amount of assets withdrawn and amount of assets in vault function beforeWithdraw(uint256 _assets) internal returns (uint256, uint256) { // If reserves dont cover the withdrawal, start withdrawing from strategies ERC20 _token = asset; uint256 vaultBalance = vaultAssets; if (_assets > vaultBalance) { uint48 _strategyId = strategyQueue.head; while (true) { address _strategy = nodes[_strategyId].strategy; // break if we have withdrawn all we need if (_assets <= vaultBalance) break; uint256 amountNeeded = _assets - vaultBalance; StrategyParams storage _strategyData = strategies[_strategy]; amountNeeded = Math.min(amountNeeded, _strategyData.totalDebt); // If nothing is needed or strategy has no assets, continue if (amountNeeded > 0) { (uint256 withdrawn, uint256 loss) = IStrategy(_strategy) .withdraw(amountNeeded); // Handle the loss if any if (loss > 0) { _assets = _assets - loss; _reportLoss(_strategy, loss); } // Remove withdrawn amount from strategy and vault debts _strategyData.totalDebt -= withdrawn; vaultTotalDebt -= withdrawn; vaultBalance += withdrawn; emit LogWithdrawalFromStrategy( _strategyId, _strategyData.totalDebt, vaultTotalDebt, loss ); } _strategyId = nodes[_strategyId].next; if (_strategyId == 0) break; } if (_assets > vaultBalance) { _assets = vaultBalance; } } return (_assets, vaultBalance); } /// @notice Calculate how much profit is currently locked function _calculateLockedProfit() internal view returns (uint256) { uint256 _releaseTime = releaseTime; uint256 _timeSinceLastReport = block.timestamp - lastReport; if (_releaseTime > _timeSinceLastReport) { uint256 _lockedProfit = lockedProfit; return _lockedProfit - ((_lockedProfit * _timeSinceLastReport) / _releaseTime); } else { return 0; } } /// @notice the number of total assets the GVault has excluding profits /// and losses function _freeFunds() internal view returns (uint256) { return _totalAssets() - _calculateLockedProfit(); } /// @notice Calculate the amount of assets the vault has available for the strategy to pull and invest, /// the available credit is based on the strategies debt ratio and the total available assets /// the vault has /// @param _strategy target strategy /// @dev called during harvest function _creditAvailable(address _strategy) internal view returns (uint256) { StrategyParams memory _strategyData = strategies[_strategy]; uint256 vaultTotalAssets = _totalAssets(); uint256 vaultDebtLimit = (vaultDebtRatio * vaultTotalAssets) / PERCENTAGE_DECIMAL_FACTOR; uint256 _vaultTotalDebt = vaultTotalDebt; uint256 strategyDebtLimit = (_strategyData.debtRatio * vaultTotalAssets) / PERCENTAGE_DECIMAL_FACTOR; uint256 strategyTotalDebt = _strategyData.totalDebt; if ( strategyDebtLimit <= strategyTotalDebt || vaultDebtLimit <= _vaultTotalDebt ) { return 0; } uint256 available = strategyDebtLimit - strategyTotalDebt; available = Math.min(available, vaultDebtLimit - _vaultTotalDebt); return Math.min(available, vaultAssets); } /// @notice Deal with any loss that a strategy has realized /// @param _strategy target strategy /// @param _loss amount of loss realized function _reportLoss(address _strategy, uint256 _loss) internal { StrategyParams storage strategy = strategies[_strategy]; // Loss can only be up the amount of debt issued to strategy if (strategy.totalDebt < _loss) revert Errors.StrategyLossTooHigh(); // Add loss to strategy and remove loss from strategyDebt strategy.totalLoss += _loss; strategy.totalDebt -= _loss; vaultTotalDebt -= _loss; } /// @notice Amount by which a strategy exceeds its current debt limit /// @param _strategy target strategy /// @return amount of debt the strategy has to pay back and the current debt ratio of the strategy function _excessDebt(address _strategy) internal view returns (uint256, uint256) { StrategyParams storage strategy = strategies[_strategy]; uint256 _debtRatio = strategy.debtRatio; uint256 strategyDebtLimit = (_debtRatio * _totalAssets()) / PERCENTAGE_DECIMAL_FACTOR; uint256 strategyTotalDebt = strategy.totalDebt; if (strategyTotalDebt <= strategyDebtLimit) { return (0, _debtRatio); } else { return (strategyTotalDebt - strategyDebtLimit, _debtRatio); } } function _calcFees(uint256 _gain) internal returns (uint256) { uint256 fees = (_gain * vaultFee) / PERCENTAGE_DECIMAL_FACTOR; if (fees > 0) { uint256 shares = convertToShares(fees); _mint(feeCollector, shares); } return _gain - fees; } /// @notice Update a given strategys debt ratio /// @param _strategy target strategy /// @param _debtRatio new debt ratio /// @dev See setDebtRatio functions function _setDebtRatio(address _strategy, uint256 _debtRatio) internal { uint256 _vaultDebtRatio = vaultDebtRatio - strategies[_strategy].debtRatio + _debtRatio; if (_vaultDebtRatio > PERCENTAGE_DECIMAL_FACTOR) revert Errors.VaultDebtRatioTooHigh(); strategies[_strategy].debtRatio = _debtRatio; vaultDebtRatio = _vaultDebtRatio; emit LogNewDebtRatio(_strategy, _debtRatio, _vaultDebtRatio); } /// @notice Get current estimated amount of assets in strategy /// @param _index index of strategy function _getStrategyEstimatedTotalAssets(uint256 _index) internal view returns (uint256) { return IStrategy(nodes[_index].strategy).estimatedTotalAssets(); } /// @notice Remove strategy from vault /// @param _strategy address of strategy function _revokeStrategy(address _strategy) internal { vaultDebtRatio -= strategies[_strategy].debtRatio; strategies[_strategy].debtRatio = 0; strategies[_strategy].active = false; } /// @notice Vault adapters total assets including loose assets and debts /// @dev note that this does not consider estimated gains/losses from the strategies function _totalAssets() private view returns (uint256) { return vaultAssets + vaultTotalDebt; } /// @notice Vault adapters total assets including loose assets and estimated returns /// @dev note that this does consider estimated gains/losses from the strategies function _estimatedTotalAssets() private view returns (uint256) { uint256 total = vaultAssets; uint256[MAXIMUM_STRATEGIES] memory _queue = fullWithdrawalQueue(); for (uint256 i = 0; i < noOfStrategies(); ++i) { total += _getStrategyEstimatedTotalAssets(_queue[i]); } return total; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a >= b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a / b + (a % b == 0 ? 0 : 1); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor() { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } }
// SPDX-License-Identifier: AGPLv3 pragma solidity 0.8.10; interface IStrategy { function asset() external view returns (address); function vault() external view returns (address); function isActive() external view returns (bool); function estimatedTotalAssets() external view returns (uint256); function withdraw(uint256 _amount) external returns (uint256, uint256); function canHarvest() external view returns (bool); function runHarvest() external; function canStopLoss() external view returns (bool); function stopLoss() external returns (bool); function getMetaPool() external view returns (address); }
// SPDX-License-Identifier: AGPLv3 pragma solidity 0.8.10; // ________ ________ ________ // |\ ____\|\ __ \|\ __ \ // \ \ \___|\ \ \|\ \ \ \|\ \ // \ \ \ __\ \ _ _\ \ \\\ \ // \ \ \|\ \ \ \\ \\ \ \\\ \ // \ \_______\ \__\\ _\\ \_______\ // \|_______|\|__|\|__|\|_______| // gro protocol: https://github.com/groLabs/GSquared /// @title StrategyQueue /// @notice StrategyQueue - logic for handling ordering of vault strategies /// --------- --------- --------- /// | Strat | | Strat | | Strat | /// | --- | | --- | | --- | /// 0<--| prev-|<---| prev-|<---| prev-| /// | next-|--->| next-|--->| next-|-->0 /// --------- --------- --------- /// Head Tail contract StrategyQueue { /*////////////////////////////////////////////////////////////// STORAGE VARIABLES & TYPES //////////////////////////////////////////////////////////////*/ // node in queue struct Strategy { uint48 next; uint48 prev; address strategy; } // Information regarding queue struct Queue { uint48 head; uint48 tail; uint48 totalNodes; uint48 nextAvailableNode; } /*////////////////////////////////////////////////////////////// CONSTANTS & IMMUTABLES //////////////////////////////////////////////////////////////*/ uint256 public constant MAXIMUM_STRATEGIES = 5; address internal constant ZERO_ADDRESS = address(0); uint48 internal constant EMPTY_NODE = 0; mapping(address => uint256) public strategyId; mapping(uint256 => Strategy) internal nodes; Queue internal strategyQueue; /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event LogStrategyRemoved(address indexed strategy, uint256 indexed id); event LogStrategyAdded( address indexed strategy, uint256 indexed id, uint256 pos ); event LogNewQueueLink(uint256 indexed id, uint256 next); event LogNewQueueHead(uint256 indexed id); event LogNewQueueTail(uint256 indexed id); /*////////////////////////////////////////////////////////////// ERRORS HANDLING //////////////////////////////////////////////////////////////*/ error NoIdEntry(uint256 id); error StrategyNotMoved(uint256 errorNo); // 1 - no move specified // 2 - strategy cant be moved further up/down the queue // 3 - strategy moved to its own position error NoStrategyEntry(address strategy); error StrategyExists(address strategy); error MaxStrategyExceeded(); /*////////////////////////////////////////////////////////////// GETTERS //////////////////////////////////////////////////////////////*/ /// @notice Get strategy at position i of withdrawal queue /// @param i position in withdrawal queue /// @return strategy strategy at position i function withdrawalQueueAt(uint256 i) external view returns (address strategy) { if (i == 0 || i == strategyQueue.totalNodes - 1) { strategy = i == 0 ? nodes[strategyQueue.head].strategy : nodes[strategyQueue.tail].strategy; } else { uint256 index = strategyQueue.head; for (uint256 j; j <= i; j++) { if (j == i) return nodes[index].strategy; index = nodes[index].next; } } } /// @notice Get the entire withdrawal queue /// @return queue list of all strategy ids in order of withdrawal priority function fullWithdrawalQueue() internal view returns (uint256[MAXIMUM_STRATEGIES] memory queue) { uint256 index = strategyQueue.head; uint256 _totalNodes = strategyQueue.totalNodes; queue[0] = index; for (uint256 i = 1; i < _totalNodes; ++i) { index = nodes[index].next; queue[i] = index; } } /// @notice Get position of strategy in withdrawal queue /// @param _strategy address of strategy /// @return returns position of strategy in withdrawal queue function getStrategyPositions(address _strategy) public view returns (uint256) { uint48 index = strategyQueue.head; uint48 _totalNodes = strategyQueue.totalNodes; for (uint48 i = 0; i <= _totalNodes; ++i) { if (_strategy == nodes[index].strategy) { return i; } index = nodes[index].next; } revert NoStrategyEntry(_strategy); } /*////////////////////////////////////////////////////////////// QUEUE LOGIC //////////////////////////////////////////////////////////////*/ /// @notice Add a strategy to the end of the queue /// @param _strategy address of strategy to add /// @dev creates a new node which is inserted at the end of the /// strategy queue. the strategy is assigned an id and is /// linked to the previous tail. Note that this ID isnt /// necessarily the same as the position in the withdrawal queue function _push(address _strategy) internal returns (uint256) { if (strategyId[_strategy] > 0) revert StrategyExists(_strategy); uint48 nodeId = _createNode(_strategy); return uint256(nodeId); } /// @notice Remove strategy from queue /// @param _strategy strategy to remove /// @dev removes a node and links the nodes neighbours function _pop(address _strategy) internal { uint256 id = strategyId[_strategy]; if (id == 0) revert NoStrategyEntry(_strategy); Strategy storage removeNode = nodes[uint48(id)]; address strategy = removeNode.strategy; if (strategy == ZERO_ADDRESS) revert NoIdEntry(id); _link(removeNode.prev, removeNode.next); strategyId[_strategy] = 0; emit LogStrategyRemoved(strategy, id); delete nodes[uint48(id)]; strategyQueue.totalNodes -= 1; } /// @notice move a strategy to a new position in the queue /// @param _id id of strategy to move /// @param _steps number of steps to move the strategy /// @param _back move towards tail (true) or head (false) /// @dev Moves a strategy a given number of steps. If the number /// of steps exceeds the position of the head/tail, the /// strategy will take the place of the current head/tail function move( uint48 _id, uint48 _steps, bool _back ) internal { Strategy storage oldPos = nodes[_id]; if (_steps == 0) revert StrategyNotMoved(1); if (oldPos.strategy == ZERO_ADDRESS) revert NoIdEntry(_id); uint48 _newPos = !_back ? oldPos.prev : oldPos.next; if (_newPos == 0) revert StrategyNotMoved(2); for (uint256 i = 1; i < _steps; ++i) { _newPos = !_back ? nodes[_newPos].prev : nodes[_newPos].next; if (_newPos == 0) { _newPos = !_back ? strategyQueue.head : strategyQueue.tail; break; } } if (_newPos == _id) revert StrategyNotMoved(3); Strategy memory newPos = nodes[_newPos]; _link(oldPos.prev, oldPos.next); if (!_back) { _link(newPos.prev, _id); _link(_id, _newPos); } else { _link(_id, newPos.next); _link(_newPos, _id); } } /// @notice Create a new node to be inserted at the tail of the queue /// @param _strategy address of strategy to add /// @return id of strategy function _createNode(address _strategy) internal returns (uint48) { uint48 _totalNodes = strategyQueue.totalNodes; if (_totalNodes >= MAXIMUM_STRATEGIES) revert MaxStrategyExceeded(); strategyQueue.nextAvailableNode += 1; strategyQueue.totalNodes = _totalNodes + 1; uint48 newId = uint48(strategyQueue.nextAvailableNode); strategyId[_strategy] = newId; uint48 _tail = strategyQueue.tail; Strategy memory node = Strategy(EMPTY_NODE, _tail, _strategy); _link(_tail, newId); _setTail(newId); nodes[newId] = node; emit LogStrategyAdded(_strategy, newId, _totalNodes + 1); return newId; } /// @notice Set the head of the queue /// @param _id Id of the strategy to set the head to function _setHead(uint256 _id) internal { strategyQueue.head = uint48(_id); emit LogNewQueueHead(_id); } /// @notice Set the tail of the queue /// @param _id Id of the strategy to set the tail to function _setTail(uint256 _id) internal { strategyQueue.tail = uint48(_id); emit LogNewQueueTail(_id); } /// @notice Link two nodes /// @param _prevId id of previous node /// @param _nextId id of next node function _link(uint48 _prevId, uint48 _nextId) internal { if (_prevId == EMPTY_NODE) { _setHead(_nextId); } else { nodes[_prevId].next = _nextId; } if (_nextId == EMPTY_NODE) { _setTail(_prevId); } else { nodes[_nextId].prev = _prevId; } emit LogNewQueueLink(_prevId, _nextId); } }
// SPDX-License-Identifier: AGPLv3 pragma solidity 0.8.10; import "GToken.sol"; /// @notice Rebasing token implementation of the GToken. /// This contract defines the PWRD Stablecoin (pwrd) - A yield bearing stable coin used in /// Gro protocol. The Rebasing token does not rebase in discrete events by minting new tokens, /// but rather relies on the GToken factor to establish the amount of tokens in circulation, /// in a continuous manner. The token supply is defined as: /// BASE (10**18) / factor (total supply / total assets) /// where the total supply is the number of minted tokens, and the total assets /// is the USD value of the underlying assets used to mint the token. /// For simplicity the underlying amount of tokens will be referred to as base, while /// the rebased amount (base/factor) will be referred to as rebase. contract SeniorTranche is GToken { using SafeERC20 for IERC20; using SafeMath for uint256; event LogTransfer( address indexed sender, address indexed recipient, uint256 indexed amount ); constructor(string memory name, string memory symbol) GToken(name, symbol) {} /// @notice TotalSupply override - the totalsupply of the Rebasing token is /// calculated by dividing the totalSupplyBase (standard ERC20 totalSupply) /// by the factor. This result is the rebased amount function totalSupply() public view override returns (uint256) { uint256 f = factor(); return f > 0 ? applyFactor(totalSupplyBase(), f, false) : 0; } function balanceOf(address account) public view override returns (uint256) { uint256 f = factor(); return f > 0 ? applyFactor(balanceOfBase(account), f, false) : 0; } /// @notice Transfer override - Overrides the transfer method to transfer /// the correct underlying base amount of tokens, but emit the rebased amount /// @param recipient Recipient of transfer /// @param amount Base amount to transfer function transfer(address recipient, uint256 amount) public override returns (bool) { uint256 transferAmount = applyFactor(amount, factor(), true); super._transfer(msg.sender, recipient, transferAmount, amount); emit LogTransfer(msg.sender, recipient, amount); return true; } /// @notice Price should always be 1E18 function getPricePerShare() external view override returns (uint256) { return BASE; } function getShareAssets(uint256 shares) external view override returns (uint256) { return shares; } function getAssets(address account) external view override returns (uint256) { return balanceOf(account); } /// @notice Mint RebasingGTokens /// @param account Target account /// @param _factor Factor to use for mint /// @param amount Mint amount in USD function mint( address account, uint256 _factor, uint256 amount ) external override onlyWhitelist { require(account != address(0), "mint: 0x"); require(amount > 0, "Amount is zero."); // Apply factor to amount to get rebase amount uint256 mintAmount = applyFactor(amount, _factor, true); // uint256 mintAmount = amount.mul(_factor).div(BASE); _mint(account, mintAmount, amount); } /// @notice Burn RebasingGTokens /// @param account Target account /// @param _factor Factor to use for mint /// @param amount Burn amount in USD function burn( address account, uint256 _factor, uint256 amount ) external override onlyWhitelist { require(account != address(0), "burn: 0x"); require(amount > 0, "Amount is zero."); // Apply factor to amount to get rebase amount uint256 burnAmount = applyFactor(amount, _factor, true); // uint256 burnAmount = amount.mul(_factor).div(BASE); _burn(account, burnAmount, amount); } /// @notice Burn all pwrds for account - used by withdraw all methods /// @param account Target account function burnAll(address account) external override onlyWhitelist { require(account != address(0), "burnAll: 0x"); uint256 burnAmount = balanceOfBase(account); uint256 amount = applyFactor(burnAmount, factor(), false); // uint256 amount = burnAmount.mul(BASE).div(factor()); // Apply factor to amount to get rebase amount _burn(account, burnAmount, amount); } /// @notice transferFrom override - Overrides the transferFrom method /// to transfer the correct amount of underlying tokens (Base amount) /// but emit the rebased amount /// @param sender Sender of transfer /// @param recipient Reciepient of transfer /// @param amount Mint amount in USD function transferFrom( address sender, address recipient, uint256 amount ) public virtual override returns (bool) { super._decreaseApproved(sender, msg.sender, amount); uint256 transferAmount = applyFactor(amount, factor(), true); // amount.mul(factor()).div(BASE) super._transfer(sender, recipient, transferAmount, amount); return true; } }
// SPDX-License-Identifier: AGPLv3 pragma solidity 0.8.10; import "Context.sol"; import "Address.sol"; import "IERC20.sol"; import "Ownable.sol"; import "SafeERC20.sol"; import "SafeMath.sol"; import "Constants.sol"; import "Whitelist.sol"; import "IERC20Detailed.sol"; abstract contract GERC20 is Context, IERC20 { using Address for address; using SafeMath for uint256; mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; uint8 private _decimals; constructor( string memory name_, string memory symbol_, uint8 decimals_ ) { _name = name_; _symbol = symbol_; _decimals = decimals_; } /** * @dev Returns the name of the token. */ function name() public view returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5,05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is * called. * * 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 returns (uint8) { return _decimals; } /** * @dev See {IERC20-totalSupply}. */ function totalSupplyBase() public view returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOfBase(address account) public view returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}; * * Requirements: * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom( address sender, address recipient, uint256 amount ) public virtual override returns (bool) { _transfer(sender, recipient, amount, amount); _approve( sender, _msgSender(), _allowances[sender][_msgSender()] - amount ); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve( _msgSender(), spender, _allowances[_msgSender()][spender] + addedValue ); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve( _msgSender(), spender, _allowances[_msgSender()][spender] - subtractedValue ); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * GERC20 addition - transferAmount added to take rebased amount into account * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer( address sender, address recipient, uint256 transferAmount, uint256 amount ) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, transferAmount); _balances[sender] = _balances[sender].sub( transferAmount, "ERC20: transfer amount exceeds balance" ); _balances[recipient] = _balances[recipient].add(transferAmount); emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * GERC20 addition - mintAmount added to take rebased amount into account * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements * * - `to` cannot be the zero address. */ function _mint( address account, uint256 mintAmount, uint256 amount ) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, mintAmount); _totalSupply = _totalSupply.add(mintAmount); _balances[account] = _balances[account].add(mintAmount); emit Transfer(address(0), account, amount); } event LogTestGToken(uint256 _burnAmount, uint256 _balance); /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * GERC20 addition - burnAmount added to take rebased amount into account * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn( address account, uint256 burnAmount, uint256 amount ) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), burnAmount); emit LogTestGToken(burnAmount, _balances[account]); _balances[account] = _balances[account].sub( burnAmount, "ERC20: burn amount exceeds balance" ); _totalSupply = _totalSupply.sub(burnAmount); emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens. * * This is internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve( address owner, address spender, uint256 amount ) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } function _decreaseApproved( address owner, address spender, uint256 amount ) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = _allowances[owner][spender] - (amount); emit Approval(owner, spender, _allowances[owner][spender]); } /** * @dev Sets {decimals} to a value other than the default one of 18. * * WARNING: This function should only be called from the constructor. Most * applications that interact with token contracts will not expect * {decimals} to ever change, and may work incorrectly if it does. */ function _setupDecimals(uint8 decimals_) internal { _decimals = decimals_; } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual {} } interface IController { function stablecoins() external view returns (address[3] memory); function vaults() external view returns (address[3] memory); function underlyingVaults(uint256 i) external view returns (address vault); function curveVault() external view returns (address); function pnl() external view returns (address); function insurance() external view returns (address); function lifeGuard() external view returns (address); function buoy() external view returns (address); function reward() external view returns (address); function isValidBigFish( bool pwrd, bool deposit, uint256 amount ) external view returns (bool); function withdrawHandler() external view returns (address); function emergencyHandler() external view returns (address); function depositHandler() external view returns (address); function totalAssets() external view returns (uint256); function gTokenTotalAssets() external view returns (uint256); function eoaOnly(address sender) external; function getSkimPercent() external view returns (uint256); function gToken(bool _pwrd) external view returns (address); function emergencyState() external view returns (bool); function deadCoin() external view returns (uint256); function distributeStrategyGainLoss(uint256 gain, uint256 loss) external; function burnGToken( bool pwrd, bool all, address account, uint256 amount, uint256 bonus ) external; function mintGToken( bool pwrd, address account, uint256 amount ) external; function getUserAssets(bool pwrd, address account) external view returns (uint256 deductUsd); function referrals(address account) external view returns (address); function addReferral(address account, address referral) external; function getStrategiesTargetRatio() external view returns (uint256[] memory); function withdrawalFee(bool pwrd) external view returns (uint256); function validGTokenDecrease(uint256 amount) external view returns (bool); } interface IToken { function factor() external view returns (uint256); function factor(uint256 totalAssets) external view returns (uint256); function mint( address account, uint256 _factor, uint256 amount ) external; function burn( address account, uint256 _factor, uint256 amount ) external; function burnAll(address account) external; function totalAssets() external view returns (uint256); function getPricePerShare() external view returns (uint256); function getShareAssets(uint256 shares) external view returns (uint256); function getAssets(address account) external view returns (uint256); } /// @notice Base contract for gro protocol tokens - The Gro token specifies some additional functionality /// shared by both tokens (Rebasing, NonRebasing). /// - Factor: /// The GToken factor. The two tokens are associated with a factor that controls their price (NonRebasing), /// or their amount (Rebasing). The factor is defined by the totalSupply / total assets lock in token. /// - Base: /// The base amount of minted tokens, this affects the Rebasing token as the totalSupply is defined by: /// BASE amount / factor /// - Total assets: /// Total assets is the dollarvalue of the underlying assets used to mint Gtokens. The Gtoken /// depends on an external contract (Controller.sol) to get this value (retrieved from PnL calculations) abstract contract GToken is GERC20, Constants, Whitelist, IToken { uint256 public constant BASE = DEFAULT_DECIMALS_FACTOR; using SafeERC20 for IERC20; using SafeMath for uint256; IController public ctrl; constructor(string memory name, string memory symbol) GERC20(name, symbol, DEFAULT_DECIMALS) {} function setController(address controller) external onlyOwner { ctrl = IController(controller); } function factor() public view override returns (uint256) { return factor(totalAssets()); } function applyFactor( uint256 a, uint256 b, bool base ) internal pure returns (uint256 resultant) { uint256 _BASE = BASE; uint256 diff; if (base) { diff = a.mul(b) % _BASE; resultant = a.mul(b).div(_BASE); } else { diff = a.mul(_BASE) % b; resultant = a.mul(_BASE).div(b); } if (diff >= 5E17) { resultant = resultant.add(1); } } function factor(uint256 _totalAssets) public view override returns (uint256) { if (totalSupplyBase() == 0) { return getInitialBase(); } if (_totalAssets > 0) { return totalSupplyBase().mul(BASE).div(_totalAssets); } // This case is totalSupply > 0 && totalAssets == 0, and only occurs on system loss return 0; } function totalAssets() public view override returns (uint256) { return ctrl.gTokenTotalAssets(); } function getInitialBase() internal pure virtual returns (uint256) { return BASE; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); /** * @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); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "IERC20.sol"; import "Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 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 { using Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } /** * @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). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol) pragma solidity ^0.8.0; // CAUTION // This version of SafeMath should only be used with Solidity 0.8 or later, // because it relies on the compiler's built in overflow checks. /** * @dev Wrappers over Solidity's arithmetic operations. * * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler * now has built in overflow checking. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } } /** * @dev Returns the substraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b > a) return (false, 0); return (true, a - b); } } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a / b); } } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a % b); } } /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { return a + b; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return a - b; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { return a * b; } /** * @dev Returns the integer division of two unsigned integers, reverting on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b <= a, errorMessage); return a - b; } } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b > 0, errorMessage); return a / b; } } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b > 0, errorMessage); return a % b; } } }
// SPDX-License-Identifier: AGPLv3 pragma solidity 0.8.10; import {Ownable} from "Ownable.sol"; import {Errors} from "Errors.sol"; contract Whitelist is Ownable { mapping(address => bool) public whitelist; event LogAddToWhitelist(address indexed user); event LogRemoveFromWhitelist(address indexed user); modifier onlyWhitelist() { if (!whitelist[msg.sender]) { revert Errors.NotInWhitelist(); } _; } function addToWhitelist(address user) external onlyOwner { if (user == address(0)) { revert Errors.ZeroAddress(); } whitelist[user] = true; emit LogAddToWhitelist(user); } function removeFromWhitelist(address user) external onlyOwner { if (user == address(0)) { revert Errors.ZeroAddress(); } whitelist[user] = false; emit LogRemoveFromWhitelist(user); } }
// SPDX-License-Identifier: AGPLv3 pragma solidity 0.8.10; interface IERC20Detailed { function name() external view returns (string memory); function symbol() external view returns (string memory); function decimals() external view returns (uint8); }
{ "evmVersion": "istanbul", "optimizer": { "enabled": true, "runs": 200 }, "libraries": { "GMigration.sol": {} }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"contract GVault","name":"_gVault","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InsufficientShares","type":"error"},{"inputs":[],"name":"TrancheAlreadySet","type":"error"},{"inputs":[],"name":"TrancheNotSet","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"CHAINLINK_PRICE_DECIMALS","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CHAINLINK_PRICE_DECIMAL_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CURVE_RATIO_DECIMALS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CURVE_RATIO_DECIMALS_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_DECIMALS","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_DECIMALS_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"N_COINS","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERCENTAGE_DECIMALS","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERCENTAGE_DECIMAL_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gTranche","outputs":[{"internalType":"contract GTranche","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"minAmountThreeCRV","type":"uint256"},{"internalType":"uint256","name":"minAmountShares","type":"uint256"}],"name":"prepareMigration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"seniorTrancheDollarAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract GTranche","name":"_gTranche","type":"address"}],"name":"setGTranche","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60a060405234801561001057600080fd5b50604051610ccb380380610ccb83398101604081905261002f91610099565b61003833610049565b6001600160a01b03166080526100c9565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000602082840312156100ab57600080fd5b81516001600160a01b03811681146100c257600080fd5b9392505050565b608051610bd96100f260003960008181610682015281816106c6015261076f0152610bd96000f3fe608060405234801561001057600080fd5b50600436106101005760003560e01c80639070642511610097578063e963f18f11610066578063e963f18f146101ce578063ea0d5c52146101d6578063eb3f506a146101de578063f2fde38b146101e757600080fd5b806390706425146101ae578063ae70b98a146101b6578063c5008f46146101be578063e0501ecf146101c657600080fd5b80633e5f6bbc116100d35780633e5f6bbc1461017a5780636f923e731461018d578063715018a6146101955780638da5cb5b1461019d57600080fd5b806312b06cfc146101055780631e1e49191461011a578063250108f61461014a5780632935775014610160575b600080fd5b6101186101133660046109c0565b6101fa565b005b60015461012d906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b61015261028e565b604051908152602001610141565b610168600381565b60405160ff9091168152602001610141565b6101186101883660046109e4565b61029d565b610168600881565b6101186107a1565b6000546001600160a01b031661012d565b610168600481565b6101526107d7565b610168601281565b610152600681565b6101526107e3565b6101526107ef565b61015260025481565b6101186101f53660046109c0565b6107fb565b6000546001600160a01b0316331461022d5760405162461bcd60e51b815260040161022490610a06565b60405180910390fd5b600054600160a01b900460ff161561025857604051637467391160e11b815260040160405180910390fd5b600180546001600160a01b039092166001600160a01b03199092169190911790556000805460ff60a01b1916600160a01b179055565b61029a6006600a610b37565b81565b6000546001600160a01b031633146102c75760405162461bcd60e51b815260040161022490610a06565b600054600160a01b900460ff166102f1576040516363c4b67960e11b815260040160405180910390fd5b73f0a93d4994b3d98fb5e3a2f90dbc2d69073cb86b6001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa158015610343573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103679190610b43565b6002556040516370a0823160e01b8152306004820152600090736b175474e89094c44da98b954eedeac495271d0f906370a0823190602401602060405180830381865afa1580156103bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103e09190610b43565b6040516370a0823160e01b815230600482015290915060009073a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48906370a0823190602401602060405180830381865afa158015610435573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104599190610b43565b6040516370a0823160e01b815230600482015290915060009073dac17f958d2ee523a2206206994597c13d831ec7906370a0823190602401602060405180830381865afa1580156104ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104d29190610b43565b9050610507736b175474e89094c44da98b954eedeac495271d0f73bebc44782c7db0a1a60cb6fe97d0b483032ff1c785610896565b61053a73a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4873bebc44782c7db0a1a60cb6fe97d0b483032ff1c784610896565b61056d73dac17f958d2ee523a2206206994597c13d831ec773bebc44782c7db0a1a60cb6fe97d0b483032ff1c783610896565b60408051606081018252848152602081018490528082018390529051634515cef360e01b815273bebc44782c7db0a1a60cb6fe97d0b483032ff1c791634515cef3916105be91908990600401610b5c565b600060405180830381600087803b1580156105d857600080fd5b505af11580156105ec573d6000803e3d6000fd5b50506040516370a0823160e01b815230600482015260009250736c3f90f043a72fa612cbac8115ee7e52bde6e49091506370a0823190602401602060405180830381865afa158015610642573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106669190610b43565b90506106a7736c3f90f043a72fa612cbac8115ee7e52bde6e4907f000000000000000000000000000000000000000000000000000000000000000083610896565b604051636e553f6560e01b8152600481018290523060248201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690636e553f65906044016020604051808303816000875af1158015610717573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061073b9190610b43565b90508581101561075e57604051633999656760e01b815260040160405180910390fd5b600154610798906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116911683610896565b50505050505050565b6000546001600160a01b031633146107cb5760405162461bcd60e51b815260040161022490610a06565b6107d56000610914565b565b61029a6004600a610b94565b61029a6008600a610b94565b61029a6012600a610b94565b6000546001600160a01b031633146108255760405162461bcd60e51b815260040161022490610a06565b6001600160a01b03811661088a5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610224565b61089381610914565b50565b600060405163095ea7b360e01b81526001600160a01b03841660048201528260248201526000806044836000895af19150506108d181610964565b61090e5760405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b6044820152606401610224565b50505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60003d8261097657806000803e806000fd5b806020811461098e57801561099f57600092506109a4565b816000803e600051151592506109a4565b600192505b5050919050565b6001600160a01b038116811461089357600080fd5b6000602082840312156109d257600080fd5b81356109dd816109ab565b9392505050565b600080604083850312156109f757600080fd5b50508035926020909101359150565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b634e487b7160e01b600052601160045260246000fd5b600181815b80851115610a8c578160001904821115610a7257610a72610a3b565b80851615610a7f57918102915b93841c9390800290610a56565b509250929050565b600082610aa357506001610b31565b81610ab057506000610b31565b8160018114610ac65760028114610ad057610aec565b6001915050610b31565b60ff841115610ae157610ae1610a3b565b50506001821b610b31565b5060208310610133831016604e8410600b8410161715610b0f575081810a610b31565b610b198383610a51565b8060001904821115610b2d57610b2d610a3b565b0290505b92915050565b60006109dd8383610a94565b600060208284031215610b5557600080fd5b5051919050565b60808101818460005b6003811015610b84578151835260209283019290910190600101610b65565b5050508260608301529392505050565b60006109dd60ff841683610a9456fea2646970667358221220eeb21b2ffdd515aa65b229625373f271b5505d26300f555b4c02f8673952a7bb64736f6c634300080a00330000000000000000000000001402c1caa002354fc2c4a4cd2b4045a5b9625ef3
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101005760003560e01c80639070642511610097578063e963f18f11610066578063e963f18f146101ce578063ea0d5c52146101d6578063eb3f506a146101de578063f2fde38b146101e757600080fd5b806390706425146101ae578063ae70b98a146101b6578063c5008f46146101be578063e0501ecf146101c657600080fd5b80633e5f6bbc116100d35780633e5f6bbc1461017a5780636f923e731461018d578063715018a6146101955780638da5cb5b1461019d57600080fd5b806312b06cfc146101055780631e1e49191461011a578063250108f61461014a5780632935775014610160575b600080fd5b6101186101133660046109c0565b6101fa565b005b60015461012d906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b61015261028e565b604051908152602001610141565b610168600381565b60405160ff9091168152602001610141565b6101186101883660046109e4565b61029d565b610168600881565b6101186107a1565b6000546001600160a01b031661012d565b610168600481565b6101526107d7565b610168601281565b610152600681565b6101526107e3565b6101526107ef565b61015260025481565b6101186101f53660046109c0565b6107fb565b6000546001600160a01b0316331461022d5760405162461bcd60e51b815260040161022490610a06565b60405180910390fd5b600054600160a01b900460ff161561025857604051637467391160e11b815260040160405180910390fd5b600180546001600160a01b039092166001600160a01b03199092169190911790556000805460ff60a01b1916600160a01b179055565b61029a6006600a610b37565b81565b6000546001600160a01b031633146102c75760405162461bcd60e51b815260040161022490610a06565b600054600160a01b900460ff166102f1576040516363c4b67960e11b815260040160405180910390fd5b73f0a93d4994b3d98fb5e3a2f90dbc2d69073cb86b6001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa158015610343573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103679190610b43565b6002556040516370a0823160e01b8152306004820152600090736b175474e89094c44da98b954eedeac495271d0f906370a0823190602401602060405180830381865afa1580156103bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103e09190610b43565b6040516370a0823160e01b815230600482015290915060009073a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48906370a0823190602401602060405180830381865afa158015610435573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104599190610b43565b6040516370a0823160e01b815230600482015290915060009073dac17f958d2ee523a2206206994597c13d831ec7906370a0823190602401602060405180830381865afa1580156104ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104d29190610b43565b9050610507736b175474e89094c44da98b954eedeac495271d0f73bebc44782c7db0a1a60cb6fe97d0b483032ff1c785610896565b61053a73a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4873bebc44782c7db0a1a60cb6fe97d0b483032ff1c784610896565b61056d73dac17f958d2ee523a2206206994597c13d831ec773bebc44782c7db0a1a60cb6fe97d0b483032ff1c783610896565b60408051606081018252848152602081018490528082018390529051634515cef360e01b815273bebc44782c7db0a1a60cb6fe97d0b483032ff1c791634515cef3916105be91908990600401610b5c565b600060405180830381600087803b1580156105d857600080fd5b505af11580156105ec573d6000803e3d6000fd5b50506040516370a0823160e01b815230600482015260009250736c3f90f043a72fa612cbac8115ee7e52bde6e49091506370a0823190602401602060405180830381865afa158015610642573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106669190610b43565b90506106a7736c3f90f043a72fa612cbac8115ee7e52bde6e4907f0000000000000000000000001402c1caa002354fc2c4a4cd2b4045a5b9625ef383610896565b604051636e553f6560e01b8152600481018290523060248201526000907f0000000000000000000000001402c1caa002354fc2c4a4cd2b4045a5b9625ef36001600160a01b031690636e553f65906044016020604051808303816000875af1158015610717573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061073b9190610b43565b90508581101561075e57604051633999656760e01b815260040160405180910390fd5b600154610798906001600160a01b037f0000000000000000000000001402c1caa002354fc2c4a4cd2b4045a5b9625ef38116911683610896565b50505050505050565b6000546001600160a01b031633146107cb5760405162461bcd60e51b815260040161022490610a06565b6107d56000610914565b565b61029a6004600a610b94565b61029a6008600a610b94565b61029a6012600a610b94565b6000546001600160a01b031633146108255760405162461bcd60e51b815260040161022490610a06565b6001600160a01b03811661088a5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610224565b61089381610914565b50565b600060405163095ea7b360e01b81526001600160a01b03841660048201528260248201526000806044836000895af19150506108d181610964565b61090e5760405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b6044820152606401610224565b50505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60003d8261097657806000803e806000fd5b806020811461098e57801561099f57600092506109a4565b816000803e600051151592506109a4565b600192505b5050919050565b6001600160a01b038116811461089357600080fd5b6000602082840312156109d257600080fd5b81356109dd816109ab565b9392505050565b600080604083850312156109f757600080fd5b50508035926020909101359150565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b634e487b7160e01b600052601160045260246000fd5b600181815b80851115610a8c578160001904821115610a7257610a72610a3b565b80851615610a7f57918102915b93841c9390800290610a56565b509250929050565b600082610aa357506001610b31565b81610ab057506000610b31565b8160018114610ac65760028114610ad057610aec565b6001915050610b31565b60ff841115610ae157610ae1610a3b565b50506001821b610b31565b5060208310610133831016604e8410600b8410161715610b0f575081810a610b31565b610b198383610a51565b8060001904821115610b2d57610b2d610a3b565b0290505b92915050565b60006109dd8383610a94565b600060208284031215610b5557600080fd5b5051919050565b60808101818460005b6003811015610b84578151835260209283019290910190600101610b65565b5050508260608301529392505050565b60006109dd60ff841683610a9456fea2646970667358221220eeb21b2ffdd515aa65b229625373f271b5505d26300f555b4c02f8673952a7bb64736f6c634300080a0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000001402c1caa002354fc2c4a4cd2b4045a5b9625ef3
-----Decoded View---------------
Arg [0] : _gVault (address): 0x1402c1cAa002354fC2C4a4cD2b4045A5b9625EF3
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000001402c1caa002354fc2c4a4cd2b4045a5b9625ef3
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.