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

Adds ParachainStaking auto-compound feature #1828

Merged
merged 66 commits into from
Oct 12, 2022
Merged

Conversation

nbaztec
Copy link
Contributor

@nbaztec nbaztec commented Sep 22, 2022

What does it do?

autocompounds staking rewards for delegators based on a user-defined setting. Collator rewards will not be auto-compounded.

⚠️ Breaking Changes ⚠️ | ✔️ New Additions ✔️

Extrinsics

✔️ set_auto_compound - sets auto-compounding reward for an existing delegation.

set_auto_compound(
	origin: OriginFor<T>,
	candidate: T::AccountId,
	value: Percent,
	candidate_auto_compounding_delegation_count_hint: u32,
	delegation_count_hint: u32,
)

✔️ delegate_with_auto_compound - delegates and sets auto-compounding reward for a new delegation.

delegate_with_auto_compound(
	origin: OriginFor<T>,
	candidate: T::AccountId,
	amount: BalanceOf<T>,
	auto_compound: Percent,
	candidate_delegation_count: u32,
	candidate_auto_compounding_delegation_count: u32,
	delegation_count: u32,
)

Precompiles

✔️ delegationAutoCompound(address delegator, address candidate) - returns a uint8 respresenting the auto-compound value for that delegation.
✔️ delegateWithAutoCompound(address candidate, uint256 amount, uint8 autoCompound, uint256 candidateDelegationCount, uint256 candidateAutoCompoundingDelegationCount, uint256 delegatorDelegationCount) - delegates to a candidate with a specific auto-compound value.
✔️ setAutoCompound(address candidate, uint8 value, uint256 candidateAutoCompoundingDelegationCount, uint256 delegatorDelegationCount) - sets an auto-compound value for an existing delegation.

Storage

✔️ AutoCompoundingDelegations - ValueQuery, indexed by candidate id and contains the delegation auto compound config as a vec.

#[pallet::getter(fn auto_compounding_delegations)]
pub(crate) type AutoCompoundingDelegations<T: Config> = StorageMap<
	_,
	Blake2_128Concat,
	T::AccountId, // candidate id
	Vec<DelegationAutoCompoundConfig<T::AccountId>>,
	ValueQuery,
>;
{
    "0xffee...": [ 
        { delegator: "0x123...", value: 100. },   // 100% auto-compound
        { delegator: "0xabc...", value: 50 },     // 50% auto-compound
    ]
}

✔️ MigratedAtStake - OptionQuery, stores the first Round where the CollatorSnapshot was migrated. This is a temporary storage item, that will be removed in the subsequent RT update.

pub type MigratedAtStake<T: Config> = StorageValue<_, RoundIndex, OptionQuery>;

⚠️ AtStake - change in delegations field to also support auto-compound percentage.

Before

{
    "bond":  100,
    "delegations": [ 
        { owner: "0x123...", amount: 50 },
        { owner: "0xabc...", amount: 50 },
    ],
    "total":  200,
}

After

{
    "bond":  100,
    "delegations": [ 
        { owner: "0x123...", amount: 50,  autoCompound: 50 },   // 50% auto-compound
        { owner: "0xabc...", amount: 50,  autoCompound: 0  },   // no auto-compound
    ],
    "total":  200,
}

Events

✔️ DelegationAutoCompoundingSet - when set_auto_compound is called.

// Auto-compounding reward percent was set for a delegation.
AutoCompoundSet {
	candidate: T::AccountId,
	delegator: T::AccountId,
	value: Percent,
}

✔️ Compounded - emitted when a reward is paid out which also has auto-compounding set. ❗ Delegators having auto-compound enabled (and not 0%) will emit Compounded in addition to the Rewarded event.

// Compounded a portion of rewards towards the delegation.
Compounded {
	candidate: T::AccountId,
	delegator: T::AccountId,
	compounded: BalanceOf<T>,
}

⚠️ Delegation - adds new field auto_compound. If the extrinsic delegate(...) is called then the field has value of 0, else if extrinsic delegate_with_auto_compound(...) is called then the field has the user-provided value.

//// New delegation (increase of the existing one).
Delegation {
	delegator: T::AccountId,
	locked_amount: BalanceOf<T>,
	candidate: T::AccountId,
	delegator_position: DelegatorAdded<BalanceOf<T>>,
	auto_compound: Percent,
}

Errors

✔️ TooLowCandidateAutoCompoundingDelegationCountToDelegate - emitted if delegate_with_auto_compound is called with an incorrect candidate_auto_compounding_delegation_count.

✔️ TooLowDelegationCountToAutoCompound - emitted if delegation_set_auto_compounding_reward is called with an incorrect delegation_count.

✔️ TooLowCandidateAutoCompoundingDelegationCountToAutoCompound - emitted if delegation_set_auto_compounding_reward is called with an incorrect candidate_auto_compounding_delegation_count.

What important points reviewers should know?

  1. Current approach uses a similar strategy to scheduled requests by introducing the AutoCompoundingDelegations storage item. It's indexed by a collator and contains the delegations as a vec.

  2. The compound code is, plugged into the bondMore functionality (delegator_bond_more) but no DelegationIncreased will be emitted when auto-compounding. Reusing the same bondMore extrinsics gives us the benefit of having consistent validation.

  3. If an auto-compound config exists then a Rewarded event is emitted with the reward amount and a Compounded event is emitted containing the compounded amount, if no config exists then the reward is simply minted and Rewarded event is emitted.

  4. The compound percentage is stored per round, following the usual snapshot rules. This leads to extra runtime weight during round's on_initialize but was found to be "low-enough" (500 bytes in worst case) with zstd compression.
    Since we're doing additional work on on_initialize, the PoV size and extrinsic time increase sub-quadratically.
    1, 10, 100, 250 delegators paid out.
    staking-mint: existing implementation (only mint) - no delegator has auto-compound
    staking-compound: suggested implementation (mint+compound) - all delegators have 100% auto-compound

image

Worst case analysis

350,0 : 350 delegations, 0 auto-compounding
350,175 : 350 delegations, 175 auto-compounding
350,350 : 350 delegations, 350 auto-compounding
staking-mint: existing implementation (only mint)
staking-compound: suggested implementation (mint+compound)

image

The runtime-complexity is sub-quadratic, as defined below:

candidates: C
collator_max_delegations_top: T
collator_max_delegations_bottom: B
delegator_max_delegations: M 


previously O(T)
now        O(T * (T+B + M + T + log(T))
             |    |     |   |   |
             |    |     |   |   \-- bond more, sort top delegations
             |    |     |   \-- bond more, find & increase amount in top delegation
             |    |     \-- bond more, find delegation in DelegatorState & update amount
             |    \-- bond more, check for scheduled revokes in DelegationScheduledRequests for the collator
             \-- for each rewarded top delegator

Is there something left for follow-up PRs?

What alternative implementations were considered?

Are there relevant PRs or issues in other repositories (Substrate, Polkadot, Frontier, Cumulus)?

What value does it bring to the blockchain users?

Allows delegators to auto-compound a percentage of their staking rewards towards a collator.

TODO

  • run benchmark on proper machine
  • add typescript tests

@nbaztec nbaztec added B7-runtimenoteworthy Changes should be noted in any runtime-upgrade release notes D9-needsaudit👮 PR contains changes to fund-managing logic that should be properly reviewed and externally audited labels Sep 23, 2022
@nbaztec nbaztec marked this pull request as ready for review September 23, 2022 14:29
@nbaztec nbaztec merged commit aa799b2 into master Oct 12, 2022
@nbaztec nbaztec deleted the nish-staking-autocompound branch October 12, 2022 15:02
timbrinded pushed a commit that referenced this pull request Oct 14, 2022
* include analyze functionality in pov tool

* add extrinsics for handling auto-compounding

* user percent based compounding

Co-authored-by: Crystalin <[email protected]>
@crystalin crystalin changed the title [MOON-1902] autocompound staking rewards Adds ParachainStaking auto-compound feature Oct 20, 2022
@notlesh notlesh added D1-audited👍 PR contains changes to fund-managing logic that has been properly reviewed and externally audited and removed D9-needsaudit👮 PR contains changes to fund-managing logic that should be properly reviewed and externally audited labels Dec 14, 2022
imstar15 pushed a commit to AvaProtocol/moonbeam that referenced this pull request May 16, 2023
* include analyze functionality in pov tool

* add extrinsics for handling auto-compounding

* user percent based compounding

Co-authored-by: Crystalin <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
B7-runtimenoteworthy Changes should be noted in any runtime-upgrade release notes D1-audited👍 PR contains changes to fund-managing logic that has been properly reviewed and externally audited
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants