-
Notifications
You must be signed in to change notification settings - Fork 601
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 and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sip 185 debt shares #1601
Sip 185 debt shares #1601
Changes from 3 commits
6522f12
510823f
8ed2fff
834ec9d
bff2fb6
9043de1
ae5c111
d532380
3b53405
5076ab1
e351a65
f1280ab
fea5c91
10ea7bd
7cf745b
113ada3
f267e6e
667fcc8
2265bf6
fb1530a
bdf3e69
87cd0da
8171cb3
1deac59
e961d51
09c501e
54bc7b2
26bddc0
093812a
7b052d8
074f973
3c10f8d
f70d5e3
9005af7
a4e67b9
7d91e8a
832fe23
1b12e4c
3783e05
6104a06
1c8d00c
da67a27
21e76b9
63dc62b
d8aeb25
9e35b2b
f3537b9
afdefac
49d8a69
dc3c276
804a96b
19eb553
331cb88
82c0c70
43b6fe1
fe3e180
ebdb8b0
86bde07
1053059
d0c69d5
b9add27
15202c1
b8e0e9a
dd7961b
9b54331
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 |
---|---|---|
|
@@ -16,11 +16,10 @@ import "./interfaces/IERC20.sol"; | |
import "./interfaces/ISynth.sol"; | ||
import "./interfaces/ISystemStatus.sol"; | ||
import "./interfaces/ISynthetix.sol"; | ||
import "./FeePoolState.sol"; | ||
import "./interfaces/ISynthetixDebtShare.sol"; | ||
import "./FeePoolEternalStorage.sol"; | ||
import "./interfaces/IExchanger.sol"; | ||
import "./interfaces/IIssuer.sol"; | ||
import "./interfaces/ISynthetixState.sol"; | ||
import "./interfaces/IRewardEscrowV2.sol"; | ||
import "./interfaces/IDelegateApprovals.sol"; | ||
import "./interfaces/IRewardsDistribution.sol"; | ||
|
@@ -44,7 +43,6 @@ contract FeePool is Owned, Proxyable, LimitedSetup, MixinSystemSettings, IFeePoo | |
// This struct represents the issuance activity that's happened in a fee period. | ||
struct FeePeriod { | ||
uint64 feePeriodId; | ||
uint64 startingDebtIndex; | ||
uint64 startTime; | ||
uint feesToDistribute; | ||
uint feesClaimed; | ||
|
@@ -66,11 +64,10 @@ contract FeePool is Owned, Proxyable, LimitedSetup, MixinSystemSettings, IFeePoo | |
|
||
bytes32 private constant CONTRACT_SYSTEMSTATUS = "SystemStatus"; | ||
bytes32 private constant CONTRACT_SYNTHETIX = "Synthetix"; | ||
bytes32 private constant CONTRACT_FEEPOOLSTATE = "FeePoolState"; | ||
bytes32 private constant CONTRACT_SYNTHETIXDEBTSHARE = "SynthetixDebtShare"; | ||
bytes32 private constant CONTRACT_FEEPOOLETERNALSTORAGE = "FeePoolEternalStorage"; | ||
bytes32 private constant CONTRACT_EXCHANGER = "Exchanger"; | ||
bytes32 private constant CONTRACT_ISSUER = "Issuer"; | ||
bytes32 private constant CONTRACT_SYNTHETIXSTATE = "SynthetixState"; | ||
bytes32 private constant CONTRACT_REWARDESCROW_V2 = "RewardEscrowV2"; | ||
bytes32 private constant CONTRACT_DELEGATEAPPROVALS = "DelegateApprovals"; | ||
bytes32 private constant CONTRACT_COLLATERALMANAGER = "CollateralManager"; | ||
|
@@ -95,20 +92,19 @@ contract FeePool is Owned, Proxyable, LimitedSetup, MixinSystemSettings, IFeePoo | |
/* ========== VIEWS ========== */ | ||
function resolverAddressesRequired() public view returns (bytes32[] memory addresses) { | ||
bytes32[] memory existingAddresses = MixinSystemSettings.resolverAddressesRequired(); | ||
bytes32[] memory newAddresses = new bytes32[](13); | ||
bytes32[] memory newAddresses = new bytes32[](12); | ||
newAddresses[0] = CONTRACT_SYSTEMSTATUS; | ||
newAddresses[1] = CONTRACT_SYNTHETIX; | ||
newAddresses[2] = CONTRACT_FEEPOOLSTATE; | ||
newAddresses[2] = CONTRACT_SYNTHETIXDEBTSHARE; | ||
newAddresses[3] = CONTRACT_FEEPOOLETERNALSTORAGE; | ||
newAddresses[4] = CONTRACT_EXCHANGER; | ||
newAddresses[5] = CONTRACT_ISSUER; | ||
newAddresses[6] = CONTRACT_SYNTHETIXSTATE; | ||
newAddresses[7] = CONTRACT_REWARDESCROW_V2; | ||
newAddresses[8] = CONTRACT_DELEGATEAPPROVALS; | ||
newAddresses[9] = CONTRACT_REWARDSDISTRIBUTION; | ||
newAddresses[10] = CONTRACT_COLLATERALMANAGER; | ||
newAddresses[11] = CONTRACT_WRAPPER_FACTORY; | ||
newAddresses[12] = CONTRACT_ETHER_WRAPPER; | ||
newAddresses[6] = CONTRACT_REWARDESCROW_V2; | ||
newAddresses[7] = CONTRACT_DELEGATEAPPROVALS; | ||
newAddresses[8] = CONTRACT_REWARDSDISTRIBUTION; | ||
newAddresses[9] = CONTRACT_COLLATERALMANAGER; | ||
newAddresses[10] = CONTRACT_WRAPPER_FACTORY; | ||
newAddresses[11] = CONTRACT_ETHER_WRAPPER; | ||
addresses = combineArrays(existingAddresses, newAddresses); | ||
} | ||
|
||
|
@@ -120,8 +116,8 @@ contract FeePool is Owned, Proxyable, LimitedSetup, MixinSystemSettings, IFeePoo | |
return ISynthetix(requireAndGetAddress(CONTRACT_SYNTHETIX)); | ||
} | ||
|
||
function feePoolState() internal view returns (FeePoolState) { | ||
return FeePoolState(requireAndGetAddress(CONTRACT_FEEPOOLSTATE)); | ||
function synthetixDebtShare() internal view returns (ISynthetixDebtShare) { | ||
return ISynthetixDebtShare(requireAndGetAddress(CONTRACT_SYNTHETIXDEBTSHARE)); | ||
} | ||
|
||
function feePoolEternalStorage() internal view returns (FeePoolEternalStorage) { | ||
|
@@ -140,10 +136,6 @@ contract FeePool is Owned, Proxyable, LimitedSetup, MixinSystemSettings, IFeePoo | |
return IIssuer(requireAndGetAddress(CONTRACT_ISSUER)); | ||
} | ||
|
||
function synthetixState() internal view returns (ISynthetixState) { | ||
return ISynthetixState(requireAndGetAddress(CONTRACT_SYNTHETIXSTATE)); | ||
} | ||
|
||
function rewardEscrowV2() internal view returns (IRewardEscrowV2) { | ||
return IRewardEscrowV2(requireAndGetAddress(CONTRACT_REWARDESCROW_V2)); | ||
} | ||
|
@@ -181,7 +173,6 @@ contract FeePool is Owned, Proxyable, LimitedSetup, MixinSystemSettings, IFeePoo | |
view | ||
returns ( | ||
uint64 feePeriodId, | ||
uint64 startingDebtIndex, | ||
uint64 startTime, | ||
uint feesToDistribute, | ||
uint feesClaimed, | ||
|
@@ -192,7 +183,6 @@ contract FeePool is Owned, Proxyable, LimitedSetup, MixinSystemSettings, IFeePoo | |
FeePeriod memory feePeriod = _recentFeePeriodsStorage(index); | ||
return ( | ||
feePeriod.feePeriodId, | ||
feePeriod.startingDebtIndex, | ||
feePeriod.startTime, | ||
feePeriod.feesToDistribute, | ||
feePeriod.feesClaimed, | ||
|
@@ -205,31 +195,6 @@ contract FeePool is Owned, Proxyable, LimitedSetup, MixinSystemSettings, IFeePoo | |
return _recentFeePeriods[(_currentFeePeriod + index) % FEE_PERIOD_LENGTH]; | ||
} | ||
|
||
/* ========== MUTATIVE FUNCTIONS ========== */ | ||
|
||
/** | ||
* @notice Logs an accounts issuance data per fee period | ||
* @param account Message.Senders account address | ||
* @param debtRatio Debt percentage this account has locked after minting or burning their synth | ||
* @param debtEntryIndex The index in the global debt ledger. synthetixState.issuanceData(account) | ||
* @dev onlyIssuer to call me on synthetix.issue() & synthetix.burn() calls to store the locked SNX | ||
* per fee period so we know to allocate the correct proportions of fees and rewards per period | ||
*/ | ||
function appendAccountIssuanceRecord( | ||
address account, | ||
uint debtRatio, | ||
uint debtEntryIndex | ||
) external onlyIssuerAndSynthetixState { | ||
feePoolState().appendAccountIssuanceRecord( | ||
account, | ||
debtRatio, | ||
debtEntryIndex, | ||
_recentFeePeriodsStorage(0).startingDebtIndex | ||
); | ||
|
||
emitIssuanceDebtRatioEntry(account, debtRatio, debtEntryIndex, _recentFeePeriodsStorage(0).startingDebtIndex); | ||
} | ||
|
||
/** | ||
* @notice The Exchanger contract informs us when fees are paid. | ||
* @param amount susd amount in fees being paid. | ||
|
@@ -286,9 +251,11 @@ contract FeePool is Owned, Proxyable, LimitedSetup, MixinSystemSettings, IFeePoo | |
// Open up the new fee period. | ||
// Increment periodId from the recent closed period feePeriodId | ||
_recentFeePeriodsStorage(0).feePeriodId = uint64(uint256(_recentFeePeriodsStorage(1).feePeriodId).add(1)); | ||
_recentFeePeriodsStorage(0).startingDebtIndex = uint64(synthetixState().debtLedgerLength()); | ||
_recentFeePeriodsStorage(0).startTime = uint64(now); | ||
|
||
// Inform Issuer to start recording for the new fee period | ||
issuer().setCurrentPeriodId(now); | ||
|
||
emitFeePeriodClosed(_recentFeePeriodsStorage(1).feePeriodId); | ||
} | ||
|
||
|
@@ -363,18 +330,14 @@ contract FeePool is Owned, Proxyable, LimitedSetup, MixinSystemSettings, IFeePoo | |
function importFeePeriod( | ||
uint feePeriodIndex, | ||
uint feePeriodId, | ||
uint startingDebtIndex, | ||
uint startTime, | ||
uint feesToDistribute, | ||
uint feesClaimed, | ||
uint rewardsToDistribute, | ||
uint rewardsClaimed | ||
) public optionalProxy_onlyOwner onlyDuringSetup { | ||
barrasso marked this conversation as resolved.
Show resolved
Hide resolved
|
||
require(startingDebtIndex <= synthetixState().debtLedgerLength(), "Cannot import bad data"); | ||
|
||
_recentFeePeriods[_currentFeePeriod.add(feePeriodIndex).mod(FEE_PERIOD_LENGTH)] = FeePeriod({ | ||
feePeriodId: uint64(feePeriodId), | ||
startingDebtIndex: uint64(startingDebtIndex), | ||
startTime: uint64(startTime), | ||
feesToDistribute: feesToDistribute, | ||
feesClaimed: feesClaimed, | ||
|
@@ -580,15 +543,14 @@ contract FeePool is Owned, Proxyable, LimitedSetup, MixinSystemSettings, IFeePoo | |
function feesByPeriod(address account) public view returns (uint[2][FEE_PERIOD_LENGTH] memory results) { | ||
// What's the user's debt entry index and the debt they owe to the system at current feePeriod | ||
uint userOwnershipPercentage; | ||
uint debtEntryIndex; | ||
FeePoolState _feePoolState = feePoolState(); | ||
ISynthetixDebtShare _debtShare = synthetixDebtShare(); | ||
|
||
(userOwnershipPercentage, debtEntryIndex) = _feePoolState.getAccountsDebtEntry(account, 0); | ||
userOwnershipPercentage = _debtShare.sharePercent(account); | ||
|
||
// If they don't have any debt ownership and they never minted, they don't have any fees. | ||
// User ownership can reduce to 0 if user burns all synths, | ||
// however they could have fees applicable for periods they had minted in before so we check debtEntryIndex. | ||
if (debtEntryIndex == 0 && userOwnershipPercentage == 0) { | ||
if (userOwnershipPercentage == 0) { | ||
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. @dbeal-eth Have to be careful here that they can still claim previous fee periods even if they now have burned all their debt / no current debt shares %. This is why it checked that their 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. is there a unit test, or an easy way to write one? 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. I think best way to test is
|
||
uint[2][FEE_PERIOD_LENGTH] memory nullResults; | ||
return nullResults; | ||
} | ||
|
@@ -597,7 +559,7 @@ contract FeePool is Owned, Proxyable, LimitedSetup, MixinSystemSettings, IFeePoo | |
// fees owing for, so we need to report on it anyway. | ||
uint feesFromPeriod; | ||
uint rewardsFromPeriod; | ||
(feesFromPeriod, rewardsFromPeriod) = _feesAndRewardsFromPeriod(0, userOwnershipPercentage, debtEntryIndex); | ||
(feesFromPeriod, rewardsFromPeriod) = _feesAndRewardsFromPeriod(0, userOwnershipPercentage); | ||
|
||
results[0][0] = feesFromPeriod; | ||
results[0][1] = rewardsFromPeriod; | ||
|
@@ -608,22 +570,16 @@ contract FeePool is Owned, Proxyable, LimitedSetup, MixinSystemSettings, IFeePoo | |
// Go through our fee periods from the oldest feePeriod[FEE_PERIOD_LENGTH - 1] and figure out what we owe them. | ||
// Condition checks for periods > 0 | ||
for (uint i = FEE_PERIOD_LENGTH - 1; i > 0; i--) { | ||
uint next = i - 1; | ||
uint nextPeriodStartingDebtIndex = _recentFeePeriodsStorage(next).startingDebtIndex; | ||
|
||
// We can skip the period, as no debt minted during period (next period's startingDebtIndex is still 0) | ||
dbeal-eth marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (nextPeriodStartingDebtIndex > 0 && lastFeeWithdrawal < _recentFeePeriodsStorage(i).feePeriodId) { | ||
// We calculate a feePeriod's closingDebtIndex by looking at the next feePeriod's startingDebtIndex | ||
// we can use the most recent issuanceData[0] for the current feePeriod | ||
// else find the applicableIssuanceData for the feePeriod based on the StartingDebtIndex of the period | ||
uint closingDebtIndex = uint256(nextPeriodStartingDebtIndex).sub(1); | ||
if (lastFeeWithdrawal < _recentFeePeriodsStorage(i).feePeriodId) { | ||
|
||
// Gas optimisation - to reuse debtEntryIndex if found new applicable one | ||
dbeal-eth marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// if applicable is 0,0 (none found) we keep most recent one from issuanceData[0] | ||
// return if userOwnershipPercentage = 0) | ||
(userOwnershipPercentage, debtEntryIndex) = _feePoolState.applicableIssuanceData(account, closingDebtIndex); | ||
userOwnershipPercentage = _debtShare.sharePercentOnPeriod(account, uint(_recentFeePeriodsStorage(i).startTime)); | ||
|
||
(feesFromPeriod, rewardsFromPeriod) = _feesAndRewardsFromPeriod(i, userOwnershipPercentage, debtEntryIndex); | ||
(feesFromPeriod, rewardsFromPeriod) = _feesAndRewardsFromPeriod(i, userOwnershipPercentage); | ||
|
||
results[i][0] = feesFromPeriod; | ||
results[i][1] = rewardsFromPeriod; | ||
|
@@ -641,62 +597,29 @@ contract FeePool is Owned, Proxyable, LimitedSetup, MixinSystemSettings, IFeePoo | |
*/ | ||
function _feesAndRewardsFromPeriod( | ||
uint period, | ||
uint ownershipPercentage, | ||
uint debtEntryIndex | ||
uint ownershipPercentage | ||
) internal view returns (uint, uint) { | ||
// If it's zero, they haven't issued, and they have no fees OR rewards. | ||
if (ownershipPercentage == 0) return (0, 0); | ||
|
||
uint debtOwnershipForPeriod = ownershipPercentage; | ||
|
||
// If period has closed we want to calculate debtPercentage for the period | ||
if (period > 0) { | ||
uint closingDebtIndex = uint256(_recentFeePeriodsStorage(period - 1).startingDebtIndex).sub(1); | ||
debtOwnershipForPeriod = _effectiveDebtRatioForPeriod(closingDebtIndex, ownershipPercentage, debtEntryIndex); | ||
} | ||
|
||
// Calculate their percentage of the fees / rewards in this period | ||
// This is a high precision integer. | ||
uint feesFromPeriod = _recentFeePeriodsStorage(period).feesToDistribute.multiplyDecimal(debtOwnershipForPeriod); | ||
uint feesFromPeriod = _recentFeePeriodsStorage(period).feesToDistribute.multiplyDecimal(ownershipPercentage); | ||
|
||
uint rewardsFromPeriod = | ||
_recentFeePeriodsStorage(period).rewardsToDistribute.multiplyDecimal(debtOwnershipForPeriod); | ||
_recentFeePeriodsStorage(period).rewardsToDistribute.multiplyDecimal(ownershipPercentage); | ||
|
||
return (feesFromPeriod.preciseDecimalToDecimal(), rewardsFromPeriod.preciseDecimalToDecimal()); | ||
} | ||
|
||
function _effectiveDebtRatioForPeriod( | ||
uint closingDebtIndex, | ||
uint ownershipPercentage, | ||
uint debtEntryIndex | ||
) internal view returns (uint) { | ||
// Figure out their global debt percentage delta at end of fee Period. | ||
// This is a high precision integer. | ||
ISynthetixState _synthetixState = synthetixState(); | ||
uint feePeriodDebtOwnership = | ||
_synthetixState | ||
.debtLedger(closingDebtIndex) | ||
.divideDecimalRoundPrecise(_synthetixState.debtLedger(debtEntryIndex)) | ||
.multiplyDecimalRoundPrecise(ownershipPercentage); | ||
|
||
return feePeriodDebtOwnership; | ||
return (feesFromPeriod, rewardsFromPeriod); | ||
} | ||
|
||
function effectiveDebtRatioForPeriod(address account, uint period) external view returns (uint) { | ||
require(period != 0, "Current period is not closed yet"); | ||
require(period < FEE_PERIOD_LENGTH, "Exceeds the FEE_PERIOD_LENGTH"); | ||
|
||
// If the period being checked is uninitialised then return 0. This is only at the start of the system. | ||
if (_recentFeePeriodsStorage(period - 1).startingDebtIndex == 0) return 0; | ||
|
||
uint closingDebtIndex = uint256(_recentFeePeriodsStorage(period - 1).startingDebtIndex).sub(1); | ||
|
||
uint ownershipPercentage; | ||
uint debtEntryIndex; | ||
(ownershipPercentage, debtEntryIndex) = feePoolState().applicableIssuanceData(account, closingDebtIndex); | ||
if (_recentFeePeriodsStorage(period - 1).startTime == 0) return 0; | ||
|
||
// internal function will check closingDebtIndex has corresponding debtLedger entry | ||
return _effectiveDebtRatioForPeriod(closingDebtIndex, ownershipPercentage, debtEntryIndex); | ||
return synthetixDebtShare().sharePercentOnPeriod(account, uint(_recentFeePeriods[period].startTime)); | ||
} | ||
|
||
/** | ||
|
@@ -739,10 +662,9 @@ contract FeePool is Owned, Proxyable, LimitedSetup, MixinSystemSettings, IFeePoo | |
_; | ||
} | ||
|
||
modifier onlyIssuerAndSynthetixState { | ||
modifier onlyIssuer { | ||
bool isIssuer = msg.sender == address(issuer()); | ||
bool isSynthetixState = msg.sender == address(synthetixState()); | ||
require(isIssuer || isSynthetixState, "Issuer and SynthetixState only"); | ||
require(isIssuer, "Issuer only"); | ||
_; | ||
} | ||
|
||
dbeal-eth marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
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.
Why not check whether the account has any debt shares balance (directly from the SDS contract) and if 0, you could save more gas and skip this check for transferring ?
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.
I think I just wanted to reuse the
issuer
dependency. You are right, checking the debt shares contract directly would make more sense.