Skip to content
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 366 implementation #2145

Open
wants to merge 59 commits into
base: main
Choose a base branch
from
Open

Sip 366 implementation #2145

wants to merge 59 commits into from

Conversation

leomassazza
Copy link
Contributor

No description provided.

@leomassazza leomassazza self-assigned this May 9, 2024
@leomassazza leomassazza marked this pull request as ready for review May 20, 2024 20:48
@@ -56,14 +164,181 @@ interface IVaultModule {
*
* Emits a {DelegationUpdated} event.
*/
function delegateCollateral(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to have the interfaces set up where atomic delegation can still be possible and the interface doesn't change? Like this function could be the "process" function if necessary a delay is set?

There's also an abstraction where everything is async, and atomic can be done with a batch of two tx's when the delay is configured to 0.

Copy link
Member

@kaleb-keny kaleb-keny Jun 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i believe switching the delay and window to 0 leads to atomic processing with the same interface.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i have same feedback

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hey @dbeal-eth can the legacy market migration be retrofitted for this sip? Want to avoid a situation of having unexpected behavior for sips that are still in limbo

Copy link
Contributor

@dbeal-eth dbeal-eth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please see my storage optimization comments in particular.

the removal of minDelegationTime is likely to have ripple effects elsewhere.

also, I already raised this with kaleb, but due to the removal of the minDelegationTime check on the PoolModule, it is no longer possible to enforce delegation time for users who want to frontrun on a pool level. We need to make sure the same rules apply both to pool owners, and to vault delegators.

/**
* @inheritdoc IMarketManagerModule
*/
function setUndelegateCollateralWindow(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please combine these individual setUndelegateCollateral... functions into a single setter that takes a tuple/structure.

its likely that such values will be always set/changed at the same time, reduces surface area, and

same goes for getter.

@@ -148,7 +148,6 @@ contract PoolModule is IPoolModule {
) external override {
Pool.Data storage pool = Pool.loadExisting(poolId);
Pool.onlyPoolOwner(poolId, ERC2771Context._msgSender());
pool.requireMinDelegationTimeElapsed(pool.lastConfigurationTime);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this line existed in order to prevent pool owners (or, single user-pool owners, which is something users can do, create a pool for themselves) from being exempt from the min delegation timeout

in order to effectively apply the delegation limitations to protect the pool, the same intent system must be developed on the market side as well.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In reference to #2145 (comment) hopefully a min delay / window can be enforced on pool owners, similar to the delay

/**
* @dev Returns the account delegation intents stored at the specified account id. Checks if it's valid
*/
function getValid(uint128 accountId) internal returns (Data storage accountDelegationIntents) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the name of this function and understanding the difference between getValid and loadValid seems extremely difficult. maybe a better name is loadWithInit or something?

/**
* @notice An incrementing id (nonce) to ensure the uniqueness of the intent and prevent replay attacks
*/
uint256 id;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this field could be done without--see other comments

delegation is already very expensive so every storage slot we can reduce is huge help

uint128 accountId;
uint128 delegationIntentsEpoch; // nonce used to nuke previous intents using a new era (useful on liquidations)
SetUtil.UintSet intentsId;
mapping(bytes32 => SetUtil.UintSet) intentsByPair; // poolId/collateralType => intentIds[]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we just embed DelegationIntent.Data[] type right here? then we:

  1. don't need to store intentsId
  2. don't need to store id inside of the DelegationIntent
  3. since intents are added in timestamp order, we can wipe out expired intents very cleanly without searching the full intents list by creating another field lastActiveIntent and incrementing that.
  4. if we make it a rule that intents have to be processed in the order they are submitted, this same field can be used to invalidate intents as we go

also if an intent is "cancelled" by the owner before it can be processed, we can add a field to the intent "cancelled, which only incurs a cost on intent processing and nowhere else

Copy link
Member

@kaleb-keny kaleb-keny Jun 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All in favor optimizing the removal of id @dbeal-eth and replacing with timestamps, in case the lp/account-id/market is the key to holding the stored intents... In order to avoid different timestamps stepping over each other. My concern is multiple users executing actions in the same block however, for vaults...

Regarding 3) yeah there is this expireIntent where you give it an id, and allows to expire specific intents, so no need to search for the full list, and there are a lot of view functions leo put together to find expired intents

The rule (mentioned in 4) to make intents processed in the order they are submitted can lead to issues with vaults... Assume the delay / window are configured differently for delegate / undelegate and a user specifies a undelegation on a vault and another user a delegate.. They can lead to stepping over each other... So this would require 2 intent data's right? One for delegation and another for undelegation?
Also another scenario I was thinking about is assume you have an intent that can't be expired or executed, and is in limbo until expiry, Example hitting caps on delegation. This leads to kind of bricking until parameters are updated... So enforcing sequentiality I thought about this and opted to not ask for it to be employed as to allow of execution of intents regardless of the order they are submitted....

Copy link
Contributor

@dbeal-eth dbeal-eth Jun 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea 3 is literally intended tobe a simplification for clients by allowing for the contract to be able to handle everything effieicntly.

regarding 4, if delegate/undelegate have different window or delay, then we can separate the lists by "operation" (its already separated by vault). Tho to simplify things I dont see a lot of use in allowing different window limits for delegate/undelegate...

regarding update of parameters... I mean these settings apply immediately to all pending intents regardless of when they are created and seems like it would be an issue with either scheme, so seems like a small issue if anything comparing to the benefits of the optimizations.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is also the situation of an intent that is neither executable and the window is not out for it's expiry, blocking the vault from executing any other intent. Aside from parameter changes, there are things that could go wrong and requiring sequential execution for async intents I feel is too fragile for the sake of optimization.

mapping(bytes32 => SetUtil.UintSet) intentsByPair; // poolId/collateralType => intentIds[]
// accounting for the intents collateral delegated
// Per Collateral
SetUtil.AddressSet delegatedCollaterals;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we can avoid storing this and only track it off-chain, save about 2-3 storage slots!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if this one is possible tho

/**
* @dev Returns the account delegation intents stored at the specified account id.
*/
function load(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we got rid of this load function and instead embedded this data structure into the Account, it would reduce load complexity and heiarchally store data in a convenient manner

@kaleb-keny
Copy link
Member

kaleb-keny commented Jun 5, 2024

please see my storage optimization comments in particular.

the removal of minDelegationTime is likely to have ripple effects elsewhere.

also, I already raised this with kaleb, but due to the removal of the minDelegationTime check on the PoolModule, it is no longer possible to enforce delegation time for users who want to frontrun on a pool level. We need to make sure the same rules apply both to pool owners, and to vault delegators.

Hey @leomassazza , there needs to be a minimum delay / window enforced on pool owners as db mentioned, otherwise one can setup a pool with 0 delay/window parameters in order to front-run price updates. That said, if the protocol owner, can push any parameters regardless of the minimum specified, on specific LP's, it would be great, as it allows for special cases where 0 delay/window are possible. But this isn't necessary in the near term and can be thought about in the future, dealing with just the minimum delay/window is fine for now.

Hey @dbeal-eth thank just to be on the same page here, you are fine with having a minimum delay / window applied on pool owners by the core right, in order to do away with minDelegateTime?

@dbeal-eth
Copy link
Contributor

Hey @dbeal-eth thank just to be on the same page here, you are fine with having a minimum delay / window applied on pool owners by the core right, in order to do away with minDelegateTime?

yea I think this is fine. but I am way more concerned about the breaking interface changes. should not be made lightly...

@kaleb-keny
Copy link
Member

kaleb-keny commented Jun 6, 2024

Hey @dbeal-eth thank just to be on the same page here, you are fine with having a minimum delay / window applied on pool owners by the core right, in order to do away with minDelegateTime?

yea I think this is fine. but I am way more concerned about the breaking interface changes. should not be made lightly...

Awesome thank you, yeah I get you, given that the core staking is yet to be integrated across different protocols I think we can risk it at this point, giving dhedge the necessary time to prepare. The reason i insist on this is that the intention is to lower the intent delay to 30 minutes and window to 1 hour. So having a minDelegateTime set to zero if it's kept. Which might lead to issues in having to deal with legacy code there, in case someone can setup their own pool without intents and just use the old interface for delegation, with 0 minDelegateTime...

uint32 __reservedForLater1;
uint64 __reservedForLater2;
uint64 __reservedForLater3;
uint64 __reservedForLater4;
Copy link
Collaborator

@mjlescano mjlescano Jun 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Listing storage changes as this looks risky:

  1. ⚠️ Deprecating uint32 minDelegateTime as uint32 __unusedLegacyStorageSlot (This should be marked with something like "DO NOT USE", because maybe it has data).
  2. ✅ Renaming uint32 __reservedForLater1 to uint32 undelegateCollateralDelay
  3. ✅ Expanding slot uint64 __reservedForLater3 to uint32 undelegateCollateralWindow & uint32 delegateCollateralDelay
  4. ❗ Renaming and Resizing uint64 __reservedForLater4 to uint32 delegateCollateralWindow. Please confirm @leomassazza, but this needs to add a uint32 __reservedForLaterX so it doesn't leak into the next slot for uint256 minLiquidityRatioD18

protocol/synthetix/contracts/storage/VaultEpoch.sol Outdated Show resolved Hide resolved
* renames and comments on storage changes

* reduce setter/getter footprint

* apply same fix to mock market

* fix test related to SetDelegateCollateralConfiguration

* Missing fix on tests

* rollback `minDelegationTime` and `delegateCollateral` removal

* Add separate FF for legacy and two steps delegation

* use twoStepDelegateCollateral in cannonfile test

* fix test (attempt)

* uses right FF

* test both modes

* remove duplicated code

* update storage

* rename getValid

* add global min/max to delegate delay and window

* update storage dump

* Add test to both FFs enabled

* Link `AccountDelegationIntents` directly to `Account`

* add tests to global params

* use declarationTime as id for intents

* Revert "use declarationTime as id for intents"

This reverts commit beb4f8c.

* fix typo in comments
Copy link
Contributor

@dbeal-eth dbeal-eth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems ok

we need to do some gas profiling tho because the way this has been designed may work well enough to cover the somewhat edge case of the needs of dhedge, but it does so by using an extreme amoun tof gas and storing excessive amounts of data imo. I am very certain there is a better way.

uint32 delegateCollateralDelay; // Accumulated Alignment 128
uint32 delegateCollateralWindow; // Accumulated Alignment 160
uint32 __reservedForLater1; // Accumulated Alignment 192
uint64 __reservedForLater2; // Accumulated Alignment 256
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems reasonable to believe that these alignments are correct but have we verified with the actual solidity compiler? I'm told that it creates a storage manifest cc @mjlescano


bytes32 private constant _DELEGATE_FEATURE_FLAG = "delegateCollateral";
bytes32 private constant _TWO_STEPS_DELEGATE_FEATURE_FLAG = "twoStepsDelegateCollateral";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

really we should be naming all feature flags the same as the function they guard

bytes32 private constant _DECLARE_DELEGATE_FEATURE_FLAG = "declareIntentToDelegateCollateral";
bytes32 private constant _PROCESS_DELEGATE_FEATURE_FLAG = "processIntentToDelegateCollateral";

having a flag to disable both of them at the same time icnraseses code complexity and can be done almost as eaily by simply specifying both of the function names for deny all feature flag.
that way its straightforward in an emergency situation to block any function by simply using its name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed, though couldn't use the full function name due to naming length (bytes32)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants