Skip to content

Commit

Permalink
Reserve Updates (#272)
Browse files Browse the repository at this point in the history
* fix: make sure GOOD rewards are updated on change

* fix: limit amount of G$ to sell for fees

* fix: dont count on fundmanager and exchange helper for funds transfer

* fix: dont allow untrusted staking contracts

* fix: dont allow transfer of hacked funds

* add: hack fix upgrade script

* add: test for reward update fix

* add: celo distribution helper

* add: basic upgrade script

* Revert "fix: dont count on fundmanager and exchange helper for funds transfer"

This reverts commit deeec59.

* Revert "fix: dont allow transfer of hacked funds"

This reverts commit a88a438.

* revert: initial reserve deposit

* fix: reset so other test file dont break

* fix: e2e testing issue

* add: todo

* add: mento upgrade simulation and contract

* add: mint ubi calculation fixes and guardian proposal approval

* fix: dont count on fundmanager and exchange helper for funds transfer

* add: only trust reservesupply for ubi minting

* fix: new var storage location

* add: eth reserve restore upgrade

* add: audit fixes

* add: fix tests

* fix: celodisthelper sell gd

* fix: celodist helper tests

* fix: failing test

* fix: coverage

* add: use transferfrom to prevent dai lock. upgrade fuse governance

* add: remove mento upgrade into separate PR

* add: removed outdated todo

* fix: code review use constant for repeating addresses

* fix: remove redundant require

* add: allow to restore reserve in batches
  • Loading branch information
sirpy authored Dec 5, 2024
1 parent c2865d5 commit bba0de1
Show file tree
Hide file tree
Showing 27 changed files with 1,612 additions and 680 deletions.
6 changes: 6 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@
"bracketSpacing": true,
"explicitTypes": "always"
}
},
{
"files": "test/**/*.ts",
"options": {
"printWidth": 80
}
}
],
"printWidth": 120,
Expand Down
122 changes: 122 additions & 0 deletions contracts/Interfaces.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
);
}
37 changes: 26 additions & 11 deletions contracts/governance/CompoundVotingMachine.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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%
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 (
Expand All @@ -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];
}

Expand Down Expand Up @@ -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(
Expand Down
26 changes: 13 additions & 13 deletions contracts/governance/StakersDistribution.sol
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ contract StakersDistribution is
) external {
_onlyAvatar();
monthlyReputationDistribution = newMonthlyReputationDistribution;
_updateRewards();
}

/**
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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++) {
(
Expand Down Expand Up @@ -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++) {
Expand Down
39 changes: 29 additions & 10 deletions contracts/reserve/DistributionHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ contract DistributionHelper is
0x467719aD09025FcC6cF6F8311755809d45a5E5f3;

enum TransferType {
FuseBridge,
DEPRECATED_FuseBridge,
LayerZeroBridge,
AxelarBridge,
Contract
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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");
Expand Down
Loading

0 comments on commit bba0de1

Please sign in to comment.