diff --git a/.prettierrc b/.prettierrc index 06347021..dc3aa68e 100644 --- a/.prettierrc +++ b/.prettierrc @@ -10,6 +10,12 @@ "bracketSpacing": true, "explicitTypes": "always" } + }, + { + "files": "test/**/*.ts", + "options": { + "printWidth": 80 + } } ], "printWidth": 120, diff --git a/contracts/Interfaces.sol b/contracts/Interfaces.sol index cd166521..41b7a288 100644 --- a/contracts/Interfaces.sol +++ b/contracts/Interfaces.sol @@ -520,3 +520,125 @@ interface IMultichainRouter { uint256 toChainID ) external; } + +// @uniswap/v3-core +interface ISwapRouter { + struct ExactInputSingleParams { + address tokenIn; + address tokenOut; + uint24 fee; + address recipient; + uint256 amountIn; + uint256 amountOutMinimum; + uint160 sqrtPriceLimitX96; + } + + /// @notice Swaps `amountIn` of one token for as much as possible of another token + /// @dev Setting `amountIn` to 0 will cause the contract to look up its own balance, + /// and swap the entire amount, enabling contracts to send tokens before calling this function. + /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata + /// @return amountOut The amount of the received token + function exactInputSingle( + ExactInputSingleParams calldata params + ) external payable returns (uint256 amountOut); + + struct ExactInputParams { + bytes path; + address recipient; + uint256 amountIn; + uint256 amountOutMinimum; + } + + /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path + /// @dev Setting `amountIn` to 0 will cause the contract to look up its own balance, + /// and swap the entire amount, enabling contracts to send tokens before calling this function. + /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata + /// @return amountOut The amount of the received token + function exactInput( + ExactInputParams calldata params + ) external payable returns (uint256 amountOut); + + struct ExactOutputSingleParams { + address tokenIn; + address tokenOut; + uint24 fee; + address recipient; + uint256 amountOut; + uint256 amountInMaximum; + uint160 sqrtPriceLimitX96; + } + + /// @notice Swaps as little as possible of one token for `amountOut` of another token + /// that may remain in the router after the swap. + /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata + /// @return amountIn The amount of the input token + function exactOutputSingle( + ExactOutputSingleParams calldata params + ) external payable returns (uint256 amountIn); + + struct ExactOutputParams { + bytes path; + address recipient; + uint256 amountOut; + uint256 amountInMaximum; + } + + /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed) + /// that may remain in the router after the swap. + /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata + /// @return amountIn The amount of the input token + function exactOutput( + ExactOutputParams calldata params + ) external payable returns (uint256 amountIn); +} + +interface IQuoterV2 { + /// @notice Returns the amount out received for a given exact input swap without executing the swap + /// @param path The path of the swap, i.e. each token pair and the pool fee + /// @param amountIn The amount of the first token to swap + /// @return amountOut The amount of the last token that would be received + /// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path + /// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for each pool in the path + /// @return gasEstimate The estimate of the gas that the swap consumes + function quoteExactInput( + bytes memory path, + uint256 amountIn + ) + external + returns ( + uint256 amountOut, + uint160[] memory sqrtPriceX96AfterList, + uint32[] memory initializedTicksCrossedList, + uint256 gasEstimate + ); + + struct QuoteExactInputSingleParams { + address tokenIn; + address tokenOut; + uint256 amountIn; + uint24 fee; + uint160 sqrtPriceLimitX96; + } + + /// @notice Returns the amount out received for a given exact input but for a swap of a single pool + /// @param params The params for the quote, encoded as `QuoteExactInputSingleParams` + /// tokenIn The token being swapped in + /// tokenOut The token being swapped out + /// fee The fee of the token pool to consider for the pair + /// amountIn The desired input amount + /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap + /// @return amountOut The amount of `tokenOut` that would be received + /// @return sqrtPriceX96After The sqrt price of the pool after the swap + /// @return initializedTicksCrossed The number of initialized ticks that the swap crossed + /// @return gasEstimate The estimate of the gas that the swap consumes + function quoteExactInputSingle( + QuoteExactInputSingleParams memory params + ) + external + returns ( + uint256 amountOut, + uint160 sqrtPriceX96After, + uint32 initializedTicksCrossed, + uint256 gasEstimate + ); +} diff --git a/contracts/governance/CompoundVotingMachine.sol b/contracts/governance/CompoundVotingMachine.sol index fa06db92..8666ea49 100644 --- a/contracts/governance/CompoundVotingMachine.sol +++ b/contracts/governance/CompoundVotingMachine.sol @@ -33,11 +33,9 @@ contract CompoundVotingMachine is ContextUpgradeable, DAOUpgradeableContract { /// @notice The number of votes required in order for a voter to become a proposer uint256 public proposalPercentage; - function proposalThreshold(uint256 blockNumber) - public - view - returns (uint256) - { + function proposalThreshold( + uint256 blockNumber + ) public view returns (uint256) { return (rep.totalSupplyAt(blockNumber) * proposalPercentage) / 1000000; //0.25% } @@ -102,6 +100,8 @@ contract CompoundVotingMachine is ContextUpgradeable, DAOUpgradeableContract { uint256 quoromRequired; // support proposal voting bridge uint256 forBlockchain; + // flag to mark proposal was approved as not malicious by guardians + bool guardianApproved; } /// @notice Ballot receipt record for a voter @@ -426,6 +426,11 @@ contract CompoundVotingMachine is ContextUpgradeable, DAOUpgradeableContract { "CompoundVotingMachine::execute: proposal for wrong blockchain" ); + require( + address(guardian) == address(0) || proposals[proposalId].guardianApproved, + "CompoundVotingMachine: proposal not approved" + ); + proposals[proposalId].executed = true; address[] memory _targets = proposals[proposalId].targets; uint256[] memory _values = proposals[proposalId].values; @@ -501,7 +506,9 @@ contract CompoundVotingMachine is ContextUpgradeable, DAOUpgradeableContract { } /// @notice get the actions to be done in a proposal - function getActions(uint256 proposalId) + function getActions( + uint256 proposalId + ) public view returns ( @@ -516,11 +523,10 @@ contract CompoundVotingMachine is ContextUpgradeable, DAOUpgradeableContract { } /// @notice get the receipt of a single voter in a proposal - function getReceipt(uint256 proposalId, address voter) - public - view - returns (Receipt memory) - { + function getReceipt( + uint256 proposalId, + address voter + ) public view returns (Receipt memory) { return proposals[proposalId].receipts[voter]; } @@ -770,6 +776,15 @@ contract CompoundVotingMachine is ContextUpgradeable, DAOUpgradeableContract { emit GuardianSet(guardian); } + function approveProposal(uint256 _proposalId) public { + require( + _msgSender() == address(avatar) || _msgSender() == guardian, + "CompoundVotingMachine: not avatar or guardian" + ); + + proposals[_proposalId].guardianApproved = true; + } + /// @notice allow anyone to emit details about proposal that passed. can be used for cross-chain proposals using blockheader proofs function emitSucceeded(uint256 _proposalId) public { require( diff --git a/contracts/governance/StakersDistribution.sol b/contracts/governance/StakersDistribution.sol index 2b33de5d..718ac967 100644 --- a/contracts/governance/StakersDistribution.sol +++ b/contracts/governance/StakersDistribution.sol @@ -52,6 +52,7 @@ contract StakersDistribution is ) external { _onlyAvatar(); monthlyReputationDistribution = newMonthlyReputationDistribution; + _updateRewards(); } /** @@ -174,9 +175,10 @@ contract StakersDistribution is _claimReputation(_staker, _stakingContracts); } - function _claimReputation(address _staker, address[] memory _stakingContracts) - internal - { + function _claimReputation( + address _staker, + address[] memory _stakingContracts + ) internal { uint256 totalRep; GoodFundManager gfm = GoodFundManager( nameService.getAddress("FUND_MANAGER") @@ -206,11 +208,10 @@ contract StakersDistribution is * @param _user the user to check rewards for * @return reputation rewards pending for user */ - function getUserPendingRewards(address[] memory _contracts, address _user) - public - view - returns (uint256) - { + function getUserPendingRewards( + address[] memory _contracts, + address _user + ) public view returns (uint256) { uint256 pending; for (uint256 i = 0; i < _contracts.length; i++) { ( @@ -239,11 +240,10 @@ contract StakersDistribution is * @param _user account to get rewards status for * @return (minted, pending) in GDAO 18 decimals */ - function getUserMintedAndPending(address[] memory _contracts, address _user) - public - view - returns (uint256, uint256) - { + function getUserMintedAndPending( + address[] memory _contracts, + address _user + ) public view returns (uint256, uint256) { uint256 pending = getUserPendingRewards(_contracts, _user); uint256 minted; for (uint256 i = 0; i < _contracts.length; i++) { diff --git a/contracts/reserve/DistributionHelper.sol b/contracts/reserve/DistributionHelper.sol index 019d0770..db2fdf73 100644 --- a/contracts/reserve/DistributionHelper.sol +++ b/contracts/reserve/DistributionHelper.sol @@ -44,7 +44,7 @@ contract DistributionHelper is 0x467719aD09025FcC6cF6F8311755809d45a5E5f3; enum TransferType { - FuseBridge, + DEPRECATED_FuseBridge, LayerZeroBridge, AxelarBridge, Contract @@ -182,10 +182,11 @@ contract DistributionHelper is if (toDistribute == 0) return; if (address(this).balance < feeSettings.minBalanceForFees) { - uint256 gdToSellfForFee = (toDistribute * + uint256 gdToSellForFee = (toDistribute * feeSettings.percentageToSellForFee) / 100; - toDistribute -= gdToSellfForFee; - buyNativeWithGD(gdToSellfForFee); + gdToSellForFee = calcGDToSell(gdToSellForFee); + toDistribute -= gdToSellForFee; + buyNativeWithGD(gdToSellForFee); } uint256 totalDistributed; @@ -234,12 +235,8 @@ contract DistributionHelper is DistributionRecipient storage _recipient, uint256 _amount ) internal { - if (_recipient.transferType == TransferType.FuseBridge) { - nativeToken().transferAndCall( - fuseBridge, - _amount, - abi.encodePacked(_recipient.addr) - ); + if (_recipient.transferType == TransferType.DEPRECATED_FuseBridge) { + revert("DEPRECATED"); } else if (_recipient.transferType == TransferType.LayerZeroBridge) { nativeToken().approve(address(mpbBridge), _amount); (uint256 lzFee, ) = ILayerZeroFeeEstimator(address(mpbBridge)) @@ -276,6 +273,28 @@ contract DistributionHelper is } } + function calcGDToSell( + uint256 maxAmountToSell + ) public view returns (uint256 gdToSell) { + uint24[] memory fees = new uint24[](1); + fees[0] = 500; + + uint256 ethToBuy = feeSettings.minBalanceForFees * 3; + (uint256 ethValueInUSDC, ) = STATIC_ORACLE + .quoteSpecificFeeTiersWithTimePeriod( + uint128(ethToBuy), + WETH_TOKEN, + USDC_TOKEN, + fees, + 60 //last 1 minute + ); + + uint256 gdPriceInDai = GoodReserveCDai(nameService.getAddress("RESERVE")) + .currentPriceDAI(); + gdToSell = (ethValueInUSDC * 1e12 * 100) / gdPriceInDai; //* 1e12 to increase usdc to 18 decimals, mul by 100 so result is in 2 G$ 2 decimals + gdToSell = gdToSell > maxAmountToSell ? maxAmountToSell : gdToSell; + } + function buyNativeWithGD(uint256 amountToSell) internal { address[] memory path = new address[](2); path[0] = nameService.getAddress("DAI"); diff --git a/contracts/reserve/ExchangeHelper.sol b/contracts/reserve/ExchangeHelper.sol index 59359e02..8640b88c 100644 --- a/contracts/reserve/ExchangeHelper.sol +++ b/contracts/reserve/ExchangeHelper.sol @@ -5,6 +5,8 @@ import "../utils/DAOUpgradeableContract.sol"; import "../utils/NameService.sol"; import "./GoodReserveCDai.sol"; +// import "hardhat/console.sol"; + contract ExchangeHelper is DAOUpgradeableContract { uint256 private _status; @@ -78,6 +80,14 @@ contract ExchangeHelper is DAOUpgradeableContract { cDaiAddress = nameService.getAddress("CDAI"); // Approve transfer to cDAI contract ERC20(daiAddress).approve(cDaiAddress, type(uint256).max); + address reserve = nameService.getAddress("RESERVE"); + if (reserve != address(0)) { + ERC20(nameService.getAddress("GOODDOLLAR")).approve( + reserve, + type(uint256).max + ); + ERC20(cDaiAddress).approve(reserve, type(uint256).max); + } ERC20(daiAddress).approve( nameService.getAddress("UNISWAP_ROUTER"), type(uint256).max @@ -121,11 +131,9 @@ contract ExchangeHelper is DAOUpgradeableContract { require( ERC20(_buyPath[0]).transferFrom( msg.sender, - address(_buyPath[0]) == cDaiAddress - ? address(reserve) - : address(this), + address(this), _tokenAmount - ) == true, + ), "transferFrom failed, make sure you approved input token transfer" ); } @@ -191,8 +199,9 @@ contract ExchangeHelper is DAOUpgradeableContract { GoodReserveCDai reserve = GoodReserveCDai( nameService.getAddress("RESERVE") ); - IGoodDollar(nameService.getAddress("GOODDOLLAR")).burnFrom( + ERC20(nameService.getAddress("GOODDOLLAR")).transferFrom( msg.sender, + address(this), _gdAmount ); @@ -290,7 +299,6 @@ contract ExchangeHelper is DAOUpgradeableContract { require(cDaiResult == 0, "Minting cDai failed"); uint256 cDaiInput = cDai.balanceOf(address(this)) - currCDaiBalance; - cDai.transfer(address(reserve), cDaiInput); return reserve.buy(cDaiInput, _minReturn, _targetAddress); } @@ -333,8 +341,9 @@ contract ExchangeHelper is DAOUpgradeableContract { ); return swap; } else { - if (isBuy) + if (isBuy) { ERC20(_inputPath[0]).approve(address(uniswapContract), _tokenAmount); + } swap = uniswapContract.swapExactTokensForTokens( _tokenAmount, isBuy ? _minDAIAmount : _minTokenReturn, diff --git a/contracts/reserve/GoodMarketMaker.sol b/contracts/reserve/GoodMarketMaker.sol index 3acdfa31..ce5d35f1 100644 --- a/contracts/reserve/GoodMarketMaker.sol +++ b/contracts/reserve/GoodMarketMaker.sol @@ -8,6 +8,8 @@ import "../DAOStackInterfaces.sol"; import "../Interfaces.sol"; import "../utils/DAOUpgradeableContract.sol"; +// import "hardhat/console.sol"; + /** @title Dynamic reserve ratio market maker */ @@ -187,11 +189,10 @@ contract GoodMarketMaker is DAOUpgradeableContract, DSMath { * @param _tokenAmount The amount of reserve token buying with * @return Number of GD that should be given in exchange as calculated by the bonding curve */ - function buyReturn(ERC20 _token, uint256 _tokenAmount) - public - view - returns (uint256) - { + function buyReturn( + ERC20 _token, + uint256 _tokenAmount + ) public view returns (uint256) { ReserveToken memory rtoken = reserveTokens[address(_token)]; return getBancor().calculatePurchaseReturn( @@ -208,11 +209,10 @@ contract GoodMarketMaker is DAOUpgradeableContract, DSMath { * @param _gdAmount The amount of GD that are sold * @return Number of tokens that should be given in exchange as calculated by the bonding curve */ - function sellReturn(ERC20 _token, uint256 _gdAmount) - public - view - returns (uint256) - { + function sellReturn( + ERC20 _token, + uint256 _gdAmount + ) public view returns (uint256) { ReserveToken memory rtoken = reserveTokens[address(_token)]; return getBancor().calculateSaleReturn( @@ -264,7 +264,8 @@ contract GoodMarketMaker is DAOUpgradeableContract, DSMath { rtoken.gdSupply += _gdAmount; rtoken.reserveRatio = uint32( ((rtoken.reserveSupply * 1e27) / - (rtoken.gdSupply * priceBeforeGdSupplyChange)) / 10**reserveDecimalsDiff + (rtoken.gdSupply * priceBeforeGdSupplyChange)) / + 10 ** reserveDecimalsDiff ); // Divide it decimal diff to bring it proper decimal } @@ -317,55 +318,46 @@ contract GoodMarketMaker is DAOUpgradeableContract, DSMath { * @param _token The desired reserve token to have * @return price of GD */ - function currentPrice(ERC20 _token) public view returns (uint256) { + function currentPrice(ERC20 _token) public view returns (uint256 price) { ReserveToken memory rtoken = reserveTokens[address(_token)]; - return - getBancor().calculateSaleReturn( - rtoken.gdSupply, - rtoken.reserveSupply, - rtoken.reserveRatio, - (10**decimals) - ); + price = + (rtoken.reserveSupply * 1e8) / + (rtoken.gdSupply * rtoken.reserveRatio); // gd precision 1e2 + reserveRatio precision 1e6 = multiply by 1e8 not to lose precision } - //TODO: need real calculation and tests /** * @dev Calculates how much G$ to mint based on added token supply (from interest) * and on current reserve ratio, in order to keep G$ price the same at the bonding curve - * formula to calculate the gd to mint: gd to mint = - * addreservebalance * (gdsupply / (reservebalance * reserveratio)) + * formula to calculate the gd to mint: amountToMint = reserveInterest * tokenSupply / reserveBalance * @param _token the reserve token * @param _addTokenSupply amount of token added to supply - * @return how much to mint in order to keep price in bonding curve the same + * @return toMint how much to mint in order to keep price in bonding curve the same */ - function calculateMintInterest(ERC20 _token, uint256 _addTokenSupply) - public - view - returns (uint256) - { - uint256 decimalsDiff = uint256(27) - decimals; - //resulting amount is in RAY precision - //we divide by decimalsdiff to get precision in GD (2 decimals) - return - ((_addTokenSupply * 1e27) / currentPrice(_token)) / (10**decimalsDiff); + function calculateMintInterest( + ERC20 _token, + uint256 _addTokenSupply + ) public view returns (uint256 toMint) { + ReserveToken memory rtoken = reserveTokens[address(_token)]; + + toMint = (_addTokenSupply * rtoken.gdSupply) / rtoken.reserveSupply; } /** * @dev Updates bonding curve based on _addTokenSupply and new minted amount * @param _token The reserve token * @param _addTokenSupply Amount of token added to supply - * @return How much to mint in order to keep price in bonding curve the same + * @return toMint How much to mint in order to keep price in bonding curve the same */ - function mintInterest(ERC20 _token, uint256 _addTokenSupply) - public - returns (uint256) - { + function mintInterest( + ERC20 _token, + uint256 _addTokenSupply + ) public returns (uint256 toMint) { _onlyReserveOrAvatar(); _onlyActiveToken(_token); if (_addTokenSupply == 0) { return 0; } - uint256 toMint = calculateMintInterest(_token, _addTokenSupply); + toMint = calculateMintInterest(_token, _addTokenSupply); ReserveToken storage reserveToken = reserveTokens[address(_token)]; reserveToken.gdSupply += toMint; reserveToken.reserveSupply += _addTokenSupply; @@ -376,24 +368,22 @@ contract GoodMarketMaker is DAOUpgradeableContract, DSMath { /** * @dev Calculate how much G$ to mint based on expansion change (new reserve * ratio), in order to keep G$ price the same at the bonding curve. the - * formula to calculate the gd to mint: gd to mint = - * (reservebalance / (newreserveratio * currentprice)) - gdsupply + * formula to calculate the gd to mint: + * amountToMint = (tokenSupply * reserveRatio - tokenSupply * newRatio) / newRatio * @param _token The reserve token - * @return How much to mint in order to keep price in bonding curve the same + * @return toMint How much to mint in order to keep price in bonding curve the same */ - function calculateMintExpansion(ERC20 _token) public view returns (uint256) { + function calculateMintExpansion( + ERC20 _token + ) public view returns (uint256 toMint) { ReserveToken memory reserveToken = reserveTokens[address(_token)]; uint32 newReserveRatio = calculateNewReserveRatio(_token); // new reserve ratio - uint256 reserveDecimalsDiff = uint256(27) - _token.decimals(); // //result is in RAY precision - uint256 denom = (uint256(newReserveRatio) * - 1e21 * - currentPrice(_token) * - (10**reserveDecimalsDiff)) / 1e27; // (newreserveratio * currentprice) in RAY precision - uint256 gdDecimalsDiff = uint256(27) - decimals; - uint256 toMint = ((reserveToken.reserveSupply * - (10**reserveDecimalsDiff) * - 1e27) / denom) / (10**gdDecimalsDiff); // reservebalance in RAY precision // return to gd precision - return toMint - reserveToken.gdSupply; + toMint = + (reserveToken.gdSupply * + reserveToken.reserveRatio - + reserveToken.gdSupply * + newReserveRatio) / + newReserveRatio; } /** diff --git a/contracts/reserve/GoodReserveCDai.sol b/contracts/reserve/GoodReserveCDai.sol index e11076cf..505046b7 100644 --- a/contracts/reserve/GoodReserveCDai.sol +++ b/contracts/reserve/GoodReserveCDai.sol @@ -59,6 +59,9 @@ contract GoodReserveCDai is bool public gdxDisabled; bool public discountDisabled; + + uint256 private _reentrantStatus; + // Emits when new GD tokens minted event UBIMinted( //epoch of UBI @@ -186,19 +189,21 @@ contract GoodReserveCDai is uint256 _tokenAmount, uint256 _minReturn, address _targetAddress - ) external returns (uint256) { + ) external nonReentrant returns (uint256) { ERC20 buyWith = ERC20(cDaiAddress); uint256 gdReturn = getMarketMaker().buy(buyWith, _tokenAmount); _targetAddress = _targetAddress == address(0x0) ? msg.sender : _targetAddress; address exchangeHelper = nameService.getAddress("EXCHANGE_HELPER"); - if (msg.sender != exchangeHelper) - require( - buyWith.transferFrom(msg.sender, address(this), _tokenAmount) == true, - "transferFrom failed, make sure you approved input token transfer" - ); + + require( + buyWith.transferFrom(msg.sender, address(this), _tokenAmount) == true, + "transferFrom failed, make sure you approved input token transfer" + ); + require(gdReturn >= _minReturn, "GD return must be above the minReturn"); + _mintGoodDollars(_targetAddress, gdReturn, true); //mint GDX _mintGDX(_targetAddress, gdReturn); @@ -224,7 +229,7 @@ contract GoodReserveCDai is address _token, address _to, uint256 _amount - ) external { + ) external nonReentrant { getMarketMaker().mintFromReserveRatio(ERC20(_token), _amount); _mintGoodDollars(_to, _amount, false); //mint GDX @@ -247,16 +252,24 @@ contract GoodReserveCDai is uint256 _minReturn, address _target, address _seller - ) external returns (uint256, uint256) { + ) external nonReentrant returns (uint256, uint256) { require(paused() == false, "paused"); GoodMarketMaker mm = getMarketMaker(); if (msg.sender != nameService.getAddress("EXCHANGE_HELPER")) { - IGoodDollar(nameService.getAddress("GOODDOLLAR")).burnFrom( - msg.sender, - _gdAmount - ); _seller = msg.sender; } + + /** + * transferfrom and then burn instead of burnfrom to make sure funds after fee are valid + * ie specifically for addresses that hold stolen funds who has 100% fee applied + */ + IGoodDollar(nameService.getAddress("GOODDOLLAR")).transferFrom( + msg.sender, + address(this), + _gdAmount + ); + IGoodDollar(nameService.getAddress("GOODDOLLAR")).burn(_gdAmount); + _target = _target == address(0x0) ? msg.sender : _target; //discount on exit contribution based on gdx @@ -358,18 +371,21 @@ contract GoodReserveCDai is * @dev only FundManager or other with mint G$ permission can call this to trigger minting. * Reserve sends UBI + interest to FundManager. * @param _daiToConvert DAI amount to convert cDAI - * @param _startingCDAIBalance Initial cDAI balance before staking collect process start * @param _interestToken The token that was transfered to the reserve * @return gdUBI,interestInCdai how much GD UBI was minted and how much cDAI collected from staking contracts */ function mintUBI( uint256 _daiToConvert, - uint256 _startingCDAIBalance, + uint256 /*_startingCDAIBalance*/, // dont trust it, use reserveSupply from marketmaker instead ERC20 _interestToken - ) external returns (uint256, uint256) { + ) external nonReentrant returns (uint256, uint256) { cERC20(cDaiAddress).mint(_daiToConvert); + + (uint256 reserveSupply, , , ) = getMarketMaker().reserveTokens(cDaiAddress); + uint256 interestInCdai = _interestToken.balanceOf(address(this)) - - _startingCDAIBalance; + reserveSupply; + uint256 gdInterestToMint = getMarketMaker().mintInterest( _interestToken, interestInCdai @@ -519,4 +535,16 @@ contract GoodReserveCDai is function when() public pure override returns (CallPhase) { return CallPhase.Pre; } + + modifier nonReentrant() { + // On the first call to nonReentrant, _status will be _NOT_ENTERED + require(_reentrantStatus != 1, "ReentrancyGuard: reentrant call"); + + // Any calls to nonReentrant after this point will fail + _reentrantStatus = 1; + _; + // By storing the original value once again, a refund is triggered (see + // https://eips.ethereum.org/EIPS/eip-2200) + _reentrantStatus = 0; + } } diff --git a/contracts/staking/GoodFundManager.sol b/contracts/staking/GoodFundManager.sol index 6d2f8600..df735c37 100644 --- a/contracts/staking/GoodFundManager.sol +++ b/contracts/staking/GoodFundManager.sol @@ -32,6 +32,13 @@ contract GoodFundManager is DAOUpgradeableContract, DSMath { //address of the active staking contracts address[] public activeContracts; + // Rewards per block for particular Staking contract + mapping(address => Reward) public rewardsForStakingContract; + + uint256 private _reentrantStatus; + + /** ADD NEW VARIABLES AFTER THIS LINE **/ + event GasCostSet(uint256 newGasCost); event CollectInterestTimeThresholdSet( uint256 newCollectInterestTimeThreshold @@ -62,8 +69,7 @@ contract GoodFundManager is DAOUpgradeableContract, DSMath { uint256 maxGasAmountSoFar; // Max gas amount that can spend to collect this interest according to interest amount bool maxGasLargerOrEqualRequired; // Bool that indicates if max gas amount larger or equal to actual gas needed } - // Rewards per block for particular Staking contract - mapping(address => Reward) public rewardsForStakingContract; + // Emits when `transferInterest` transfers // funds to the staking contract and to // the bridge @@ -221,7 +227,7 @@ contract GoodFundManager is DAOUpgradeableContract, DSMath { function collectInterest( address[] calldata _stakingContracts, bool _forceAndWaiverRewards - ) external { + ) external nonReentrant { uint256 initialGas = gasleft(); uint256 gdUBI; uint256 interestInCdai; @@ -242,6 +248,10 @@ contract GoodFundManager is DAOUpgradeableContract, DSMath { // elements are sorted by balances from lowest to highest if (_stakingContracts[i - 1] != address(0x0)) { + require( + isActiveContract(_stakingContracts[i - 1]), + "collectInterest: not a dao contract" + ); IGoodStaking(_stakingContracts[i - 1]).collectUBIInterest( reserveAddress ); @@ -277,7 +287,11 @@ contract GoodFundManager is DAOUpgradeableContract, DSMath { if ( block.timestamp >= lastCollectedInterest + collectInterestTimeThreshold ) { - require(interestInCdai >= gasPriceIncDAI, "UBI < gas costs"); // This require is necessary to keeper can not abuse this function + require( + interestInCdai >= gasPriceIncDAI || + gdUBI >= interestMultiplier * gdRewardToMint, + "UBI < gas costs" + ); // This require is necessary to keeper can not abuse this function } else { require( interestInCdai >= interestMultiplier * gasPriceIncDAI || @@ -299,6 +313,20 @@ contract GoodFundManager is DAOUpgradeableContract, DSMath { lastCollectedInterestBlock = block.number; } + /** + * @dev verifies that contract was added to the approved staking contracts in the past, and is not blacklisted + * @param _contract address of the contract + * @return isActive true if contract is active + **/ + function isActiveContract( + address _contract + ) public view returns (bool isActive) { + return + !rewardsForStakingContract[_contract].isBlackListed && + (rewardsForStakingContract[_contract].blockStart > 0 || + rewardsForStakingContract[_contract].blockEnd > 0); + } + /** * @dev Function that get interest informations of staking contracts in the sorted array by highest interest to lowest interest amount * @return array of interestInfo struct @@ -467,4 +495,16 @@ contract GoodFundManager is DAOUpgradeableContract, DSMath { function getActiveContractsCount() public view returns (uint256) { return activeContracts.length; } + + modifier nonReentrant() { + // On the first call to nonReentrant, _status will be _NOT_ENTERED + require(_reentrantStatus != 1, "ReentrancyGuard: reentrant call"); + + // Any calls to nonReentrant after this point will fail + _reentrantStatus = 1; + _; + // By storing the original value once again, a refund is triggered (see + // https://eips.ethereum.org/EIPS/eip-2200) + _reentrantStatus = 0; + } } diff --git a/contracts/utils/BuyGDClone.sol b/contracts/utils/BuyGDClone.sol index ae403adf..08f8659c 100644 --- a/contracts/utils/BuyGDClone.sol +++ b/contracts/utils/BuyGDClone.sol @@ -6,128 +6,6 @@ import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@mean-finance/uniswap-v3-oracle/solidity/interfaces/IStaticOracle.sol"; import "../Interfaces.sol"; -// @uniswap/v3-core -interface ISwapRouter { - struct ExactInputSingleParams { - address tokenIn; - address tokenOut; - uint24 fee; - address recipient; - uint256 amountIn; - uint256 amountOutMinimum; - uint160 sqrtPriceLimitX96; - } - - /// @notice Swaps `amountIn` of one token for as much as possible of another token - /// @dev Setting `amountIn` to 0 will cause the contract to look up its own balance, - /// and swap the entire amount, enabling contracts to send tokens before calling this function. - /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata - /// @return amountOut The amount of the received token - function exactInputSingle( - ExactInputSingleParams calldata params - ) external payable returns (uint256 amountOut); - - struct ExactInputParams { - bytes path; - address recipient; - uint256 amountIn; - uint256 amountOutMinimum; - } - - /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path - /// @dev Setting `amountIn` to 0 will cause the contract to look up its own balance, - /// and swap the entire amount, enabling contracts to send tokens before calling this function. - /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata - /// @return amountOut The amount of the received token - function exactInput( - ExactInputParams calldata params - ) external payable returns (uint256 amountOut); - - struct ExactOutputSingleParams { - address tokenIn; - address tokenOut; - uint24 fee; - address recipient; - uint256 amountOut; - uint256 amountInMaximum; - uint160 sqrtPriceLimitX96; - } - - /// @notice Swaps as little as possible of one token for `amountOut` of another token - /// that may remain in the router after the swap. - /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata - /// @return amountIn The amount of the input token - function exactOutputSingle( - ExactOutputSingleParams calldata params - ) external payable returns (uint256 amountIn); - - struct ExactOutputParams { - bytes path; - address recipient; - uint256 amountOut; - uint256 amountInMaximum; - } - - /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed) - /// that may remain in the router after the swap. - /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata - /// @return amountIn The amount of the input token - function exactOutput( - ExactOutputParams calldata params - ) external payable returns (uint256 amountIn); -} - -interface IQuoterV2 { - /// @notice Returns the amount out received for a given exact input swap without executing the swap - /// @param path The path of the swap, i.e. each token pair and the pool fee - /// @param amountIn The amount of the first token to swap - /// @return amountOut The amount of the last token that would be received - /// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path - /// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for each pool in the path - /// @return gasEstimate The estimate of the gas that the swap consumes - function quoteExactInput( - bytes memory path, - uint256 amountIn - ) - external - returns ( - uint256 amountOut, - uint160[] memory sqrtPriceX96AfterList, - uint32[] memory initializedTicksCrossedList, - uint256 gasEstimate - ); - - struct QuoteExactInputSingleParams { - address tokenIn; - address tokenOut; - uint256 amountIn; - uint24 fee; - uint160 sqrtPriceLimitX96; - } - - /// @notice Returns the amount out received for a given exact input but for a swap of a single pool - /// @param params The params for the quote, encoded as `QuoteExactInputSingleParams` - /// tokenIn The token being swapped in - /// tokenOut The token being swapped out - /// fee The fee of the token pool to consider for the pair - /// amountIn The desired input amount - /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap - /// @return amountOut The amount of `tokenOut` that would be received - /// @return sqrtPriceX96After The sqrt price of the pool after the swap - /// @return initializedTicksCrossed The number of initialized ticks that the swap crossed - /// @return gasEstimate The estimate of the gas that the swap consumes - function quoteExactInputSingle( - QuoteExactInputSingleParams memory params - ) - external - returns ( - uint256 amountOut, - uint160 sqrtPriceX96After, - uint32 initializedTicksCrossed, - uint256 gasEstimate - ); -} - /* * @title BuyGDClone * @notice This contract allows users to swap Celo or cUSD for GoodDollar (GD) tokens. diff --git a/contracts/utils/FuseOldBridgeKill.sol b/contracts/utils/FuseOldBridgeKill.sol new file mode 100644 index 00000000..0f6c6de0 --- /dev/null +++ b/contracts/utils/FuseOldBridgeKill.sol @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8; + +import "../Interfaces.sol"; + +interface IUpgradeabilityOwnerStorage { + function upgradeabilityOwner() external view returns (address); +} + +contract Upgradeable { + // Avoid using onlyUpgradeabilityOwner name to prevent issues with implementation from proxy contract + modifier onlyIfUpgradeabilityOwner() { + require( + msg.sender == + IUpgradeabilityOwnerStorage(address(this)).upgradeabilityOwner() + ); + /* solcov ignore next */ + _; + } +} + +/** + * @title EternalStorage + * @dev This contract holds all the necessary state variables to carry out the storage of any contract. + */ +contract EternalStorage { + mapping(bytes32 => uint256) internal uintStorage; + mapping(bytes32 => string) internal stringStorage; + mapping(bytes32 => address) internal addressStorage; + mapping(bytes32 => bytes) internal bytesStorage; + mapping(bytes32 => bool) internal boolStorage; + mapping(bytes32 => int256) internal intStorage; +} + +/** + * @title Ownable + * @dev This contract has an owner address providing basic authorization control + */ +contract Ownable is EternalStorage { + bytes4 internal constant UPGRADEABILITY_OWNER = 0x6fde8202; // upgradeabilityOwner() + + /** + * @dev Event to show ownership has been transferred + * @param previousOwner representing the address of the previous owner + * @param newOwner representing the address of the new owner + */ + event OwnershipTransferred(address previousOwner, address newOwner); + + /** + * @dev Throws if called by any account other than the owner. + */ + modifier onlyOwner() { + require(msg.sender == owner()); + /* solcov ignore next */ + _; + } + + /** + * @dev Throws if called by any account other than contract itself or owner. + */ + modifier onlyRelevantSender() { + // proxy owner if used through proxy, address(0) otherwise + (bool ok, bytes memory addr) = address(this).call( + abi.encodeWithSelector(UPGRADEABILITY_OWNER) + ); + address upgowner = abi.decode(addr, (address)); + require( + (ok && upgowner != address(0)) || // covers usage without calling through storage proxy + msg.sender == + IUpgradeabilityOwnerStorage(address(this)).upgradeabilityOwner() || // covers usage through regular proxy calls + msg.sender == address(this) // covers calls through upgradeAndCall proxy method + ); + /* solcov ignore next */ + _; + } + + bytes32 internal constant OWNER = + 0x02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c0; // keccak256(abi.encodePacked("owner")) + + /** + * @dev Tells the address of the owner + * @return the address of the owner + */ + function owner() public view returns (address) { + return addressStorage[OWNER]; + } + + /** + * @dev Allows the current owner to transfer control of the contract to a newOwner. + * @param newOwner the address to transfer ownership to. + */ + function transferOwnership(address newOwner) external onlyOwner { + require(newOwner != address(0)); + setOwner(newOwner); + } + + /** + * @dev Sets a new owner address + */ + function setOwner(address newOwner) internal { + emit OwnershipTransferred(owner(), newOwner); + addressStorage[OWNER] = newOwner; + } +} + +contract Initializable is EternalStorage { + bytes32 internal constant INITIALIZED = + 0x0a6f646cd611241d8073675e00d1a1ff700fbf1b53fcf473de56d1e6e4b714ba; // keccak256(abi.encodePacked("isInitialized")) + + function setInitialize() internal { + boolStorage[INITIALIZED] = true; + } + + function isInitialized() public view returns (bool) { + return boolStorage[INITIALIZED]; + } +} + +contract FuseOldBridgeKill is Initializable, Upgradeable { + function end() external { + IGoodDollar(0x495d133B938596C9984d462F007B676bDc57eCEC).renounceMinter(); + } +} diff --git a/contracts/utils/ReserveRestore.sol b/contracts/utils/ReserveRestore.sol new file mode 100644 index 00000000..469a7cba --- /dev/null +++ b/contracts/utils/ReserveRestore.sol @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import "../utils/NameService.sol"; +import "../Interfaces.sol"; +import "../reserve/GoodReserveCDai.sol"; +import "hardhat/console.sol"; + +contract ReserveRestore { + NameService ns; + uint256 public constant LOCKED_HACKED_FUNDS = 971921364208; + bool public executed; + address owner; + + constructor(NameService _ns) { + ns = _ns; + owner = msg.sender; + } + + function end() external { + require(msg.sender == owner, "not owner"); + Controller ctrl = Controller(ns.getAddress("CONTROLLER")); + address avatar = ns.dao().avatar(); + + // prevent executing again} + require(ctrl.unregisterSelf(avatar), "unregistering failed"); + } + + function donate(address daiFrom, uint256 amount) external { + require(msg.sender == owner, "not owner"); + address avatar = ns.dao().avatar(); + + GoodReserveCDai reserve = GoodReserveCDai(ns.getAddress("RESERVE")); + ERC20(ns.getAddress("DAI")).transferFrom(daiFrom, address(this), amount); + uint256 daiBalance = ERC20(ns.getAddress("DAI")).balanceOf(address(this)); + cERC20 cdai = cERC20(ns.getAddress("CDAI")); + ERC20 dai = ERC20(ns.getAddress("DAI")); + + dai.approve(address(cdai), daiBalance); + //Mint cDAIs + uint256 cDaiResult = cdai.mint(daiBalance); + require(cDaiResult == 0, "Minting cDai failed"); + uint256 cdaiBalance = cdai.balanceOf(address(this)); + cdai.transfer(address(reserve), cdaiBalance); + cdaiBalance = cdai.balanceOf(address(reserve)); + + uint256 gdSupply = ERC20(ns.getAddress("GOODDOLLAR")).totalSupply() - + LOCKED_HACKED_FUNDS; + console.log("supply: %s", gdSupply); + // get 0.0001 dai price in cdai + uint256 currentPrice = reserve.currentPrice(); + console.log("currentPrice: %s", currentPrice); + + console.log("cdaiBalance: %s", cdaiBalance); + + // given price calculate the reserve ratio + uint32 reserveRatio = uint32( + (cdaiBalance * 1e2 * 1e6) / (currentPrice * gdSupply) + ); // mul by 1e2 to cover gd precision, cdaibalance precision=initialprice, mul by 1e6 to receive result in the precision of reserveRatio(1e6) + console.log("reserveRatio: %s", reserveRatio); + Controller ctrl = Controller(ns.getAddress("CONTROLLER")); + // function initializeToken( + // ERC20 _token, + // uint256 _gdSupply, + // uint256 _tokenSupply, + // uint32 _reserveRatio, + // uint256 _lastExpansion + // ) + (bool ok, ) = ctrl.genericCall( + address(reserve.getMarketMaker()), + abi.encodeCall( + GoodMarketMaker.initializeToken, + (cdai, gdSupply, cdaiBalance, reserveRatio, block.timestamp) + ), + address(avatar), + 0 + ); + require(ok, "initializeToken failed"); + } + + function upgrade(address daiFrom, uint256 amount) external { + require(msg.sender == owner, "not owner"); + require(executed == false, "already upgraded"); + executed = true; + address avatar = ns.dao().avatar(); + + GoodReserveCDai reserve = GoodReserveCDai(ns.getAddress("RESERVE")); + ERC20(ns.getAddress("DAI")).transferFrom(daiFrom, address(this), amount); + uint256 daiBalance = ERC20(ns.getAddress("DAI")).balanceOf(address(this)); + require(daiBalance >= 50000e18, "not enough reserve"); + cERC20 cdai = cERC20(ns.getAddress("CDAI")); + ERC20 dai = ERC20(ns.getAddress("DAI")); + + dai.approve(address(cdai), daiBalance); + //Mint cDAIs + uint256 cDaiResult = cdai.mint(daiBalance); + require(cDaiResult == 0, "Minting cDai failed"); + uint256 cdaiBalance = cdai.balanceOf(address(this)); + cdai.transfer(address(reserve), cdaiBalance); + cdaiBalance = cdai.balanceOf(address(reserve)); + + uint256 gdSupply = ERC20(ns.getAddress("GOODDOLLAR")).totalSupply() - + LOCKED_HACKED_FUNDS; + console.log("supply: %s", gdSupply); + // get 0.0001 dai price in cdai + uint256 initialPriceCdai = (0.0001 * 1e8 * 1e28) / + cdai.exchangeRateStored(); //excghange rate is at 1e28 precision rate/1e28=1 cdai price in dai mul by 1e8 to get in cdai precision + console.log("initialPriceCdai: %s", initialPriceCdai); + + console.log("cdaiBalance: %s", cdaiBalance); + + // given price calculate the reserve ratio + uint32 reserveRatio = uint32( + (cdaiBalance * 1e2 * 1e6) / (initialPriceCdai * gdSupply) + ); // mul by 1e2 to cover gd precision, cdaibalance precision=initialprice, mul by 1e6 to receive result in the precision of reserveRatio(1e6) + console.log("reserveRatio: %s", reserveRatio); + Controller ctrl = Controller(ns.getAddress("CONTROLLER")); + // function initializeToken( + // ERC20 _token, + // uint256 _gdSupply, + // uint256 _tokenSupply, + // uint32 _reserveRatio, + // uint256 _lastExpansion + // ) + (bool ok, ) = ctrl.genericCall( + address(reserve.getMarketMaker()), + abi.encodeCall( + GoodMarketMaker.initializeToken, + (cdai, gdSupply, cdaiBalance, reserveRatio, block.timestamp) + ), + address(avatar), + 0 + ); + require(ok, "initializeToken failed"); + // ContributionCalc( + // ns.getAddress("CONTRIBUTION_CALCULATION") + // ).setContributionRatio(0.1*1e18,1e18); + + // exit contribution to 10% + (ok, ) = ctrl.genericCall( + address(ns.getAddress("CONTRIBUTION_CALCULATION")), + abi.encodeCall(ContributionCalc.setContributionRatio, (0.1 * 1e18, 1e18)), + address(avatar), + 0 + ); + require(ok, "setContributionRatio failed"); + + (ok, ) = ctrl.genericCall( + address(reserve), + abi.encodeCall(GoodReserveCDai.setGDXDisabled, (true, true)), + address(avatar), + 0 + ); + + require(ok, "setContributionRatio failed"); + } +} diff --git a/package.json b/package.json index 35804032..5251020b 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ "fs-extra": "9.0.0", "graphql": "^15.5.0", "graphql-request": "^3.4.0", - "hardhat": "^2.17.3", + "hardhat": "^2.*", "hardhat-contract-sizer": "^2.6.1", "hardhat-gas-reporter": "^1.0.8", "hardhat-storage-layout": "^0.1.7", @@ -133,7 +133,7 @@ "typechain": "^8.3.1", "typescript": "^5.3.3" }, - "packageManager": "yarn@3.6.0", + "packageManager": "yarn@3.6.1", "resolutions": { "@swc/core": "1.3.96" }, diff --git a/releases/deployment.json b/releases/deployment.json index 4e2d9c2a..83e45cab 100644 --- a/releases/deployment.json +++ b/releases/deployment.json @@ -434,6 +434,7 @@ "StaticOracle": "0x00851A91a3c4E9a4c1B48df827Bacc1f884bdE28", "BuyGDFactory": "0x00e533B7d6255D05b7f15034B1c989c21F51b91C", "BuyGDFactoryV2": "0x1F60C4C7037C6766924A43666B781ED1479587a2", + "CUSD": "0x765DE816845861e75A25fCA122bb6898B8B1282a", "BulkWhitelist": "0x30Afd0534dAA29135308d048103b40dc1BA4518d" }, "gnosis": { "GuardiansSafe": "0x84c10b45fe51bfb4f86c19a47fdbc187d4572fb8" }, diff --git a/scripts/multichain-deploy/helpers.ts b/scripts/multichain-deploy/helpers.ts index 3e8963b1..c07e1352 100644 --- a/scripts/multichain-deploy/helpers.ts +++ b/scripts/multichain-deploy/helpers.ts @@ -193,6 +193,10 @@ export const executeViaGuardian = async ( const results = []; for (let i = 0; i < contracts.length; i++) { const contract = contracts[i]; + if (!contract) { + console.warn("skipping executing missing contract", i, contracts[i], functionSigs[i], functionInputs[i]) + continue; + } console.log("executing:", contracts[i], functionSigs[i], functionInputs[i]); const sigHash = ethers.utils.keccak256(ethers.utils.toUtf8Bytes(functionSigs[i])).slice(0, 10); const encoded = ethers.utils.solidityPack(["bytes4", "bytes"], [sigHash, functionInputs[i]]); @@ -404,9 +408,8 @@ export const verifyContract = async ( networkProvider = networkProvider === "mainnet" ? "ethereum" : networkProvider; console.log("truffle compile..."); await exec("npx truffle compile"); - const cmd = `npx truffle run verify ${proxyName ? "--custom-proxy " + proxyName : ""} ${contractName}@${address} ${ - forcedConstructorArguments ? "--forceConstructorArgs string:" + forcedConstructorArguments.slice(2) : "" - } --network ${networkProvider}`; + const cmd = `npx truffle run verify ${proxyName ? "--custom-proxy " + proxyName : ""} ${contractName}@${address} ${forcedConstructorArguments ? "--forceConstructorArgs string:" + forcedConstructorArguments.slice(2) : "" + } --network ${networkProvider}`; console.log("running...:", cmd); await exec(cmd).then(({ stdout, stderr }) => { console.log("Result for:", cmd); diff --git a/scripts/proposals/reserveRestore.ts b/scripts/proposals/reserveRestore.ts new file mode 100644 index 00000000..63a26250 --- /dev/null +++ b/scripts/proposals/reserveRestore.ts @@ -0,0 +1,429 @@ +/*** + * Mainnet: + * FIXES: + * - prevent hacked funds burnFrom + * - set GOOD rewards to 0 + * - prevent untrusted contracts in goodfundmanager + * - use bonding curve for actual cDAI balance (prevent the "buy" instead of "transferTo" used in hack to trick reserve into minting UBI from interest) + * - set exit contribution to 10% + * - disable gdx + * - fix reserve calculations of expansion/currentprice + * - add requirement of guardians to approve on-chain proposals + * - reserve should not trust exchange helper + * - resere should not trust fundmanager for its starting balance + * + * PLAN: + * - pause staking + * - prevent fusebridge usage + * - set GOOD rewards to 0 + * - blacklist hacked accounts to prevent burn (transfer already blocked done via tax) + * - withdraw funds from fuse + * - transfer to MPB bridge + * - upgrade reserve + * - set new reserve ratio, supply(minus hacked funds) and reserve + * - set contribution to 10% + * - unpause reserve + * - upgrade exchangeHelper + * - upgrade goodfundmanager + * - upgrade governance + * - unpause reserve + * - unpause goodfundmanager + * - switch fuse distribution to use lz bridge insted of deprecated fuse bridge + * + * + * Fuse: + * PLAN: + * - prevent old fuse bridge usage + * - upgrade governance + * + **/ + +import { network, ethers } from "hardhat"; +import { reset, time } from "@nomicfoundation/hardhat-network-helpers"; +import { defaultsDeep, last } from "lodash"; +import prompt from "prompt"; +// import mpbDeployments from "@gooddollar/bridge-contracts/release/mpb.json" + +import { executeViaGuardian, executeViaSafe, verifyProductionSigner } from "../multichain-deploy/helpers"; + +import ProtocolSettings from "../../releases/deploy-settings.json"; + +import dao from "../../releases/deployment.json"; +import { ExchangeHelper, FuseOldBridgeKill, GoodFundManager, GoodMarketMaker, GoodReserveCDai, IGoodDollar, ReserveRestore } from "../../types"; +let { name: networkName } = network; + + +const isSimulation = network.name === "hardhat" || network.name === "fork" || network.name === "localhost"; + +// hacker and hacked multichain bridge accounts +const LOCKED_ACCOUNTS = ["0xeC577447D314cf1e443e9f4488216651450DBE7c", "0xD17652350Cfd2A37bA2f947C910987a3B1A1c60d", "0x6738fA889fF31F82d9Fe8862ec025dbE318f3Fde"] +const INITIAL_DAI = ethers.utils.parseEther("100000") // 100k +// reserve funder (goodlabs safe) +const funder = "0xF0652a820dd39EC956659E0018Da022132f2f40a" + +export const upgradeMainnet = async network => { + const isProduction = networkName.includes("production"); + let [root, ...signers] = await ethers.getSigners(); + + if (isProduction) verifyProductionSigner(root); + + let guardian = root; + + //simulate produciton on fork + if (isSimulation) { + networkName = "production-mainnet"; + } + + let release: { [key: string]: any } = dao[networkName]; + let protocolSettings = defaultsDeep({}, ProtocolSettings[networkName], ProtocolSettings["default"]); + + //simulate on fork, make sure safe has enough eth to simulate txs + if (isSimulation) { + await reset("https://eth.drpc.org"); + guardian = await ethers.getImpersonatedSigner(protocolSettings.guardiansSafe); + + await root.sendTransaction({ + value: ethers.utils.parseEther("1"), + to: protocolSettings.guardiansSafe + }); + } + + const rootBalance = await ethers.provider.getBalance(root.address).then(_ => _.toString()); + const guardianBalance = await ethers.provider.getBalance(guardian.address).then(_ => _.toString()); + + console.log("got signers:", { + networkName, + root: root.address, + guardian: guardian.address, + balance: rootBalance, + guardianBalance: guardianBalance + }); + + const reserveImpl = await ethers.deployContract("GoodReserveCDai"); + const goodFundManagerImpl = await ethers.deployContract("GoodFundManager"); + const exchangeHelperImpl = await ethers.deployContract("ExchangeHelper"); + const stakersDistImpl = await ethers.deployContract("StakersDistribution"); + const govImpl = await ethers.deployContract("CompoundVotingMachine"); + const distHelperImplt = await ethers.deployContract("DistributionHelper"); + const marketMakerImpl = await ethers.deployContract("GoodMarketMaker"); + const upgradeImpl = await ethers.deployContract("ReserveRestore", [release.NameService]) as ReserveRestore; + + const gd = (await ethers.getContractAt("IGoodDollar", release.GoodDollar)) as IGoodDollar; + + + // test blacklisting to prevent burn by hacker + if (isSimulation) { + + const locked = await ethers.getImpersonatedSigner(LOCKED_ACCOUNTS[0]); + const tx = await gd + .connect(locked) + .burn("10") + .then(_ => _.wait()) + .then(_ => _.status) + .catch(e => e); + + console.log("Burn tx before:", tx); + + const funderSigner = await ethers.getImpersonatedSigner(funder) + const dai = await ethers.getContractAt("IGoodDollar", release.DAI) + await dai.connect(funderSigner).approve(upgradeImpl.address, ethers.utils.parseEther("200000")) + const whale = await ethers.getImpersonatedSigner("0xa359Fc83C48277EedF375a5b6DC9Ec7D093aD3f2") + await dai.connect(whale).transfer(root.address, ethers.utils.parseEther("100000")) + + const lockedFunds = await Promise.all(LOCKED_ACCOUNTS.map(_ => gd.balanceOf(_))) + const totalLocked = lockedFunds.reduce((acc, cur) => acc.add(cur), ethers.constants.Zero) + console.log({ totalLocked }) + + } + + const startSupply = await gd.totalSupply(); + + + console.log("executing proposals"); + + const proposalContracts = [ + release.StakingContractsV3[0][0], // pause staking + release.StakingContractsV3[1][0], // pause staking + release.StakersDistribution, //set GOOD rewards to 0 + release.GoodReserveCDai, //expansion ratio + release.ForeignBridge, // prevent from using + release.Identity, // set locked G$ accounts as blacklisted so cant do burn from + release.Identity, // set locked G$ accounts as blacklisted so cant do burn from + release.Identity, // set locked G$ accounts as blacklisted so cant do burnfrom + release.ForeignBridge, // claim bridge tokens to mpb bridge + release.GoodReserveCDai, //upgrade reserve + release.GoodFundManager, //upgrade fundmanager + release.ExchangeHelper, //upgrade exchangehelper + release.DistributionHelper, //upgrade disthelper + release.StakersDistribution, //upgrade stakers dist + release.GoodMarketMaker, //upgrade mm + release.CompoundVotingMachine, // upgrade gov + release.DistributionHelper, // switch to lz bridge for fuse + release.ExchangeHelper, // activate upgrade changes + release.Controller, + // upgradeImpl.address, + release.GuardiansSafe + "_" + release.GoodReserveCDai + ]; + + const proposalEthValues = proposalContracts.map(_ => 0); + + const proposalFunctionSignatures = [ + "pause(bool)", + "pause(bool)", + "setMonthlyReputationDistribution(uint256)", + "setReserveRatioDailyExpansion(uint256,uint256)", + "setExecutionDailyLimit(uint256)", // set limit to 0 so old bridge cant be used + "addBlacklisted(address)", + "addBlacklisted(address)", + "addBlacklisted(address)", + "claimTokens(address,address)", + "upgradeTo(address)", + "upgradeTo(address)", + "upgradeTo(address)", + "upgradeTo(address)", + "upgradeTo(address)", + "upgradeTo(address)", + "upgradeTo(address)", + "addOrUpdateRecipient((uint32,uint32,address,uint8))", + "setAddresses()", + "registerScheme(address,bytes32,bytes4,address)", // give upgrade contract permissions + // "upgrade(address, uint256)", + "unpause()" + ]; + + const proposalFunctionInputs = [ + ethers.utils.defaultAbiCoder.encode(["bool"], [true]), + ethers.utils.defaultAbiCoder.encode(["bool"], [true]), + ethers.utils.defaultAbiCoder.encode(["uint256"], [0]), + ethers.utils.defaultAbiCoder.encode(["uint256", "uint256"], [999711382710978, 1e15]), + ethers.utils.defaultAbiCoder.encode(["uint256"], [0]), + ethers.utils.defaultAbiCoder.encode(["address"], [LOCKED_ACCOUNTS[0]]), + ethers.utils.defaultAbiCoder.encode(["address"], [LOCKED_ACCOUNTS[1]]), + ethers.utils.defaultAbiCoder.encode(["address"], [LOCKED_ACCOUNTS[2]]), + ethers.utils.defaultAbiCoder.encode(["address", "address"], [release.GoodDollar, release.MpbBridge]), + ethers.utils.defaultAbiCoder.encode(["address"], [reserveImpl.address]), + ethers.utils.defaultAbiCoder.encode(["address"], [goodFundManagerImpl.address]), + ethers.utils.defaultAbiCoder.encode(["address"], [exchangeHelperImpl.address]), + ethers.utils.defaultAbiCoder.encode(["address"], [distHelperImplt.address]), + ethers.utils.defaultAbiCoder.encode(["address"], [stakersDistImpl.address]), + ethers.utils.defaultAbiCoder.encode(["address"], [marketMakerImpl.address]), + ethers.utils.defaultAbiCoder.encode(["address"], [govImpl.address]), + ethers.utils.defaultAbiCoder.encode( + ["uint32", "uint32", "address", "uint8"], + [1000, 122, dao["production"].UBIScheme, 1] //10% chainId 122 ubischeme 1-lz bridge + ), + "0x", //setAddresses + ethers.utils.defaultAbiCoder.encode( + ["address", "bytes32", "bytes4", "address"], + [ + upgradeImpl.address, //scheme + ethers.constants.HashZero, //paramshash + "0x000000f1", //permissions - minimal + release.Avatar + ] + ), + "0x" + ]; + + if (isProduction) { + await executeViaSafe( + proposalContracts, + proposalEthValues, + proposalFunctionSignatures, + proposalFunctionInputs, + protocolSettings.guardiansSafe, + "mainnet" + ); + } else { + //simulation or dev envs + await executeViaGuardian( + proposalContracts, + proposalEthValues, + proposalFunctionSignatures, + proposalFunctionInputs, + guardian, + networkName + ); + } + + if (isSimulation) { + await mainnetPostChecks(upgradeImpl) + } +}; + +const mainnetPostChecks = async (upgradeImpl: ReserveRestore) => { + networkName = "production-mainnet"; + let release: { [key: string]: any } = dao[networkName]; + + let [root, ...signers] = await ethers.getSigners(); + const gd = await ethers.getContractAt("IGoodDollar", release.GoodDollar); + + //execute the reserve initialization + (await upgradeImpl.upgrade(funder, INITIAL_DAI)).wait() + + + const locked = await ethers.getImpersonatedSigner(LOCKED_ACCOUNTS[0]); + const tx = await gd + .connect(locked) + .burn("10", { maxFeePerGas: 30e9, maxPriorityFeePerGas: 1e9, gasLimit: 200000 }) + .then(_ => _.wait()) + .then(_ => _.status) + .catch(e => e); + console.log("Burn tx after should fail:", tx); + + const dai = await ethers.getContractAt("IGoodDollar", release.DAI); + const cdai = await ethers.getContractAt("IGoodDollar", release.cDAI); + const reserve = await ethers.getContractAt("GoodReserveCDai", release.GoodReserveCDai) as GoodReserveCDai + const mm = await ethers.getContractAt("GoodMarketMaker", release.GoodMarketMaker) as GoodMarketMaker + const newExpansion = await mm.reserveRatioDailyExpansion() + console.log("new expansion set:", newExpansion, newExpansion.mul(1e15).div(ethers.utils.parseEther("1000000000")).toNumber() / 1e15 === 0.999711382710978) + console.log("discount should be disabled:", await reserve.discountDisabled(), " gdx should be disabled:", await reserve.gdxDisabled()); + const resereState = await mm.reserveTokens(release.cDAI) + console.log({ resereState }) + const finalSupply = await gd.totalSupply(); + const distHelper = await ethers.getContractAt("DistributionHelper", release.DistributionHelper) + const result = await distHelper.calcGDToSell(1e9) + console.log("how much G$ to sell to cover distribution fees out of 1M:", result.toNumber() / 100) + const [cdaiPriceBefore, daiPriceBefore] = await (await Promise.all([reserve.currentPrice(), reserve.currentPriceDAI()])).map(_ => _.toNumber()) + console.log({ cdaiPriceBefore, daiPriceBefore }) + const dex = await ethers.getContractAt("ExchangeHelper", release.ExchangeHelper) as ExchangeHelper + await dai.approve(dex.address, ethers.utils.parseEther("10000")) + await dex.buy([release.DAI], ethers.utils.parseEther("10000"), 0, 0, root.address); + // check g$ prices + const [cdaiPriceAfter, daiPriceAfter] = await (await Promise.all([reserve.currentPrice(), reserve.currentPriceDAI()])).map(_ => _.toNumber()) + console.log("prices after buying form reserve with 10k DAI", { cdaiPriceAfter, daiPriceAfter }) + await gd.approve(dex.address, await gd.balanceOf(root.address)) + await dex.sell([release.DAI], await gd.balanceOf(root.address), 0, 0, root.address); + const daiBalanceAfterSell = await dai.balanceOf(root.address) + // expect a 10% sell fee + console.log("expect 10% sell fee (selling 10K gets only 9K of dai, balance should be ~99K):", { daiBalanceAfterSell }) + const cdaiReserveBalance = await cdai.balanceOf(reserve.address) + console.log({ cdaiReserveBalance }) + + const [mpbBalance, fuseBalance] = await Promise.all([gd.balanceOf(release.MpbBridge), gd.balanceOf(release.ForeignBridge)]) + console.log("fuse bridge should have 0 balance and Mpb should be >6B", { mpbBalance, fuseBalance }) + const gfm = await ethers.getContractAt("GoodFundManager", release.GoodFundManager) as GoodFundManager + const stakingContracts = await gfm.callStatic.calcSortedContracts() + console.log({ stakingContracts }) + const interesTX = await (await gfm.collectInterest(stakingContracts.map(_ => _[0]), false)).wait() + const ubiEvents = last(await reserve.queryFilter(reserve.filters.UBIMinted(), -1)) + console.log("collectinterest gfm events:", interesTX.events?.find(_ => _.event === 'FundsTransferred')) + console.log("ubiEvents after collect interest:", ubiEvents) + // check expansion after some time + await time.increase(365 * 60 * 60 * 24) + const gdSupplyBeforeExpansion = await gd.totalSupply(); + const reserveStateBeforeYearExpansion = await mm.reserveTokens(release.cDAI) + + const expansionTX = await (await gfm.collectInterest([], false)).wait() + const ubiExpansionEvents = last(await reserve.queryFilter(reserve.filters.UBIMinted(), -1)) + console.log("gfm events after 1 year expansion:", expansionTX.events?.filter(_ => _.event === 'FundsTransferred')) + console.log("ubiEvents after 1 year expansion:", ubiExpansionEvents) + const reserveStateAfterYearExpansion = await mm.reserveTokens(release.cDAI) + const gdSupplyAfterExpansion = await gd.totalSupply(); + console.log({ reserveStateAfterYearExpansion, gdSupplyAfterExpansion, gdSupplyBeforeExpansion, reserveStateBeforeYearExpansion }) + + //execute the reserve initialization + await (await upgradeImpl.donate(funder, INITIAL_DAI)).wait() + const [cdaiPriceAfterDonation, daiPriceAfterDonation] = await (await Promise.all([reserve.currentPrice(), reserve.currentPriceDAI()])).map(_ => _.toNumber()) + console.log("price after dai donation:", { cdaiPriceAfterDonation, daiPriceAfterDonation }) + const reserveStateAfterDonation = await mm.reserveTokens(release.cDAI) + console.log({ reserveStateAfterDonation }) + + await (await upgradeImpl.end()).wait() +} +export const upgradeFuse = async network => { + let [root] = await ethers.getSigners(); + + const isProduction = networkName.includes("production"); + + let networkEnv = networkName.split("-")[0]; + if (isSimulation) networkEnv = "production"; + + let release: { [key: string]: any } = dao[networkEnv]; + + let guardian = root; + //simulate on fork, make sure safe has enough eth to simulate txs + if (isSimulation) { + await reset("https://fuse.liquify.com"); + guardian = await ethers.getImpersonatedSigner(release.GuardiansSafe); + + await root.sendTransaction({ value: ethers.constants.WeiPerEther.mul(3), to: guardian.address }); + } + + + + const gd = (await ethers.getContractAt("IGoodDollar", release.GoodDollar)) as IGoodDollar; + + const isMinter = await gd.isMinter(release.HomeBridge); + + console.log({ networkEnv, guardian: guardian.address, isSimulation, isProduction, isMinter }); + + const govImpl = await ethers.deployContract("CompoundVotingMachine"); + const killBridge = await ethers.deployContract("FuseOldBridgeKill") as FuseOldBridgeKill + + const proposalContracts = [ + release.HomeBridge, // prevent from using by upgrading to empty contract and removing minting rights + release.CompoundVotingMachine, //upgrade gov + ]; + + const proposalEthValues = proposalContracts.map(_ => 0); + + const proposalFunctionSignatures = [ + "upgradeToAndCall(uint256,address,bytes)", // upgrade and call end + "upgradeTo(address)" + ]; + + + const proposalFunctionInputs = [ + ethers.utils.defaultAbiCoder.encode(["uint256", "address", "bytes"], [2, killBridge.address, killBridge.interface.encodeFunctionData("end")]), + ethers.utils.defaultAbiCoder.encode(["address"], [govImpl.address]), + + ]; + + if (isProduction) { + await executeViaSafe( + proposalContracts, + proposalEthValues, + proposalFunctionSignatures, + proposalFunctionInputs, + release.GuardiansSafe, + "fuse" + ); + } else { + await executeViaGuardian( + proposalContracts, + proposalEthValues, + proposalFunctionSignatures, + proposalFunctionInputs, + guardian, + networkEnv + ); + } + + if (isSimulation) { + + const isMinter = await gd.isMinter(release.HomeBridge); + console.log("Fuse bridge scheme registration check:", isMinter ? "Failed" : "Success"); + } +}; + +export const main = async () => { + prompt.start(); + const { network } = await prompt.get(["network"]); + + console.log("running step:", { network }); + const chain = last(network.split("-")); + switch (chain) { + case "mainnet": + // await mainnetPostChecks() + await upgradeMainnet(network); + + break; + case "fuse": + await upgradeFuse(network); + + break; + } +}; + +main().catch(console.log); diff --git a/scripts/test/localOldDaoDeploy.ts b/scripts/test/localOldDaoDeploy.ts index 0c48703b..c87ff7cc 100644 --- a/scripts/test/localOldDaoDeploy.ts +++ b/scripts/test/localOldDaoDeploy.ts @@ -650,7 +650,6 @@ export const deployUBI = async deployedDAO => { const lastBlock = await ethers.provider.getBlock("latest"); const periodStart = await ubiScheme.periodStart().then(_ => _.toNumber()); const diff = periodStart - lastBlock.timestamp; - await increaseTime(diff); //make sure period start has reached console.log("ubischeme start:", { now: now.timestamp, blockTime: lastBlock.timestamp, @@ -658,6 +657,8 @@ export const deployUBI = async deployedDAO => { periodEnd: await ubiScheme.periodEnd().then(_ => _.toString()), diff }); + if (diff > 0 && diff < 2 ** 64) + await increaseTime(diff); //make sure period start has reached const tx = await firstClaim.start(); console.log("firstclaim started"); await ubiScheme.start(); diff --git a/test/governance/CompoundVotingMachine.daoscheme.ts b/test/governance/CompoundVotingMachine.daoscheme.ts index 9c49cc28..59c107f1 100644 --- a/test/governance/CompoundVotingMachine.daoscheme.ts +++ b/test/governance/CompoundVotingMachine.daoscheme.ts @@ -89,6 +89,9 @@ describe("CompoundVotingMachine#DAOScheme", () => { await grep.mint(acct.address, ethers.BigNumber.from("500000")); queuePeriod = await gov.queuePeriod().then(_ => _.toNumber()); + + //disable guardian + await gov.renounceGuardian() }); ///cell 0 - votingPeriod blocks, 1 - quoromPercentage, 2 - proposalPercentage,3 - proposalMaxOperations, 4 - voting delay blocks, 5 - queuePeriod time diff --git a/test/governance/CompoundVotingMachine.guardian.test.ts b/test/governance/CompoundVotingMachine.guardian.test.ts index 34d7991d..51c0458f 100644 --- a/test/governance/CompoundVotingMachine.guardian.test.ts +++ b/test/governance/CompoundVotingMachine.guardian.test.ts @@ -185,13 +185,13 @@ describe("CompoundVotingMachine#Guardian", () => { await gov .connect(signers[4]) - ["propose(address[],uint256[],string[],bytes[],string)"]( - targets, - values, - signatures, - callDatas, - "do nothing" - ); + ["propose(address[],uint256[],string[],bytes[],string)"]( + targets, + values, + signatures, + callDatas, + "do nothing" + ); let proposalId = await gov.latestProposalIds(signers[4].address); await advanceBlocks(1); @@ -204,34 +204,89 @@ describe("CompoundVotingMachine#Guardian", () => { await grep.delegateTo(root.address); //delegate back our votes }); + it("Should not be able to pass proposal without guardian approval", async () => { + let targets = [gov.address]; + let values = ["0"]; + let signatures = ["setGuardian(address)"]; + let callDatas = [encodeParameters(["address"], [signers[1].address])]; + + await gov + .connect(root) + ["propose(address[],uint256[],string[],bytes[],string)"]( + targets, + values, + signatures, + callDatas, + "set guardian" + ); + let proposalBlock = +(await ethers.provider.getBlockNumber()); + let proposalId = await gov.latestProposalIds(root.address); + await advanceBlocks(1); + await gov.connect(root).castVote(proposalId, true); + await increaseTime(queuePeriod); + expect(states[await gov.state(proposalId)]).to.equal("Succeeded"); + + await expect(gov.execute(proposalId)).revertedWith(/not approved/); + + }) + it("Should be able to pass proposal to change guardian", async () => { - console.log( - grep.address, - await grep.totalSupply().then(_ => _.toString()), - await (await grep.getVotes(root.address)).toString(), - await (await grep.balanceOfLocal(root.address)).toString(), - await gov.rep() + let targets = [gov.address]; + let values = ["0"]; + let signatures = ["setGuardian(address)"]; + let callDatas = [encodeParameters(["address"], [signers[2].address])]; + + await gov + .connect(root) + ["propose(address[],uint256[],string[],bytes[],string)"]( + targets, + values, + signatures, + callDatas, + "set guardian" ); + let proposalBlock = +(await ethers.provider.getBlockNumber()); + let proposalId = await gov.latestProposalIds(root.address); + await advanceBlocks(1); + await gov.connect(root).castVote(proposalId, true); + await increaseTime(queuePeriod); + expect(states[await gov.state(proposalId)]).to.equal("Succeeded"); + + await gov.connect(signers[1]).approveProposal(proposalId) + + await gov.execute(proposalId); + expect(states[await gov.state(proposalId)]).to.equal("Executed"); + + //acct should now have 1M after proposal minted rep + expect(await gov.guardian()).to.equal(signers[2].address); + }); + + it("Should be able to pass proposal without approval if no guardian set", async () => { let targets = [gov.address]; let values = ["0"]; let signatures = ["setGuardian(address)"]; let callDatas = [encodeParameters(["address"], [signers[1].address])]; + await gov.connect(signers[2]).renounceGuardian() + + expect(await gov.guardian()).equal(ethers.constants.AddressZero) + await gov .connect(root) - ["propose(address[],uint256[],string[],bytes[],string)"]( - targets, - values, - signatures, - callDatas, - "set guardian" - ); - let proposalBlock = +(await ethers.provider.getBlockNumber()); + ["propose(address[],uint256[],string[],bytes[],string)"]( + targets, + values, + signatures, + callDatas, + "set guardian" + ); + let proposalId = await gov.latestProposalIds(root.address); await advanceBlocks(1); await gov.connect(root).castVote(proposalId, true); await increaseTime(queuePeriod); expect(states[await gov.state(proposalId)]).to.equal("Succeeded"); + await gov.execute(proposalId); expect(states[await gov.state(proposalId)]).to.equal("Executed"); diff --git a/test/governance/CompoundVotingMachine.state.test.ts b/test/governance/CompoundVotingMachine.state.test.ts index 0c3db51c..686d5340 100644 --- a/test/governance/CompoundVotingMachine.state.test.ts +++ b/test/governance/CompoundVotingMachine.state.test.ts @@ -107,6 +107,9 @@ describe("CompoundVotingMachine#States", () => { votePeriod = await gov.votingPeriod().then(_ => _.toNumber()); queuePeriod = await gov.queuePeriod().then(_ => _.toNumber()); gracePeriod = await gov.gracePeriod().then(_ => _.toNumber()); + + //disable guardian + await gov.renounceGuardian() }); it("Invalid for proposal not found", async () => { @@ -130,13 +133,13 @@ describe("CompoundVotingMachine#States", () => { await grep.delegateTo(actor.address); await gov .connect(actor) - ["propose(address[],uint256[],string[],bytes[],string)"]( - targets, - values, - signatures, - callDatas, - "do nothing" - ); + ["propose(address[],uint256[],string[],bytes[],string)"]( + targets, + values, + signatures, + callDatas, + "do nothing" + ); let newProposalId = await gov.proposalCount(); // send away the delegates @@ -212,13 +215,13 @@ describe("CompoundVotingMachine#States", () => { await advanceBlocks(1); await gov .connect(actor) - ["propose(address[],uint256[],string[],bytes[],string)"]( - targets, - values, - signatures, - callDatas, - "do nothing" - ); + ["propose(address[],uint256[],string[],bytes[],string)"]( + targets, + values, + signatures, + callDatas, + "do nothing" + ); let proposalId = await gov.latestProposalIds(actor.address); await advanceBlocks(1); await gov.connect(actor).castVote(proposalId, false); @@ -237,13 +240,13 @@ describe("CompoundVotingMachine#States", () => { await advanceBlocks(1); await gov .connect(actor) - ["propose(address[],uint256[],string[],bytes[],string)"]( - targets, - values, - signatures, - callDatas, - "do nothing" - ); + ["propose(address[],uint256[],string[],bytes[],string)"]( + targets, + values, + signatures, + callDatas, + "do nothing" + ); let proposalId = await gov.latestProposalIds(actor.address); await advanceBlocks(1); await gov.connect(actor).castVote(proposalId, true); @@ -310,13 +313,13 @@ describe("CompoundVotingMachine#States", () => { await advanceBlocks(1); await gov .connect(signers[0]) - ["propose(address[],uint256[],string[],bytes[],string)"]( - targets, - values, - signatures, - callDatas, - "do nothing" - ); + ["propose(address[],uint256[],string[],bytes[],string)"]( + targets, + values, + signatures, + callDatas, + "do nothing" + ); let proposalId = await gov.proposalCount(); await advanceBlocks(1); diff --git a/test/governance/StakersDistribution.test.ts b/test/governance/StakersDistribution.test.ts index 8be0cc6a..47676b45 100644 --- a/test/governance/StakersDistribution.test.ts +++ b/test/governance/StakersDistribution.test.ts @@ -652,7 +652,6 @@ describe("StakersDistribution - staking with GD and get Rewards in GDAO", () => ); await advanceBlocks(4); const gdaoBalanceBeforeWithdraw = await grep.balanceOf(staker.address); - console.log({ gdaoBalanceBeforeWithdraw, rewardsPerBlock }); await simpleStaking1.connect(staker).withdrawStake(stakingAmount, false); const withdrawBlockNumber = await ethers.provider.getBlockNumber(); await stakersDistribution.claimReputation(staker.address, [ @@ -903,8 +902,8 @@ describe("StakersDistribution - staking with GD and get Rewards in GDAO", () => staker.address, simpleStaking1.address ); - expect(userMintedRewardAfterStake.eq(0)); - expect(userPendingRewardAfterStake.gt(0)); + expect(userMintedRewardAfterStake).eq(0); + expect(userPendingRewardAfterStake).gt(0); await stakersDistribution.claimReputation(staker.address, [ simpleStaking1.address @@ -915,8 +914,8 @@ describe("StakersDistribution - staking with GD and get Rewards in GDAO", () => staker.address, simpleStaking1.address ); - expect(userMintedRewardAfterClaim.gt(0)); - expect(userPendingRewardAfterClaim.eq(0)); + expect(userMintedRewardAfterClaim).gt(0); + expect(userPendingRewardAfterClaim).eq(0); }); async function getUserMintedAndPendingRewards( @@ -933,4 +932,79 @@ describe("StakersDistribution - staking with GD and get Rewards in GDAO", () => return [userMintedReward, userPendingReward]; } + + it("should update user rewards when monthly rate has changed", async () => { + const goodFundManagerFactory = await ethers.getContractFactory( + "GoodFundManager" + ); + + const simpleStaking1 = await deployDaiStaking(); + const ictrl = await ethers.getContractAt( + "Controller", + controller, + schemeMock + ); + const currentBlockNumber = await ethers.provider.getBlockNumber(); + let encodedData = goodFundManagerFactory.interface.encodeFunctionData( + "setStakingReward", + [ + "100000", + simpleStaking1.address, + currentBlockNumber - 5, + currentBlockNumber + 20, + false + ] // set 10 gd per block + ); + + await ictrl.genericCall(goodFundManager.address, encodedData, avatar, 0); + + let [userMintedRewardBeforeStake, userPendingRewardBeforeStake] = + await getUserMintedAndPendingRewards( + staker.address, + simpleStaking1.address + ); + expect(userMintedRewardBeforeStake).to.eq("0"); + expect(userPendingRewardBeforeStake).to.eq("0"); + + const stakingAmount = ethers.utils.parseEther("1000"); + await dai["mint(address,uint256)"](staker.address, stakingAmount); + await dai.connect(staker).approve(simpleStaking1.address, stakingAmount); + await simpleStaking1.connect(staker).stake(stakingAmount, 0, false); + await advanceBlocks(5); //should accumulate some gdao rewards + + const rewardsPerBlockBefore = await stakersDistribution.rewardsPerBlock( + simpleStaking1.address + ); + + // reduce rewarsd speed by half from 2m to 1m + let encoded = stakersDistribution.interface.encodeFunctionData( + "setMonthlyReputationDistribution", + [ethers.utils.parseEther("1000000")] + ); + await genericCall(stakersDistribution.address, encoded); + const rewardsPerBlockAfter = await stakersDistribution.rewardsPerBlock( + simpleStaking1.address + ); + let [userMintedRewardAfterStake, userPendingRewardAfterStake] = + await getUserMintedAndPendingRewards( + staker.address, + simpleStaking1.address + ); + expect(userMintedRewardAfterStake).eq(0); + expect(userPendingRewardAfterStake).gt(0); + + await advanceBlocks(4); //should accumulate some gdao rewards + let [userMintedRewardAfterUpdate, userPendingRewardAfterUpdate] = + await getUserMintedAndPendingRewards( + staker.address, + simpleStaking1.address + ); + expect(userMintedRewardAfterUpdate).eq(0); + // rewards speed has been reduced by half, + expect(rewardsPerBlockAfter).lt(rewardsPerBlockBefore.mul(55).div(100)); + // was forced 4 blocks to pass since reward update + expect(userPendingRewardAfterUpdate).eq( + userPendingRewardAfterStake.add(rewardsPerBlockAfter.mul(4)) + ); + }); }); diff --git a/test/helpers.ts b/test/helpers.ts index 1636faa5..66bdec79 100644 --- a/test/helpers.ts +++ b/test/helpers.ts @@ -301,6 +301,9 @@ export const createDAO = async (tokenType: "super" | "regular" = "super") => { kind: "uups" } ); + + console.log("deploying disthelper..."); + let distHelper = await upgrades.deployProxy( await ethers.getContractFactory("DistributionHelper"), [nameService.address], @@ -438,6 +441,10 @@ export const createDAO = async (tokenType: "super" | "regular" = "super") => { await setDAOAddress("REPUTATION", reputation.address); console.log("setting reserve token..."); + await cDAI["mint(address,uint256)"]( + goodReserve.address, + 10000 + ); await setReserveToken( cDAI.address, "100", //1gd diff --git a/test/reserve/DistributionHelper.test.ts b/test/reserve/DistributionHelper.test.ts index a1a1f988..dea96f0b 100644 --- a/test/reserve/DistributionHelper.test.ts +++ b/test/reserve/DistributionHelper.test.ts @@ -195,7 +195,7 @@ describe("DistributionHelper", () => { expect(dr.transferType).to.equal(2); }); - it("should distribute via fuse bridge", async () => { + it("should not distribute via fuse bridge", async () => { const { distHelper, bridge } = await loadFixture(fixture); const recipient = signers[0]; @@ -215,15 +215,15 @@ describe("DistributionHelper", () => { await genericCall(distHelper.address, encodedCall, avatar.address, 0); await goodDollar.mint(distHelper.address, "100000000000"); - await distHelper.onDistribution("100000000000"); - expect(await goodDollar.balanceOf(bridge.address)).to.equal( - (100000000000 * 2000) / 10000 - ); - - const events = await bridge.queryFilter(bridge.filters.OnToken()); - expect(events[0].args.sender).to.equal(distHelper.address); - expect(events[0].args.amount).to.equal((100000000000 * 2000) / 10000); - expect(events[0].args.data).to.equal(recipient.address.toLowerCase()); + await expect(distHelper.onDistribution("100000000000")).revertedWith("DEPRECATED"); + // expect(await goodDollar.balanceOf(bridge.address)).to.equal( + // (100000000000 * 2000) / 10000 + // ); + + // const events = await bridge.queryFilter(bridge.filters.OnToken()); + // expect(events[0].args.sender).to.equal(distHelper.address); + // expect(events[0].args.amount).to.equal((100000000000 * 2000) / 10000); + // expect(events[0].args.data).to.equal(recipient.address.toLowerCase()); }); it("should distribute via layerzero bridge", async () => { diff --git a/test/reserve/GoodMarketMaker.test.ts b/test/reserve/GoodMarketMaker.test.ts index f36a2685..aa428d8e 100644 --- a/test/reserve/GoodMarketMaker.test.ts +++ b/test/reserve/GoodMarketMaker.test.ts @@ -120,6 +120,22 @@ describe("GoodMarketMaker - calculate gd value at reserve", () => { expect(onecDAIReturn.toNumber() / 100).to.be.equal(10000); //0.0001 cdai is 1 gd, so for 1eth you get 10000 gd (divide by 100 to account for 2 decimals precision) }); + it("should calculate mint UBI correctly for 8 decimals precision when price is 0.0001 and 100% ratio", async () => { + const expansion = await initializeToken( + cdai, + "100", //1gd + "10000", //0.0001 cDai + "1000000" //100% rr + ); + const gdPrice = await marketMaker.currentPrice(cdai); + const toMint = await marketMaker.calculateMintInterest(cdai, "100000000"); + const expectedTotalMinted = 10 ** 8 / gdPrice.toNumber(); //1cdai divided by gd price; + expect(expectedTotalMinted).to.be.equal(10000); //1k GD since price is 0.0001 cdai for 1 gd + expect(toMint.toString()).to.be.equal( + (expectedTotalMinted * 100).toString() + ); //add 2 decimals precision + }); + it("should update reserve ratio by days passed", async () => { const expansion = await marketMaker.reserveRatioDailyExpansion(); // 20% yearly. set up in the constructor @@ -144,16 +160,6 @@ describe("GoodMarketMaker - calculate gd value at reserve", () => { ).to.be.equal("994511"); // 998777 * 0.999388834642296000000000000^7 }); - it("should calculate mint UBI correctly for 8 decimals precision", async () => { - const gdPrice = await marketMaker.currentPrice(cdai); - const toMint = await marketMaker.calculateMintInterest(cdai, "100000000"); - const expectedTotalMinted = 10 ** 8 / gdPrice.toNumber(); //1cdai divided by gd price; - expect(expectedTotalMinted).to.be.equal(10000); //1k GD since price is 0.0001 cdai for 1 gd - expect(toMint.toString()).to.be.equal( - (expectedTotalMinted * 100).toString() - ); //add 2 decimals precision - }); - it("should not return a sell contribution if the given gd is less than the given contribution amount", async () => { let dai = await deployDAIMock(); @@ -181,13 +187,27 @@ describe("GoodMarketMaker - calculate gd value at reserve", () => { }); it("should be able to calculate and update bonding curve gd balance based on oncoming cDAI and the price stays the same", async () => { - const priceBefore = await marketMaker.currentPrice(cdai); + let priceBefore = await marketMaker.currentPrice(cdai); await marketMaker.mintInterest(cdai, BN.from(1e8)); + let priceAfter = await marketMaker.currentPrice(cdai) + + expect(priceAfter).gt(0) + expect(priceBefore).gt(0) expect( Math.floor( - (await marketMaker.currentPrice(cdai).then(_ => _.toNumber())) / 100 - ).toString() + (priceAfter.toNumber() / 100)).toString() ).to.be.equal(Math.floor(priceBefore.toNumber() / 100).toString()); + + + // very large amount of cdai + priceBefore = await marketMaker.currentPrice(cdai); + await marketMaker.mintInterest(cdai, ethers.utils.parseEther("1")); + priceAfter = await marketMaker.currentPrice(cdai) + + console.log({ priceAfter, priceBefore }) + expect(priceAfter).gt(0) + expect(priceBefore).gt(0) + expect(priceAfter).to.eq(priceBefore); }); it("should not be able to mint interest by a non owner", async () => { @@ -273,11 +293,12 @@ describe("GoodMarketMaker - calculate gd value at reserve", () => { "800000" //80% rr ); const price = await marketMaker.currentPrice(dai); - expect(price.toString()).to.be.equal("100000000000000"); //1gd is equal 0.0001 dai = 1000000000000000 wei; + expect(price.toString()).to.be.equal(ethers.utils.parseEther("0.000125")); //1gd is equal 0.0001/1*0.8 = 0.000125 const oneDAIReturn = await marketMaker.buyReturn( dai, ethers.utils.parseEther("1") //1Dai ); + //bancor formula to calcualte return //gd return = gdsupply * ((1+tokenamount/tokensupply)^rr -1) const expectedReturn = 1 * ((1 + 1 / 0.0001) ** 0.8 - 1); @@ -292,16 +313,27 @@ describe("GoodMarketMaker - calculate gd value at reserve", () => { dai, ethers.utils.parseEther("1") ); - const expectedTotalMinted = 10 ** 18 / gdPrice.toNumber(); - // according to the sell formula the gd price should be 10^14 so 10^18 / 10^14 = 10^4 - // Return = _reserveBalance * (1 - (1 - _sellAmount / _supply) ^ (1000000 / _reserveRatio)) - expect(expectedTotalMinted).to.be.equal(10000); + let reserveToken = await marketMaker.reserveTokens(dai); + + //we expect price to stay the same p = reserve/supply*RR + const newPrice = reserveToken.reserveSupply.add(ethers.utils.parseEther("1")).mul(1e8).div(reserveToken.gdSupply.add(toMint).mul(reserveToken.reserveRatio)) + expect(newPrice).equal(gdPrice) + + // the formula is amountToMint = reserveInterest * tokenSupply / reserveBalance + const expectedTotalMinted = 10 ** 18 * reserveToken.gdSupply.toNumber() / reserveToken.reserveSupply.toNumber() + expect(expectedTotalMinted).to.be.equal(1000000); expect(toMint.toString()).to.be.equal( - (expectedTotalMinted * 100).toString() + (expectedTotalMinted).toString() ); }); it("should calculate sell return with cDAI", async () => { + await initializeToken( + cdai, + "1000000000", //1gd + "100000000000", //0.0001 cDai + "900000" //80% rr + ); const gDReturn = await marketMaker.sellReturn( cdai, 10 //0.1 gd @@ -515,19 +547,36 @@ describe("GoodMarketMaker - calculate gd value at reserve", () => { }); it("should calculate amount of gd to mint based on incoming cDAI without effecting bonding curve price", async () => { - const priceBefore = await marketMaker.currentPrice(dai); - const toMint = await marketMaker.calculateMintInterest( + await initializeToken( dai, - BN.from("1000000000000000000") - ); - const totalMinted = 1e18 / priceBefore.toNumber(); - expect(toMint.toString()).to.be.equal( - Math.floor(totalMinted * 100).toString() + 6e11, + ethers.utils.parseEther("600000"), + 5e5 ); + + const priceBefore = await marketMaker.currentPrice(dai); + + await marketMaker.mintInterest(dai, ethers.utils.parseEther("100")) const priceAfter = await marketMaker.currentPrice(dai); expect(priceBefore.toString()).to.be.equal(priceAfter.toString()); }); + it("should calculate amount of gd to mint based on incoming cDAI with small precision issue effecting bonding curve price in very low amounts", async () => { + await initializeToken( + dai, + 600, + 600000000, + 5e5 + ); + + const priceBefore = await marketMaker.currentPrice(dai); + + await marketMaker.mintInterest(dai, 60000) + const priceAfter = await marketMaker.currentPrice(dai); + expect(priceBefore).equal(200000000) + expect(priceAfter).equal(200020000) + }); + it("should not change the reserve ratio when calculate how much decrease it for the reservetoken", async () => { let reserveTokenBefore = await marketMaker.reserveTokens(cdai); let reserveRatioBefore = reserveTokenBefore.reserveRatio; diff --git a/test/staking/StakingRewards.test.ts b/test/staking/StakingRewards.test.ts index a9012815..04486fb0 100644 --- a/test/staking/StakingRewards.test.ts +++ b/test/staking/StakingRewards.test.ts @@ -47,8 +47,10 @@ describe("StakingRewards - staking with cDAI mocks and get Rewards in GoodDollar genericCall, goodCompoundStakingFactory, goodCompoundStakingTestFactory, + runAsAvatarOnly, deployStaking; + before(async () => { [founder, staker, ...signers] = await ethers.getSigners(); schemeMock = signers.pop(); @@ -67,9 +69,11 @@ describe("StakingRewards - staking with cDAI mocks and get Rewards in GoodDollar reserve, setReserveToken, genericCall: gc, - COMP + COMP, + runAsAvatarOnly: raao } = await loadFixture(createDAO); + runAsAvatarOnly = raao const cdaiFactory = await ethers.getContractFactory("cDAIMock"); const cBatFactory = await ethers.getContractFactory("cBATMock"); const goodFundManagerFactory = await ethers.getContractFactory( @@ -447,6 +451,15 @@ describe("StakingRewards - staking with cDAI mocks and get Rewards in GoodDollar expect(gdBalancerAfterWithdraw).to.be.equal(gdBalanceBeforeWithdraw); }); + it("shouldn't be able to collect interest when contract is not active", async () => { + const goodCompoundStaking2 = await deployStaking(null, null, "1728000"); + await expect( + goodFundManager + .connect(staker) + .collectInterest([goodCompoundStaking2.address], true) + ).revertedWith(/not a dao contract/); + }); + // it("should set blacklisted false and mint rewards", async () => { // const goodFundManagerFactory = await ethers.getContractFactory( // "GoodFundManager" @@ -1137,6 +1150,86 @@ describe("StakingRewards - staking with cDAI mocks and get Rewards in GoodDollar expect(sortedArrays[1][0]).to.be.equal(cDAI1.address); }); + // needs to come before next test where we blacklist the goodcompoundstaking contract + it("should revert when colleced interest is not greater than gas cost when 2 months passed [ @skip-on-coverage ]", async () => { + + // make sure expansion is very low + await initializeToken( + cDAI.address, + "129966743722", //1gd + "499870173594", //0.0001 cDai + "1000000" //100% rr + ); + await runAsAvatarOnly( + goodReserve, + "setReserveRatioDailyExpansion(uint256,uint256)", + ethers.utils.parseEther("999999999"), + ethers.utils.parseEther("1000000000") + ); + + const currentBlockNumber = await ethers.provider.getBlockNumber(); + const currentBlock = await ethers.provider.getBlock(currentBlockNumber); + + let encodedData = goodFundManager.interface.encodeFunctionData( + "setStakingReward", + [ + "1000", + goodCompoundStaking.address, + 0, + currentBlockNumber + 1000000, + false + ] // set 10 gd per block + ); + await genericCall(goodFundManager.address, encodedData); + await ethers.provider.send("evm_setNextBlockTimestamp", [ + currentBlock.timestamp + 5185020 + ]); + await ethers.provider.send("evm_mine", []); + console.log(await goodFundManager.activeContracts(0)); + await expect( + goodFundManager.collectInterest([goodCompoundStaking.address], false) + ).revertedWith(/< gas costs/); + }); + + it("should be able to mint ubi when 0 interest but ubi value > interestMultiplier*gas cost when 2 months passed [ @skip-on-coverage ]", async () => { + + await initializeToken( + cDAI.address, + "129966743722", //1gd + "499870173594", //0.0001 cDai + "400000" //100% rr + ); + await runAsAvatarOnly( + goodReserve, + "setReserveRatioDailyExpansion(uint256,uint256)", + ethers.utils.parseEther("999999999"), + ethers.utils.parseEther("1000000000") + ); + + const currentBlockNumber = await ethers.provider.getBlockNumber(); + const currentBlock = await ethers.provider.getBlock(currentBlockNumber); + + let encodedData = goodFundManager.interface.encodeFunctionData( + "setStakingReward", + [ + "1000", + goodCompoundStaking.address, + 0, + currentBlockNumber + 1000000, + false + ] // set 10 gd per block + ); + await genericCall(goodFundManager.address, encodedData); + await ethers.provider.send("evm_setNextBlockTimestamp", [ + currentBlock.timestamp + 5185020 + ]); + await ethers.provider.send("evm_mine", []); + console.log(await goodFundManager.activeContracts(0)); + await expect( + goodFundManager.collectInterest([goodCompoundStaking.address], false) + ).revertedWith(/< gas costs/); + }); + it("It should not be able to calc and sort array when there is no active staking contract", async () => { const goodFundManagerFactory = await ethers.getContractFactory( "GoodFundManager" @@ -1156,22 +1249,6 @@ describe("StakingRewards - staking with cDAI mocks and get Rewards in GoodDollar await genericCall(goodFundManager.address, encodedData, avatar, 0); }); - it("collected interest should be greater than gas cost when 2 months passed", async () => { - const currentBlockNumber = await ethers.provider.getBlockNumber(); - const currentBlock = await ethers.provider.getBlock(currentBlockNumber); - - await ethers.provider.send("evm_setNextBlockTimestamp", [ - currentBlock.timestamp + 5185020 - ]); - await ethers.provider.send("evm_mine", []); - const collectableContracts = await goodFundManager - .calcSortedContracts() - .catch(e => e); - await expect( - goodFundManager.collectInterest([goodCompoundStaking.address], false) - ).revertedWith(/< gas costs/); - }); - it("Avatar should be able to set gd minting gas amount", async () => { const goodFundManagerFactory = await ethers.getContractFactory( "GoodFundManager" @@ -1512,13 +1589,13 @@ describe("StakingRewards - staking with cDAI mocks and get Rewards in GoodDollar ); await bat .connect(staker) - ["approve(address,uint256)"](simpleStaking1.address, stakingAmount); + ["approve(address,uint256)"](simpleStaking1.address, stakingAmount); await bat .connect(signers[0]) - ["approve(address,uint256)"](simpleStaking1.address, stakingAmount); + ["approve(address,uint256)"](simpleStaking1.address, stakingAmount); await bat .connect(signers[1]) - ["approve(address,uint256)"](simpleStaking1.address, stakingAmount); + ["approve(address,uint256)"](simpleStaking1.address, stakingAmount); const stakerOneStakeBlockNumber = (await ethers.provider.getBlockNumber()) + 1; await simpleStaking1["stake(uint256,uint256,bool)"]( @@ -1533,7 +1610,7 @@ describe("StakingRewards - staking with cDAI mocks and get Rewards in GoodDollar (await ethers.provider.getBlockNumber()) + 1; await simpleStaking1 .connect(staker) - ["stake(uint256,uint256,bool)"](stakingAmount.div(5), "0", false); + ["stake(uint256,uint256,bool)"](stakingAmount.div(5), "0", false); const stakerTwoGdBalanceAfterStake = await goodDollar.balanceOf( staker.address ); @@ -1541,7 +1618,7 @@ describe("StakingRewards - staking with cDAI mocks and get Rewards in GoodDollar (await ethers.provider.getBlockNumber()) + 1; await simpleStaking1 .connect(signers[0]) - ["stake(uint256,uint256,bool)"](stakingAmount.div(4), "0", false); + ["stake(uint256,uint256,bool)"](stakingAmount.div(4), "0", false); const stakerThreeGdBalanceAfterStake = await goodDollar.balanceOf( signers[0].address ); @@ -1549,7 +1626,7 @@ describe("StakingRewards - staking with cDAI mocks and get Rewards in GoodDollar (await ethers.provider.getBlockNumber()) + 1; await simpleStaking1 .connect(signers[1]) - ["stake(uint256,uint256,bool)"](stakingAmount.div(10), "0", false); + ["stake(uint256,uint256,bool)"](stakingAmount.div(10), "0", false); const stakerFourGdBalanceAfterStake = await goodDollar.balanceOf( signers[1].address ); @@ -1561,21 +1638,21 @@ describe("StakingRewards - staking with cDAI mocks and get Rewards in GoodDollar ); await simpleStaking1 .connect(staker) - ["withdrawStake(uint256,bool)"](stakingAmount.div(5), false); + ["withdrawStake(uint256,bool)"](stakingAmount.div(5), false); const stakerTwoWithdrawBlockNumber = await ethers.provider.getBlockNumber(); const stakerTwoGdBalanceAfterWithdraw = await goodDollar.balanceOf( staker.address ); await simpleStaking1 .connect(signers[0]) - ["withdrawStake(uint256,bool)"](stakingAmount.div(4), false); + ["withdrawStake(uint256,bool)"](stakingAmount.div(4), false); const stakerThreeGdBalanceAfterWithdraw = await goodDollar.balanceOf( signers[0].address ); await simpleStaking1 .connect(signers[1]) - ["withdrawStake(uint256,bool)"](stakingAmount.div(10), false); + ["withdrawStake(uint256,bool)"](stakingAmount.div(10), false); const stakerFourGdBalanceAfterWithdraw = await goodDollar.balanceOf( signers[1].address ); @@ -1683,7 +1760,7 @@ describe("StakingRewards - staking with cDAI mocks and get Rewards in GoodDollar ); await bat .connect(staker) - ["approve(address,uint256)"](simpleStaking1.address, stakingAmount); + ["approve(address,uint256)"](simpleStaking1.address, stakingAmount); await simpleStaking1["stake(uint256,uint256,bool)"]( stakingAmount, "0", @@ -1695,14 +1772,14 @@ describe("StakingRewards - staking with cDAI mocks and get Rewards in GoodDollar const stakerStakeBlockNumber = (await ethers.provider.getBlockNumber()) + 1; await simpleStaking1 .connect(staker) - ["stake(uint256,uint256,bool)"](stakingAmount.div(20), "0", false); // should get ~0.009 gd each block + ["stake(uint256,uint256,bool)"](stakingAmount.div(20), "0", false); // should get ~0.009 gd each block const stakerGdBalanceAfterStake = await goodDollar.balanceOf( staker.address ); await advanceBlocks(100); await simpleStaking1 .connect(staker) - ["withdrawStake(uint256,bool)"](stakingAmount.div(20), false); + ["withdrawStake(uint256,bool)"](stakingAmount.div(20), false); const stakerWithdrawBlockNumber = await ethers.provider.getBlockNumber(); const stakerGdBalanceAfterWithdraw = await goodDollar.balanceOf( staker.address diff --git a/test/utils/ProtocolUpgrade.test.ts b/test/utils/ProtocolUpgrade.test.ts index 0fad372c..bcf05a0f 100644 --- a/test/utils/ProtocolUpgrade.test.ts +++ b/test/utils/ProtocolUpgrade.test.ts @@ -460,6 +460,7 @@ describe("ProtocolUpgrade - Upgrade old protocol contracts to new ones", () => { "ExchangeHelper", deployment["test-mainnet"].ExchangeHelper ); + await exchangeHelper.setAddresses(); await cDAI["mint(address,uint256)"]( founder.address, ethers.utils.parseUnits("1000", 8) diff --git a/yarn.lock b/yarn.lock index 1c1bbb56..ca788a06 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1436,52 +1436,6 @@ __metadata: languageName: node linkType: hard -"@chainsafe/as-sha256@npm:^0.3.1": - version: 0.3.1 - resolution: "@chainsafe/as-sha256@npm:0.3.1" - checksum: 58ea733be1657b0e31dbf48b0dba862da0833df34a81c1460c7352f04ce90874f70003cbf34d0afb9e5e53a33ee2d63a261a8b12462be85b2ba0a6f7f13d6150 - languageName: node - linkType: hard - -"@chainsafe/persistent-merkle-tree@npm:^0.4.2": - version: 0.4.2 - resolution: "@chainsafe/persistent-merkle-tree@npm:0.4.2" - dependencies: - "@chainsafe/as-sha256": ^0.3.1 - checksum: f9cfcb2132a243992709715dbd28186ab48c7c0c696f29d30857693cca5526bf753974a505ef68ffd5623bbdbcaa10f9083f4dd40bf99eb6408e451cc26a1a9e - languageName: node - linkType: hard - -"@chainsafe/persistent-merkle-tree@npm:^0.5.0": - version: 0.5.0 - resolution: "@chainsafe/persistent-merkle-tree@npm:0.5.0" - dependencies: - "@chainsafe/as-sha256": ^0.3.1 - checksum: 2c67203da776c79cd3a6132e2d672fe132393b2e63dc71604e3134acc8c0ec25cc5e431051545939ea0f7c5ff2066fb806b9e5cab974ca085d046226a1671f7d - languageName: node - linkType: hard - -"@chainsafe/ssz@npm:^0.10.0": - version: 0.10.2 - resolution: "@chainsafe/ssz@npm:0.10.2" - dependencies: - "@chainsafe/as-sha256": ^0.3.1 - "@chainsafe/persistent-merkle-tree": ^0.5.0 - checksum: 6bb70cf741d0a19dd0b28b3f6f067b96fa39f556e2eefa6ac745b21db9c3b3a8393dc3cca8ff4a6ce065ed71ddc3fb1b2b390a92004b9d01067c26e2558e5503 - languageName: node - linkType: hard - -"@chainsafe/ssz@npm:^0.9.2": - version: 0.9.4 - resolution: "@chainsafe/ssz@npm:0.9.4" - dependencies: - "@chainsafe/as-sha256": ^0.3.1 - "@chainsafe/persistent-merkle-tree": ^0.4.2 - case: ^1.6.3 - checksum: c6eaedeae9e5618b3c666ff4507a27647f665a8dcf17d5ca86da4ed4788c5a93868f256d0005467d184fdf35ec03f323517ec2e55ec42492d769540a2ec396bc - languageName: node - linkType: hard - "@colors/colors@npm:1.5.0": version: 1.5.0 resolution: "@colors/colors@npm:1.5.0" @@ -2054,7 +2008,7 @@ __metadata: languageName: node linkType: hard -"@ethersproject/providers@npm:5.7.2, @ethersproject/providers@npm:^5.7.1, @ethersproject/providers@npm:^5.7.2": +"@ethersproject/providers@npm:5.7.2": version: 5.7.2 resolution: "@ethersproject/providers@npm:5.7.2" dependencies: @@ -2429,7 +2383,7 @@ __metadata: fs-extra: 9.0.0 graphql: ^15.5.0 graphql-request: ^3.4.0 - hardhat: ^2.17.3 + hardhat: ^2.* hardhat-contract-sizer: ^2.6.1 hardhat-gas-reporter: ^1.0.8 hardhat-storage-layout: ^0.1.7 @@ -2747,161 +2701,117 @@ __metadata: languageName: node linkType: hard -"@nomicfoundation/ethereumjs-block@npm:5.0.2": - version: 5.0.2 - resolution: "@nomicfoundation/ethereumjs-block@npm:5.0.2" - dependencies: - "@nomicfoundation/ethereumjs-common": 4.0.2 - "@nomicfoundation/ethereumjs-rlp": 5.0.2 - "@nomicfoundation/ethereumjs-trie": 6.0.2 - "@nomicfoundation/ethereumjs-tx": 5.0.2 - "@nomicfoundation/ethereumjs-util": 9.0.2 - ethereum-cryptography: 0.1.3 - ethers: ^5.7.1 - checksum: 7ff744f44a01f1c059ca7812a1cfc8089f87aa506af6cb39c78331dca71b32993cbd6fa05ad03f8c4f4fab73bb998a927af69e0d8ff01ae192ee5931606e09f5 +"@nomicfoundation/edr-darwin-arm64@npm:0.5.2": + version: 0.5.2 + resolution: "@nomicfoundation/edr-darwin-arm64@npm:0.5.2" + checksum: f6ab386603c6e080fe7f611b739eb6d1d6a370220318b725cb582702563577150b3be14b6d0be71cb72bdb854e6992c587ecfc6833216f750eae8e7cd96de46f languageName: node linkType: hard -"@nomicfoundation/ethereumjs-blockchain@npm:7.0.2": - version: 7.0.2 - resolution: "@nomicfoundation/ethereumjs-blockchain@npm:7.0.2" - dependencies: - "@nomicfoundation/ethereumjs-block": 5.0.2 - "@nomicfoundation/ethereumjs-common": 4.0.2 - "@nomicfoundation/ethereumjs-ethash": 3.0.2 - "@nomicfoundation/ethereumjs-rlp": 5.0.2 - "@nomicfoundation/ethereumjs-trie": 6.0.2 - "@nomicfoundation/ethereumjs-tx": 5.0.2 - "@nomicfoundation/ethereumjs-util": 9.0.2 - abstract-level: ^1.0.3 - debug: ^4.3.3 - ethereum-cryptography: 0.1.3 - level: ^8.0.0 - lru-cache: ^5.1.1 - memory-level: ^1.0.0 - checksum: b7e440dcd73e32aa72d13bfd28cb472773c9c60ea808a884131bf7eb3f42286ad594a0864215f599332d800f3fe1f772fff4b138d2dcaa8f41e4d8389bff33e7 +"@nomicfoundation/edr-darwin-x64@npm:0.5.2": + version: 0.5.2 + resolution: "@nomicfoundation/edr-darwin-x64@npm:0.5.2" + checksum: 6f91f6d0294c0450e0501983f1de34a48582fe93f48428bc4b798ac93bb5483a96d626c2b4c62ac91102f00c826a3f9bfa16d748301440ebe1bbb2920ba3ba6d languageName: node linkType: hard -"@nomicfoundation/ethereumjs-common@npm:4.0.2": - version: 4.0.2 - resolution: "@nomicfoundation/ethereumjs-common@npm:4.0.2" - dependencies: - "@nomicfoundation/ethereumjs-util": 9.0.2 - crc-32: ^1.2.0 - checksum: f0d84704d6254d374299c19884312bd5666974b4b6f342d3f10bc76e549de78d20e45a53d25fbdc146268a52335497127e4f069126da7c60ac933a158e704887 +"@nomicfoundation/edr-linux-arm64-gnu@npm:0.5.2": + version: 0.5.2 + resolution: "@nomicfoundation/edr-linux-arm64-gnu@npm:0.5.2" + checksum: bd84cc2741bb2be3c3a60bae9dbb8ca7794a68b8675684c97f2c6e7310e5cba7efd1cf18d392d42481cda83fb478f83c0bd605104c5cf08bab44ec07669c3010 languageName: node linkType: hard -"@nomicfoundation/ethereumjs-ethash@npm:3.0.2": - version: 3.0.2 - resolution: "@nomicfoundation/ethereumjs-ethash@npm:3.0.2" - dependencies: - "@nomicfoundation/ethereumjs-block": 5.0.2 - "@nomicfoundation/ethereumjs-rlp": 5.0.2 - "@nomicfoundation/ethereumjs-util": 9.0.2 - abstract-level: ^1.0.3 - bigint-crypto-utils: ^3.0.23 - ethereum-cryptography: 0.1.3 - checksum: e4011e4019dd9b92f7eeebfc1e6c9a9685c52d8fd0ee4f28f03e50048a23b600c714490827f59fdce497b3afb503b3fd2ebf6815ff307e9949c3efeff1403278 +"@nomicfoundation/edr-linux-arm64-musl@npm:0.5.2": + version: 0.5.2 + resolution: "@nomicfoundation/edr-linux-arm64-musl@npm:0.5.2" + checksum: e7f7d82f16be1b26805bd90964c456aecb4a6a1397e87d507810d37bd383064271fa63932564e725fdb30868925334338ec459fe32f84fc11206644b7b37825c languageName: node linkType: hard -"@nomicfoundation/ethereumjs-evm@npm:2.0.2": - version: 2.0.2 - resolution: "@nomicfoundation/ethereumjs-evm@npm:2.0.2" - dependencies: - "@ethersproject/providers": ^5.7.1 - "@nomicfoundation/ethereumjs-common": 4.0.2 - "@nomicfoundation/ethereumjs-tx": 5.0.2 - "@nomicfoundation/ethereumjs-util": 9.0.2 - debug: ^4.3.3 - ethereum-cryptography: 0.1.3 - mcl-wasm: ^0.7.1 - rustbn.js: ~0.2.0 - checksum: a23cf570836ddc147606b02df568069de946108e640f902358fef67e589f6b371d856056ee44299d9b4e3497f8ae25faa45e6b18fefd90e9b222dc6a761d85f0 +"@nomicfoundation/edr-linux-x64-gnu@npm:0.5.2": + version: 0.5.2 + resolution: "@nomicfoundation/edr-linux-x64-gnu@npm:0.5.2" + checksum: ec025bf75227638b6b2cd01b7ba01b3ddaddf54c4d18d25e9d0364ac621981be2aaf124f4e60a88da5c9e267adb41a660a42668e2d6c9a6a57e55e8671fc76f1 languageName: node linkType: hard -"@nomicfoundation/ethereumjs-rlp@npm:5.0.2": - version: 5.0.2 - resolution: "@nomicfoundation/ethereumjs-rlp@npm:5.0.2" - bin: - rlp: bin/rlp - checksum: a74434cadefca9aa8754607cc1ad7bb4bbea4ee61c6214918e60a5bbee83206850346eb64e39fd1fe97f854c7ec0163e01148c0c881dda23881938f0645a0ef2 +"@nomicfoundation/edr-linux-x64-musl@npm:0.5.2": + version: 0.5.2 + resolution: "@nomicfoundation/edr-linux-x64-musl@npm:0.5.2" + checksum: c9ff47f72645492383b2a598675878abc029b86325e2c457db1b2c4281916e11e4ef6336c355d40ae3c1736595bc43da51cfcf1e923464011f526f4db64c245b languageName: node linkType: hard -"@nomicfoundation/ethereumjs-statemanager@npm:2.0.2": - version: 2.0.2 - resolution: "@nomicfoundation/ethereumjs-statemanager@npm:2.0.2" - dependencies: - "@nomicfoundation/ethereumjs-common": 4.0.2 - "@nomicfoundation/ethereumjs-rlp": 5.0.2 - debug: ^4.3.3 - ethereum-cryptography: 0.1.3 - ethers: ^5.7.1 - js-sdsl: ^4.1.4 - checksum: 3ab6578e252e53609afd98d8ba42a99f182dcf80252f23ed9a5e0471023ffb2502130f85fc47fa7c94cd149f9be799ed9a0942ca52a143405be9267f4ad94e64 +"@nomicfoundation/edr-win32-x64-msvc@npm:0.5.2": + version: 0.5.2 + resolution: "@nomicfoundation/edr-win32-x64-msvc@npm:0.5.2" + checksum: 56da7a1283470dede413cda5f2fef96e10250ec7a25562254ca0cd8045a653212c91e40fbcf37330e7af4e036d3c3aed83ea617831f9c7a5424389c73c53ed4e languageName: node linkType: hard -"@nomicfoundation/ethereumjs-trie@npm:6.0.2": - version: 6.0.2 - resolution: "@nomicfoundation/ethereumjs-trie@npm:6.0.2" +"@nomicfoundation/edr@npm:^0.5.2": + version: 0.5.2 + resolution: "@nomicfoundation/edr@npm:0.5.2" dependencies: - "@nomicfoundation/ethereumjs-rlp": 5.0.2 - "@nomicfoundation/ethereumjs-util": 9.0.2 - "@types/readable-stream": ^2.3.13 - ethereum-cryptography: 0.1.3 - readable-stream: ^3.6.0 - checksum: d4da918d333851b9f2cce7dbd25ab5753e0accd43d562d98fd991b168b6a08d1794528f0ade40fe5617c84900378376fe6256cdbe52c8d66bf4c53293bbc7c40 + "@nomicfoundation/edr-darwin-arm64": 0.5.2 + "@nomicfoundation/edr-darwin-x64": 0.5.2 + "@nomicfoundation/edr-linux-arm64-gnu": 0.5.2 + "@nomicfoundation/edr-linux-arm64-musl": 0.5.2 + "@nomicfoundation/edr-linux-x64-gnu": 0.5.2 + "@nomicfoundation/edr-linux-x64-musl": 0.5.2 + "@nomicfoundation/edr-win32-x64-msvc": 0.5.2 + checksum: 336b1c7cad96fa78410f0c9cc9524abe9fb1e56384fe990b98bfd17f15f25b4665ad8f0525ccd9511f7c19173841fe712d50db993078629e2fc4047fda4665dc languageName: node linkType: hard -"@nomicfoundation/ethereumjs-tx@npm:5.0.2": - version: 5.0.2 - resolution: "@nomicfoundation/ethereumjs-tx@npm:5.0.2" +"@nomicfoundation/ethereumjs-common@npm:4.0.4": + version: 4.0.4 + resolution: "@nomicfoundation/ethereumjs-common@npm:4.0.4" dependencies: - "@chainsafe/ssz": ^0.9.2 - "@ethersproject/providers": ^5.7.2 - "@nomicfoundation/ethereumjs-common": 4.0.2 - "@nomicfoundation/ethereumjs-rlp": 5.0.2 - "@nomicfoundation/ethereumjs-util": 9.0.2 - ethereum-cryptography: 0.1.3 - checksum: 0bbcea75786b2ccb559afe2ecc9866fb4566a9f157b6ffba4f50960d14f4b3da2e86e273f6fadda9b860e67cfcabf589970fb951b328cb5f900a585cd21842a2 + "@nomicfoundation/ethereumjs-util": 9.0.4 + checksum: ce3f6e4ae15b976efdb7ccda27e19aadb62b5ffee209f9503e68b4fd8633715d4d697c0cc10ccd35f5e4e977edd05100d0f214e28880ec64fff77341dc34fcdf languageName: node linkType: hard -"@nomicfoundation/ethereumjs-util@npm:9.0.2": - version: 9.0.2 - resolution: "@nomicfoundation/ethereumjs-util@npm:9.0.2" +"@nomicfoundation/ethereumjs-rlp@npm:5.0.4": + version: 5.0.4 + resolution: "@nomicfoundation/ethereumjs-rlp@npm:5.0.4" + bin: + rlp: bin/rlp.cjs + checksum: ee2c2e5776c73801dc5ed636f4988b599b4563c2d0037da542ea57eb237c69dd1ac555f6bcb5e06f70515b6459779ba0d68252a6e105132b4659ab4bf62919b0 + languageName: node + linkType: hard + +"@nomicfoundation/ethereumjs-tx@npm:5.0.4": + version: 5.0.4 + resolution: "@nomicfoundation/ethereumjs-tx@npm:5.0.4" dependencies: - "@chainsafe/ssz": ^0.10.0 - "@nomicfoundation/ethereumjs-rlp": 5.0.2 + "@nomicfoundation/ethereumjs-common": 4.0.4 + "@nomicfoundation/ethereumjs-rlp": 5.0.4 + "@nomicfoundation/ethereumjs-util": 9.0.4 ethereum-cryptography: 0.1.3 - checksum: 3a08f7b88079ef9f53b43da9bdcb8195498fd3d3911c2feee2571f4d1204656053f058b2f650471c86f7d2d0ba2f814768c7cfb0f266eede41c848356afc4900 + peerDependencies: + c-kzg: ^2.1.2 + peerDependenciesMeta: + c-kzg: + optional: true + checksum: 0f1c87716682ccbcf4d92ffc6cf8ab557e658b90319d82be3219a091a736859f8803c73c98e4863682e3e86d264751c472d33ff6d3c3daf4e75b5f01d0af8fa3 languageName: node linkType: hard -"@nomicfoundation/ethereumjs-vm@npm:7.0.2": - version: 7.0.2 - resolution: "@nomicfoundation/ethereumjs-vm@npm:7.0.2" - dependencies: - "@nomicfoundation/ethereumjs-block": 5.0.2 - "@nomicfoundation/ethereumjs-blockchain": 7.0.2 - "@nomicfoundation/ethereumjs-common": 4.0.2 - "@nomicfoundation/ethereumjs-evm": 2.0.2 - "@nomicfoundation/ethereumjs-rlp": 5.0.2 - "@nomicfoundation/ethereumjs-statemanager": 2.0.2 - "@nomicfoundation/ethereumjs-trie": 6.0.2 - "@nomicfoundation/ethereumjs-tx": 5.0.2 - "@nomicfoundation/ethereumjs-util": 9.0.2 - debug: ^4.3.3 +"@nomicfoundation/ethereumjs-util@npm:9.0.4": + version: 9.0.4 + resolution: "@nomicfoundation/ethereumjs-util@npm:9.0.4" + dependencies: + "@nomicfoundation/ethereumjs-rlp": 5.0.4 ethereum-cryptography: 0.1.3 - mcl-wasm: ^0.7.1 - rustbn.js: ~0.2.0 - checksum: 1c25ba4d0644cadb8a2b0241a4bb02e578bfd7f70e3492b855c2ab5c120cb159cb8f7486f84dc1597884bd1697feedbfb5feb66e91352afb51f3694fd8e4a043 + peerDependencies: + c-kzg: ^2.1.2 + peerDependenciesMeta: + c-kzg: + optional: true + checksum: 754439f72b11cad2d8986707ad020077dcc763c4055f73e2668a0b4cadb22aa4407faa9b3c587d9eb5b97ac337afbe037eb642bc1d5a16197284f83db3462cbe languageName: node linkType: hard @@ -4660,16 +4570,6 @@ __metadata: languageName: node linkType: hard -"@types/readable-stream@npm:^2.3.13": - version: 2.3.15 - resolution: "@types/readable-stream@npm:2.3.15" - dependencies: - "@types/node": "*" - safe-buffer: ~5.1.1 - checksum: ec36f525cad09b6c65a1dafcb5ad99b9e2ed824ec49b7aa23180ac427e5d35b8a0706193ecd79ab4253a283ad485ba03d5917a98daaaa144f0ea34f4823e9d82 - languageName: node - linkType: hard - "@types/resolve@npm:^0.0.8": version: 0.0.8 resolution: "@types/resolve@npm:0.0.8" @@ -4847,7 +4747,7 @@ __metadata: languageName: node linkType: hard -"abstract-level@npm:1.0.3, abstract-level@npm:^1.0.0, abstract-level@npm:^1.0.2, abstract-level@npm:^1.0.3": +"abstract-level@npm:1.0.3": version: 1.0.3 resolution: "abstract-level@npm:1.0.3" dependencies: @@ -5134,6 +5034,15 @@ __metadata: languageName: node linkType: hard +"ansi-align@npm:^3.0.0": + version: 3.0.1 + resolution: "ansi-align@npm:3.0.1" + dependencies: + string-width: ^4.1.0 + checksum: 6abfa08f2141d231c257162b15292467081fa49a208593e055c866aa0455b57f3a86b5a678c190c618faa79b4c59e254493099cb700dd9cf2293c6be2c8f5d8d + languageName: node + linkType: hard + "ansi-colors@npm:3.2.3": version: 3.2.3 resolution: "ansi-colors@npm:3.2.3" @@ -6596,13 +6505,6 @@ __metadata: languageName: node linkType: hard -"bigint-crypto-utils@npm:^3.0.23": - version: 3.3.0 - resolution: "bigint-crypto-utils@npm:3.3.0" - checksum: 9598ce57b23f776c8936d44114c9f051e62b5fa654915b664784cbcbacc5aa0485f4479571c51ff58008abb1210c0d6a234853742f07cf84bda890f2a1e01000 - languageName: node - linkType: hard - "bignumber.js@git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2": version: 2.0.7 resolution: "bignumber.js@https://github.com/debris/bignumber.js.git#commit=94d7146671b9719e00a09c29b01a691bc85048c2" @@ -6810,6 +6712,22 @@ __metadata: languageName: node linkType: hard +"boxen@npm:^5.1.2": + version: 5.1.2 + resolution: "boxen@npm:5.1.2" + dependencies: + ansi-align: ^3.0.0 + camelcase: ^6.2.0 + chalk: ^4.1.0 + cli-boxes: ^2.2.1 + string-width: ^4.2.2 + type-fest: ^0.20.2 + widest-line: ^3.1.0 + wrap-ansi: ^7.0.0 + checksum: 82d03e42a72576ff235123f17b7c505372fe05c83f75f61e7d4fa4bcb393897ec95ce766fecb8f26b915f0f7a7227d66e5ec7cef43f5b2bd9d3aeed47ec55877 + languageName: node + linkType: hard + "brace-expansion@npm:^1.1.7": version: 1.1.11 resolution: "brace-expansion@npm:1.1.11" @@ -6863,18 +6781,6 @@ __metadata: languageName: node linkType: hard -"browser-level@npm:^1.0.1": - version: 1.0.1 - resolution: "browser-level@npm:1.0.1" - dependencies: - abstract-level: ^1.0.2 - catering: ^2.1.1 - module-error: ^1.0.2 - run-parallel-limit: ^1.1.0 - checksum: 67fbc77ce832940bfa25073eccff279f512ad56f545deb996a5b23b02316f5e76f4a79d381acc27eda983f5c9a2566aaf9c97e4fdd0748288c4407307537a29b - languageName: node - linkType: hard - "browser-pack@npm:^6.0.1": version: 6.1.0 resolution: "browser-pack@npm:6.1.0" @@ -7466,6 +7372,13 @@ __metadata: languageName: node linkType: hard +"camelcase@npm:^6.2.0": + version: 6.3.0 + resolution: "camelcase@npm:6.3.0" + checksum: 8c96818a9076434998511251dcb2761a94817ea17dbdc37f47ac080bd088fc62c7369429a19e2178b993497132c8cbcf5cc1f44ba963e76782ba469c0474938d + languageName: node + linkType: hard + "caniuse-lite@npm:^1.0.30000844": version: 1.0.30001562 resolution: "caniuse-lite@npm:1.0.30001562" @@ -7480,13 +7393,6 @@ __metadata: languageName: node linkType: hard -"case@npm:^1.6.3": - version: 1.6.3 - resolution: "case@npm:1.6.3" - checksum: febe73278f910b0d28aab7efd6f51c235f9aa9e296148edb56dfb83fd58faa88308c30ce9a0122b6e53e0362c44f4407105bd5ef89c46860fc2b184e540fd68d - languageName: node - linkType: hard - "caseless@npm:^0.12.0, caseless@npm:~0.12.0": version: 0.12.0 resolution: "caseless@npm:0.12.0" @@ -7494,7 +7400,7 @@ __metadata: languageName: node linkType: hard -"catering@npm:^2.0.0, catering@npm:^2.1.0, catering@npm:^2.1.1": +"catering@npm:^2.0.0, catering@npm:^2.1.0": version: 2.1.1 resolution: "catering@npm:2.1.1" checksum: 205daefa69c935b0c19f3d8f2e0a520dd69aebe9bda55902958003f7c9cff8f967dfb90071b421bd6eb618576f657a89d2bc0986872c9bc04bbd66655e9d4bd6 @@ -7813,20 +7719,6 @@ __metadata: languageName: node linkType: hard -"classic-level@npm:^1.2.0": - version: 1.3.0 - resolution: "classic-level@npm:1.3.0" - dependencies: - abstract-level: ^1.0.2 - catering: ^2.1.0 - module-error: ^1.0.1 - napi-macros: ^2.2.2 - node-gyp: latest - node-gyp-build: ^4.3.0 - checksum: 773da48aef52a041115d413fee8340b357a4da2eb505764f327183b155edd7cc9d24819eb4f707c83dbdae8588024f5dddeb322125567c59d5d1f6f16334cdb9 - languageName: node - linkType: hard - "clean-stack@npm:^2.0.0": version: 2.2.0 resolution: "clean-stack@npm:2.2.0" @@ -7834,6 +7726,13 @@ __metadata: languageName: node linkType: hard +"cli-boxes@npm:^2.2.1": + version: 2.2.1 + resolution: "cli-boxes@npm:2.2.1" + checksum: be79f8ec23a558b49e01311b39a1ea01243ecee30539c880cf14bf518a12e223ef40c57ead0cb44f509bffdffc5c129c746cd50d863ab879385370112af4f585 + languageName: node + linkType: hard + "cli-color@npm:^1.4.0": version: 1.4.0 resolution: "cli-color@npm:1.4.0" @@ -8166,6 +8065,13 @@ __metadata: languageName: node linkType: hard +"commander@npm:^8.1.0": + version: 8.3.0 + resolution: "commander@npm:8.3.0" + checksum: 0f82321821fc27b83bd409510bb9deeebcfa799ff0bf5d102128b500b7af22872c0c92cb6a0ebc5a4cf19c6b550fba9cedfa7329d18c6442a625f851377bacf0 + languageName: node + linkType: hard + "commondir@npm:^1.0.1": version: 1.0.1 resolution: "commondir@npm:1.0.1" @@ -10526,7 +10432,7 @@ __metadata: languageName: node linkType: hard -"ethers@npm:^5.0.0, ethers@npm:^5.0.1, ethers@npm:^5.0.13, ethers@npm:^5.0.2, ethers@npm:^5.5.2, ethers@npm:^5.7.1, ethers@npm:^5.7.2": +"ethers@npm:^5.0.0, ethers@npm:^5.0.1, ethers@npm:^5.0.13, ethers@npm:^5.0.2, ethers@npm:^5.5.2, ethers@npm:^5.7.2": version: 5.7.2 resolution: "ethers@npm:5.7.2" dependencies: @@ -12272,22 +12178,16 @@ __metadata: languageName: node linkType: hard -"hardhat@npm:^2.17.3": - version: 2.17.3 - resolution: "hardhat@npm:2.17.3" +"hardhat@npm:^2.*": + version: 2.22.10 + resolution: "hardhat@npm:2.22.10" dependencies: "@ethersproject/abi": ^5.1.2 "@metamask/eth-sig-util": ^4.0.0 - "@nomicfoundation/ethereumjs-block": 5.0.2 - "@nomicfoundation/ethereumjs-blockchain": 7.0.2 - "@nomicfoundation/ethereumjs-common": 4.0.2 - "@nomicfoundation/ethereumjs-evm": 2.0.2 - "@nomicfoundation/ethereumjs-rlp": 5.0.2 - "@nomicfoundation/ethereumjs-statemanager": 2.0.2 - "@nomicfoundation/ethereumjs-trie": 6.0.2 - "@nomicfoundation/ethereumjs-tx": 5.0.2 - "@nomicfoundation/ethereumjs-util": 9.0.2 - "@nomicfoundation/ethereumjs-vm": 7.0.2 + "@nomicfoundation/edr": ^0.5.2 + "@nomicfoundation/ethereumjs-common": 4.0.4 + "@nomicfoundation/ethereumjs-tx": 5.0.4 + "@nomicfoundation/ethereumjs-util": 9.0.4 "@nomicfoundation/solidity-analyzer": ^0.1.0 "@sentry/node": ^5.18.1 "@types/bn.js": ^5.1.0 @@ -12295,6 +12195,7 @@ __metadata: adm-zip: ^0.4.16 aggregate-error: ^3.0.0 ansi-escapes: ^4.3.0 + boxen: ^5.1.2 chalk: ^2.4.2 chokidar: ^3.4.0 ci-info: ^2.0.0 @@ -12317,7 +12218,7 @@ __metadata: raw-body: ^2.4.1 resolve: 1.17.0 semver: ^6.3.0 - solc: 0.7.3 + solc: 0.8.26 source-map-support: ^0.5.13 stacktrace-parser: ^0.1.10 tsort: 0.0.1 @@ -12334,7 +12235,7 @@ __metadata: optional: true bin: hardhat: internal/cli/bootstrap.js - checksum: 0540ef225b3992749c6b828540a9b9a20b4aaa0f2b4f25556d0769b9f32687593ef0fa9e753496647d772e5d4b07300694d588b13cfb5f7d5fb33ed8238ea9fe + checksum: 2bb961a11f428fd025f990ea18472f4197c8352dd81f4231f27c04b7a8e94bc71d668262475102ae2c339ad83dd0e759b90ac7e4905f043be7bde471c04b5951 languageName: node linkType: hard @@ -13711,13 +13612,6 @@ __metadata: languageName: node linkType: hard -"js-sdsl@npm:^4.1.4": - version: 4.4.2 - resolution: "js-sdsl@npm:4.4.2" - checksum: ba705adc1788bf3c6f6c8e5077824f2bb4f0acab5a984420ce5cc492c7fff3daddc26335ad2c9a67d4f5e3241ec790f9e5b72a625adcf20cf321d2fd85e62b8b - languageName: node - linkType: hard - "js-sha3@npm:0.5.7, js-sha3@npm:^0.5.7": version: 0.5.7 resolution: "js-sha3@npm:0.5.7" @@ -14399,16 +14293,6 @@ __metadata: languageName: node linkType: hard -"level@npm:^8.0.0": - version: 8.0.0 - resolution: "level@npm:8.0.0" - dependencies: - browser-level: ^1.0.1 - classic-level: ^1.2.0 - checksum: 13eb25bd71bfdca6cd714d1233adf9da97de9a8a4bf9f28d62a390b5c96d0250abaf983eb90eb8c4e89c7a985bb330750683d106f12670e5ea8fba1d7e608a1f - languageName: node - linkType: hard - "leveldown@npm:5.6.0, leveldown@npm:^5.4.0": version: 5.6.0 resolution: "leveldown@npm:5.6.0" @@ -14878,15 +14762,6 @@ __metadata: languageName: node linkType: hard -"mcl-wasm@npm:^0.7.1": - version: 0.7.8 - resolution: "mcl-wasm@npm:0.7.8" - dependencies: - typescript: ^4.3.4 - checksum: 811037f38d0fbe19837712cd102e08354064f3c7e5207ce97769013e62085939b5022e37ce50df9248ba3dab1cf3cd18783b72246ac48a9c1fb1019ebce63c06 - languageName: node - linkType: hard - "md5.js@npm:^1.3.4": version: 1.3.5 resolution: "md5.js@npm:1.3.5" @@ -14949,17 +14824,6 @@ __metadata: languageName: node linkType: hard -"memory-level@npm:^1.0.0": - version: 1.0.0 - resolution: "memory-level@npm:1.0.0" - dependencies: - abstract-level: ^1.0.0 - functional-red-black-tree: ^1.0.1 - module-error: ^1.0.1 - checksum: 80b1b7aedaf936e754adbcd7b9303018c3684fb32f9992fd967c448f145d177f16c724fbba9ed3c3590a9475fd563151eae664d69b83d2ad48714852e9fc5c72 - languageName: node - linkType: hard - "memorystream@npm:^0.3.1": version: 0.3.1 resolution: "memorystream@npm:0.3.1" @@ -15612,7 +15476,7 @@ __metadata: languageName: node linkType: hard -"module-error@npm:^1.0.1, module-error@npm:^1.0.2": +"module-error@npm:^1.0.1": version: 1.0.2 resolution: "module-error@npm:1.0.2" checksum: 5d653e35bd55b3e95f8aee2cdac108082ea892e71b8f651be92cde43e4ee86abee4fa8bd7fc3fe5e68b63926d42f63c54cd17b87a560c31f18739295575a3962 @@ -15778,13 +15642,6 @@ __metadata: languageName: node linkType: hard -"napi-macros@npm:^2.2.2": - version: 2.2.2 - resolution: "napi-macros@npm:2.2.2" - checksum: c6f9bd71cdbbc37ddc3535aa5be481238641d89585b8a3f4d301cb89abf459e2d294810432bb7d12056d1f9350b1a0899a5afcf460237a3da6c398cf0fec7629 - languageName: node - linkType: hard - "napi-macros@npm:~2.0.0": version: 2.0.0 resolution: "napi-macros@npm:2.0.0" @@ -18607,15 +18464,6 @@ patch-package@latest: languageName: node linkType: hard -"run-parallel-limit@npm:^1.1.0": - version: 1.1.0 - resolution: "run-parallel-limit@npm:1.1.0" - dependencies: - queue-microtask: ^1.2.2 - checksum: 672c3b87e7f939c684b9965222b361421db0930223ed1e43ebf0e7e48ccc1a022ea4de080bef4d5468434e2577c33b7681e3f03b7593fdc49ad250a55381123c - languageName: node - linkType: hard - "run-parallel@npm:^1.1.9": version: 1.2.0 resolution: "run-parallel@npm:1.2.0" @@ -19385,22 +19233,20 @@ patch-package@latest: languageName: node linkType: hard -"solc@npm:0.7.3": - version: 0.7.3 - resolution: "solc@npm:0.7.3" +"solc@npm:0.8.26": + version: 0.8.26 + resolution: "solc@npm:0.8.26" dependencies: command-exists: ^1.2.8 - commander: 3.0.2 + commander: ^8.1.0 follow-redirects: ^1.12.1 - fs-extra: ^0.30.0 js-sha3: 0.8.0 memorystream: ^0.3.1 - require-from-string: ^2.0.0 semver: ^5.5.0 tmp: 0.0.33 bin: - solcjs: solcjs - checksum: 2d8eb16c6d8f648213c94dc8d977cffe5099cba7d41c82d92d769ef71ae8320a985065ce3d6c306440a85f8e8d2b27fb30bdd3ac38f69e5c1fa0ab8a3fb2f217 + solcjs: solc.js + checksum: e3eaeac76e60676377b357af8f3919d4c8c6a74b74112b49279fe8c74a3dfa1de8afe4788689fc307453bde336edc8572988d2cf9e909f84d870420eb640400c languageName: node linkType: hard @@ -19832,7 +19678,7 @@ patch-package@latest: languageName: node linkType: hard -"string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.2.3": +"string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.0.0, string-width@npm:^4.2.2, string-width@npm:^4.2.3": version: 4.2.3 resolution: "string-width@npm:4.2.3" dependencies: @@ -20997,6 +20843,13 @@ patch-package@latest: languageName: node linkType: hard +"type-fest@npm:^0.20.2": + version: 0.20.2 + resolution: "type-fest@npm:0.20.2" + checksum: 4fb3272df21ad1c552486f8a2f8e115c09a521ad7a8db3d56d53718d0c907b62c6e9141ba5f584af3f6830d0872c521357e512381f24f7c44acae583ad517d73 + languageName: node + linkType: hard + "type-fest@npm:^0.21.3": version: 0.21.3 resolution: "type-fest@npm:0.21.3" @@ -21162,16 +21015,6 @@ patch-package@latest: languageName: node linkType: hard -"typescript@npm:^4.3.4": - version: 4.3.5 - resolution: "typescript@npm:4.3.5" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: bab033b5e2b0790dd35b77fd005df976ef80b8d84fd2c6e63cc31808151875beae9216e5a315fe7068e8499905c3c354248fe83272cdfc13b7705635f0c66c97 - languageName: node - linkType: hard - "typescript@npm:^5.3.3": version: 5.3.3 resolution: "typescript@npm:5.3.3" @@ -21182,16 +21025,6 @@ patch-package@latest: languageName: node linkType: hard -"typescript@patch:typescript@^4.3.4#~builtin": - version: 4.3.5 - resolution: "typescript@patch:typescript@npm%3A4.3.5#~builtin::version=4.3.5&hash=dba6d9" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 365df18cf979c971ef9543b2acaa8694377a803f98e1804c41d0ede0b09d7046cb0cd98f2eaf3884b0fe923c01a60af1f653841bd8805c9715d5479c09a4ebe4 - languageName: node - linkType: hard - "typescript@patch:typescript@^5.3.3#~builtin": version: 5.3.3 resolution: "typescript@patch:typescript@npm%3A5.3.3#~builtin::version=5.3.3&hash=14eedb" @@ -23243,6 +23076,15 @@ patch-package@latest: languageName: node linkType: hard +"widest-line@npm:^3.1.0": + version: 3.1.0 + resolution: "widest-line@npm:3.1.0" + dependencies: + string-width: ^4.0.0 + checksum: 03db6c9d0af9329c37d74378ff1d91972b12553c7d72a6f4e8525fe61563fa7adb0b9d6e8d546b7e059688712ea874edd5ded475999abdeedf708de9849310e0 + languageName: node + linkType: hard + "window-size@npm:^0.2.0": version: 0.2.0 resolution: "window-size@npm:0.2.0"