-
Notifications
You must be signed in to change notification settings - Fork 107
Remote GSM #451
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service 8000 and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Remote GSM #451
Changes from all commits
aaeb183
653f896
21b390e
9f88853
00b5958
0b17427
8926a08
8b12183
e957445
e2a257b
4b2ac50
af63e7f
bef55a1
bfcb5e3
69d162b
3707b75
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,99 @@ | ||||||||||||||||||
// SPDX-License-Identifier: MIT | ||||||||||||||||||
pragma solidity ^0.8.10; | ||||||||||||||||||
|
||||||||||||||||||
import {IERC20} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20.sol'; | ||||||||||||||||||
import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol'; | ||||||||||||||||||
import {VersionedInitializable} from '@aave/core-v3/contracts/protocol/libraries/aave-upgradeability/VersionedInitializable.sol'; | ||||||||||||||||||
import {IGhoReserve} from './interfaces/IGhoReserve.sol'; | ||||||||||||||||||
|
||||||||||||||||||
/** | ||||||||||||||||||
* @title GhoReserve | ||||||||||||||||||
* @author Aave/TokenLogic | ||||||||||||||||||
* @notice It allows approved entities to withdraw and return GHO funds, with a defined maximum withdrawal capacity per entity. | ||||||||||||||||||
* @dev To be covered by a proxy contract. | ||||||||||||||||||
*/ | ||||||||||||||||||
contract GhoReserve is Ownable, VersionedInitializable, IGhoReserve { | ||||||||||||||||||
/// @inheritdoc IGhoReserve | ||||||||||||||||||
address public immutable GHO_TOKEN; | ||||||||||||||||||
|
||||||||||||||||||
/// Map of entities and their assigned capacity and amount of GHO used | ||||||||||||||||||
mapping(address => GhoUsage) private _ghoUsage; | ||||||||||||||||||
|
||||||||||||||||||
/** | ||||||||||||||||||
* @dev Constructor | ||||||||||||||||||
* @param initialOwner The address of the owner | ||||||||||||||||||
* @param ghoAddress The address of the GHO token on the remote chain | ||||||||||||||||||
*/ | ||||||||||||||||||
constructor(address initialOwner, address ghoAddress) Ownable() { | ||||||||||||||||||
require(initialOwner != address(0), 'ZERO_ADDRESS_NOT_VALID'); | ||||||||||||||||||
require(ghoAddress != address(0), 'ZERO_ADDRESS_NOT_VALID'); | ||||||||||||||||||
|
||||||||||||||||||
_transferOwnership(initialOwner); | ||||||||||||||||||
GHO_TOKEN = ghoAddress; | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
/** | ||||||||||||||||||
* @dev Initializer | ||||||||||||||||||
* @param newOwner The address of the new owner | ||||||||||||||||||
*/ | ||||||||||||||||||
function initialize(address newOwner) external initializer { | ||||||||||||||||||
require(newOwner != address(0), 'ZERO_ADDRESS_NOT_VALID'); | ||||||||||||||||||
_transferOwnership(newOwner); | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
/// @inheritdoc IGhoReserve | ||||||||||||||||||
function use(uint256 amount) external { | ||||||||||||||||||
GhoUsage memory usage = _ghoUsage[msg.sender]; | ||||||||||||||||||
require(usage.limit >= usage.used + amount, 'LIMIT_REACHED'); | ||||||||||||||||||
|
||||||||||||||||||
_ghoUsage[msg.sender].used += uint128(amount); | ||||||||||||||||||
Comment on lines
+46
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||
IERC20(GHO_TOKEN).transfer(msg.sender, amount); | ||||||||||||||||||
emit GhoUsed(msg.sender, amount); | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
/// @inheritdoc IGhoReserve | ||||||||||||||||||
function restore(uint256 amount) external { | ||||||||||||||||||
_ghoUsage[msg.sender].used -= uint128(amount); | ||||||||||||||||||
IERC20(GHO_TOKEN).transferFrom(msg.sender, address(this), amount); | ||||||||||||||||||
emit GhoRestored(msg.sender, amount); | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
/// @inheritdoc IGhoReserve | ||||||||||||||||||
function transfer(address to, uint256 amount) external onlyOwner { | ||||||||||||||||||
IERC20(GHO_TOKEN).transfer(to, amount); | ||||||||||||||||||
emit GhoTransferred(to, amount); | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
/// @inheritdoc IGhoReserve | ||||||||||||||||||
function setEntityLimit(address entity, uint256 limit) external onlyOwner { | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||
_ghoUsage[entity].limit = uint128(limit); | ||||||||||||||||||
|
||||||||||||||||||
emit EntityLimitUpdated(entity, limit); | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
/// @inheritdoc IGhoReserve | ||||||||||||||||||
function getUsed(address entity) external view returns (uint256) { | ||||||||||||||||||
return _ghoUsage[entity].used; | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
/// @inheritdoc IGhoReserve | ||||||||||||||||||
function getUsage(address entity) external view returns (uint256, uint256) { | ||||||||||||||||||
GhoUsage memory usage = _ghoUsage[entity]; | ||||||||||||||||||
return (usage.limit, usage.used); | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
/// @inheritdoc IGhoReserve | ||||||||||||||||||
function getLimit(address entity) external view returns (uint256) { | ||||||||||||||||||
return _ghoUsage[entity].limit; | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
efecarranza marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||
/// @inheritdoc IGhoReserve | ||||||||||||||||||
function GHO_REMOTE_RESERVE_REVISION() public pure virtual override returns (uint256) { | ||||||||||||||||||
return 1; | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
/// @inheritdoc VersionedInitializable | ||||||||||||||||||
function getRevision() internal pure virtual override returns (uint256) { | ||||||||||||||||||
return GHO_REMOTE_RESERVE_REVISION(); | ||||||||||||||||||
} | ||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ import {IGhoFacilitator} from '../../gho/interfaces/IGhoFacilitator.sol'; | |
import {IGhoToken} from '../../gho/interfaces/IGhoToken.sol'; | ||
import {IGsmPriceStrategy} from './priceStrategy/interfaces/IGsmPriceStrategy.sol'; | ||
import {IGsm4626} from './interfaces/IGsm4626.sol'; | ||
import {IGhoReserve} from './interfaces/IGhoReserve.sol'; | ||
import {Gsm} from './Gsm.sol'; | ||
|
||
/** | ||
|
@@ -43,14 +44,14 @@ contract Gsm4626 is Gsm, IGsm4626 { | |
) external notSeized onlyRole(CONFIGURATOR_ROLE) returns (uint256) { | ||
require(amount > 0, 'INVALID_AMOUNT'); | ||
|
||
(, uint256 ghoMinted) = IGhoToken(GHO_TOKEN).getFacilitatorBucket(address(this)); | ||
(, uint256 deficit) = _getCurrentBacking(ghoMinted); | ||
uint256 usedGho = _getUsedGho(); | ||
(, uint256 deficit) = _getCurrentBacking(usedGho); | ||
require(deficit > 0, 'NO_CURRENT_DEFICIT_BACKING'); | ||
|
||
uint256 ghoToBack = amount > deficit ? deficit : amount; | ||
|
||
IGhoToken(GHO_TOKEN).transferFrom(msg.sender, address(this), ghoToBack); | ||
IGhoToken(GHO_TOKEN).burn(ghoToBack); | ||
IGhoReserve(_ghoReserve).restore(ghoToBack); | ||
|
||
emit BackingProvided(msg.sender, GHO_TOKEN, ghoToBack, ghoToBack, deficit - ghoToBack); | ||
return ghoToBack; | ||
|
@@ -62,8 +63,7 @@ contract Gsm4626 is Gsm, IGsm4626 { | |
) external notSeized onlyRole(CONFIGURATOR_ROLE) returns (uint256) { | ||
require(amount > 0, 'INVALID_AMOUNT'); | ||
|
||
(, uint256 ghoMinted) = IGhoToken(GHO_TOKEN).getFacilitatorBucket(address(this)); | ||
(, uint256 deficit) = _getCurrentBacking(ghoMinted); | ||
(, uint256 deficit) = _getCurrentBacking(_getUsedGho()); | ||
require(deficit > 0, 'NO_CURRENT_DEFICIT_BACKING'); | ||
|
||
uint128 deficitInUnderlying = IGsmPriceStrategy(PRICE_STRATEGY) | ||
|
@@ -95,8 +95,7 @@ contract Gsm4626 is Gsm, IGsm4626 { | |
|
||
/// @inheritdoc IGsm4626 | ||
function getCurrentBacking() external view returns (uint256, uint256) { | ||
(, uint256 ghoMinted) = IGhoToken(GHO_TOKEN).getFacilitatorBucket(address(this)); | ||
return _getCurrentBacking(ghoMinted); | ||
return _getCurrentBacking(_getUsedGho()); | ||
} | ||
|
||
/// @inheritdoc IGhoFacilitator | ||
|
@@ -116,36 +115,35 @@ contract Gsm4626 is Gsm, IGsm4626 { | |
/** | ||
* @dev Cumulates yield in form of GHO, aimed to be redirected to the treasury | ||
* @dev It mints GHO backed by the excess of underlying produced by the ERC4626 yield | ||
* @dev If the GHO amount exceeds the amount available, it will mint up to the remaining capacity | ||
* @dev If the GHO amount exceeds the amount available, it will mint up to the remaining limit | ||
*/ | ||
function _cumulateYieldInGho() internal { | ||
(uint256 ghoCapacity, uint256 ghoLevel) = IGhoToken(GHO_TOKEN).getFacilitatorBucket( | ||
address(this) | ||
); | ||
uint256 ghoAvailableToMint = ghoCapacity > ghoLevel ? ghoCapacity - ghoLevel : 0; | ||
uint256 ghoLevel = _getUsedGho(); | ||
uint256 ghoLimit = _getLimit(); | ||
Comment on lines
+121
to
+122
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should we create an internal function |
||
uint256 ghoAvailableToMint = ghoLimit > ghoLevel ? ghoLimit - ghoLevel : 0; | ||
(uint256 ghoExcess, ) = _getCurrentBacking(ghoLevel); | ||
if (ghoExcess > 0 && ghoAvailableToMint > 0) { | ||
ghoExcess = ghoExcess > ghoAvailableToMint ? ghoAvailableToMint : ghoExcess; | ||
_accruedFees += uint128(ghoExcess); | ||
IGhoToken(GHO_TOKEN).mint(address(this), ghoExcess); | ||
IGhoReserve(_ghoReserve).use(ghoExcess); | ||
} | ||
} | ||
|
||
/** | ||
* @dev Calculates the excess or deficit of GHO minted, reflective of GSM backing | ||
* @param ghoMinted The amount of GHO currently minted by the GSM | ||
* @return The excess amount of GHO minted, relative to the value of the underlying | ||
* @return The deficit of GHO minted, relative to the value of the underlying | ||
* @param usedGho The amount of GHO currently used by the GSM | ||
* @return The excess amount of GHO used, relative to the value of the underlying | ||
* @return The deficit of GHO used, relative to the value of the underlying | ||
*/ | ||
function _getCurrentBacking(uint256 ghoMinted) internal view returns (uint256, uint256) { | ||
function _getCurrentBacking(uint256 usedGho) internal view returns (uint256, uint256) { | ||
uint256 ghoToBack = IGsmPriceStrategy(PRICE_STRATEGY).getAssetPriceInGho( | ||
_currentExposure, | ||
false | ||
); | ||
if (ghoToBack >= ghoMinted) { | ||
return (ghoToBack - ghoMinted, 0); | ||
if (ghoToBack >= usedGho) { | ||
return (ghoToBack - usedGho, 0); | ||
} else { | ||
return (0, ghoMinted - ghoToBack); | ||
return (0, usedGho - ghoToBack); | ||
} | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
8000The reason will be displayed to describe this comment to others. Learn more.
Wondering if we should add global variables for total limit and total used. It's a bit hard to iterate over entities also, to calculate totals
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would total limit be every time we set a limit +/- to the global variable? For used it's easy to keep track
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, would be keeping global accounting every time an entity updates its usage (limit or used). Not sure it's completely needed