-
Notifications
You must be signed in to change notification settings - Fork 0
Basics of release stake doc #128
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
base: main
Are you sure you want to change the base?
Changes from all commits
6422a72
47a9103
905e6f1
200c2fb
cd52357
8a44213
6e0441f
b5c1740
fa802a0
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,166 @@ | ||
| # Release Stake | ||
|
|
||
| ## Overview | ||
|
|
||
| `release_stake` is a message in the restaking system that allows a **consumer contract** to request the release of tokens from its provider contract. | ||
|
|
||
| ### Message Structure | ||
| ```rust | ||
| ReleaseStake { | ||
| staker: String, | ||
| amount: Uint128, | ||
| denom: String, | ||
| } | ||
| ``` | ||
|
|
||
| ## Core Process | ||
| 1. **Initiation:** A consumer contract sends the `release_stake` message to the provider. | ||
| 2. **Validation:** The provider validates the request by checking the amount and ensuring proper authorization. | ||
| 3. **State Update:** Stake accounting is updated in the provider contract, reflecting the released tokens. | ||
| 3.1. *Question:* shouldn't this force the clearing of any unbonding claims and/or finalizing pending reward distributions. | ||
| 4. **Finalization:** The provider processes any unbonding claims and optionally forwards the `release_stake` message up the chain. | ||
|
|
||
| > **Exception:** If the contract is the ultimate provider (e.g., Token Staking), the process terminates by sending a bank transfer to the original staker. | ||
|
|
||
| *Question:* my understanding is that during the unbonding period, the tokens remain locked. During this period they still may be slashed or changed (delegation scenarios). So then when this period has ended, and any pending rewards are accounted for/distributed, can `release_stake` happen. | ||
|
|
||
| ## Contract Behaviors | ||
| The behavior of `release_stake` varies depending on the contract type. Contracts are categorized into **Pure Pass-Through Contracts** and **State-Mutating Contracts**. | ||
|
|
||
| ### Pure Pass-Through Contracts | ||
| These contracts do not alter the stake state and simply forward the message upstream to the provider: | ||
|
|
||
| #### 1. Token Staking | ||
| - Receives `release_stake` from a consumer. | ||
| - Creates a **Bank Send** transaction to the staker. | ||
| - Terminates the flow; does not forward to a provider as it is a leaf node. | ||
|
|
||
| #### 2. Fan In | ||
| - Maps output denominations to input denominations and forwards the request to the correct provider. | ||
| - Ensures the input denom is correctly remapped. | ||
|
|
||
| ### State-Mutating Contracts | ||
| These contracts manage stake accounting and modify state: | ||
|
|
||
| #### 1. Token Weighting | ||
| - Receives `release_stake` after unbonding. | ||
| - Makes sure it’s not recalculating weights before propagating `release_stake`. | ||
| - Adjusts **token weight ratios** and stake state. | ||
| - Forwards the message to the provider. | ||
|
|
||
| #### 2. Delegations | ||
|
|
||
| The delegations contract manages stake allocation across multiple delegates. When processing `release_stake`, it must handle proportional distribution of released amounts across delegators. | ||
|
|
||
| **Key Challenges:** | ||
| - Must handle potentially large numbers of delegators (1000+) | ||
| - Needs to maintain proper proportional distributions | ||
| - Cannot iterate through all delegators due to gas limits | ||
| - Must handle multiple release_stake requests during unbonding period | ||
| - Must update delegation ratios after release | ||
|
|
||
| **State Management:** | ||
| ```rust | ||
| #[cw_serde] | ||
| pub struct UnbondingClaim { | ||
| pub total_unbonding: Uint128, // Amount being unbonded in this claim | ||
| pub released_amount: Uint128, // Amount processed/released so far | ||
| pub end_time: u64, // When unbonding will end | ||
| } | ||
|
|
||
| // Maps (denom, delegate) -> vector of unbonding claims | ||
| pub const DELEGATE_UNBONDING: Map<(&str, &Addr), Vec<UnbondingClaim>>; | ||
| ``` | ||
|
|
||
| **Process Flow:** | ||
|
|
||
| 1. When consumer calls `release_stake`: | ||
| ```rust | ||
| pub fn release_stake( | ||
| deps: DepsMut, | ||
| env: Env, | ||
| info: MessageInfo, | ||
| staker: String, // delegate address | ||
| amount: Uint128, | ||
| denom: String, | ||
| ) -> Result<Response, ContractError> { | ||
| // Validate sender is consumer contract | ||
| // Verify delegate has sufficient stake | ||
| // Verify no pending rewards need distribution | ||
| // Record historical stake amount for proper reward calculation | ||
| // Create new UnbondingClaim | ||
| // Update total staked amount | ||
| // Emit events | ||
| } | ||
| ``` | ||
|
|
||
| 2. When delegator calls `release_unbonded`: | ||
| ```rust | ||
| pub fn release_unbonded( | ||
| deps: DepsMut, | ||
| env: Env, | ||
| info: MessageInfo, | ||
| ) -> Result<Response, ContractError> { | ||
| // For each delegate where delegator has stake: | ||
| // For each unprocessed claim: | ||
| // If unbonding period complete: | ||
| // Update delegator's stake and liens | ||
| // Recalculate delegation ratios: | ||
| // - Get all current delegations | ||
| // - Subtract the released amount | ||
| // - Normalize remaining delegations to sum to 100% | ||
| // - Update STAKER_DELEGATIONS | ||
| // Forward portion upstream via release_stake message | ||
| // Mark as processed | ||
| // Update released_amount in claim | ||
|
|
||
| // Send accumulated messages | ||
| } | ||
| ``` | ||
|
|
||
| #### 3. Fan Out | ||
| - Handles multiple output flows. | ||
| - Updates **outflow accounting** to reflect the proportionate release. | ||
| - Maintains unbonding claims instead of directly triggering `release_stake`. | ||
| - The staker must call `ReleaseUnbonded` to propagate `release_stake` upstream. | ||
|
|
||
| #### 4. Splitter | ||
| - Maintains split proportions for multiple consumers. | ||
| - Updates split accounting based on the `release_stake` message. | ||
|
|
||
| #### 5. Operators | ||
| - Maps the **operator address** to the original staker address. | ||
| - Updates stake accounting and ensures the message flows through correctly. | ||
| - Maintains unbonding claims until the staker calls `ReleaseUnbonded`. | ||
|
|
||
| ## State of Contracts Implementing The Release Stake | ||
|
|
||
|
|
||
| | Contract | `release_stake` Implementation | Current Status | Needed Changes | | ||
| | --------------- | ---------------------------------------------------------------------------------------------------------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| | Token Staking | `release_stake` implemented | Fully Implemented | Tokens are available for immediate withdrawal via `withdraw_unbonded`. | | ||
| | Fan In | `release_stake` updates `TOTAL_STAKE` and forwards upstream | Partially Implemented | Add reward accounting (not mandatory), we need partial/full release policy, add validation before forwarding | | ||
| | Fan Out | `release_stake` implemented: updates state and liens, but user must call `ReleaseUnbonded` to propagate upstream | Partially Implemented | Needs integration of reward logic (right now is `todo!()` in `withdraw_rewards`/`distribute_rewards`), ensure that all claims and outflows are settled before user triggers `ReleaseUnbonded` to finally push `release_stake` upstream | | ||
| | Delegations | still `todo!()` | Not Implemented | ensure delegators’ claims settled, distribute rewards, maybe also handle partial/full release before forwarding | | ||
| | Token Weighting | no explicit `release_stake` present | Not Implemented | implement `release_stake`: ensure no weight recalculations in progress, fix token weight ratios, then forward upstream | | ||
| | Operators | `release_stake` implemented | Partially Implemented | make sure that operator removal and slashing are in consideration. Ensure unbonding claims handling until `ReleaseUnbonded` called by staker, then forward upstream | | ||
| | Splitter | `release_stake` is still `todo!()` | Not Implemented | Implement logic for multiple splits, handle rewards/unbonding claims, then forward | | ||
|
|
||
| ## Known Trigger Points | ||
| `release_stake` can be triggered in several scenarios: | ||
|
|
||
| 1. **User-Initiated Claims** (ReleaseUnbonded): | ||
| - A user calls `ReleaseUnbonded` on a contract after the unbonding period expires. | ||
| - This triggers `release_stake` messages to propagate up to the provider. | ||
|
|
||
| 2. **Administrative Actions:** | ||
| - **DefineFlow (Fan Out):** Changing outflows creates unbonding claims for old outflows. | ||
| - **SetOperator (Operators):** Changing operators results in unbonding claims for the old operator. | ||
| - **DefineDelegates (Delegations):** Modifying delegation configuration creates unbonding claims that are finalized via `ReleaseUnbonded`. | ||
|
|
||
| ## Questions | ||
| - when it comes to validating the request by the `provider`, should the `provider` always trust the `consumer's` request (`consumer` is always whitelisted) or the `provider` should do additional validation - eg, check that the request doesn't exceed what's already staked? | ||
|
Collaborator
Author
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 it's fine to trust the consumer's request. The simple things like "exceed what's staked" should be caught by the checked math operations, and the provider doesn't really have any context of how the release_stake amount was calculated anyway. |
||
| - do we allow for partial `release_stake` - eg, a consumer would like to free 1/3 of the staked tokens | ||
|
Collaborator
Author
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. Yes, it will be up to the provider contract to handle its own stake logic correctly. |
||
| - for `token_weighting` contract: what happens when the token-weighting contract receieve a msg to release stake when it's currently calculating new weight? Basically both requests in the same block | ||
|
Collaborator
Author
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. Nice thinking. The only case we need to avoid here is where we might call |
||
| - for `fan-out` contract: what if we change the denom and request `release_stake` on the old name? It will fail ofc, but I guess we have to call the old denom name. | ||
|
Collaborator
Author
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 don't think we support renaming a denom after it has been defined. |
||
| - for `operators` contract: that's for the future, but shouldn't we check if an operator is being slashed, before releasing the stake, there might be better options? Another one is to proceed with the removal of operators and their stake | ||
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
The reason will be displayed to describe this comment to others. Learn more.
I was thinking about some specific use cases of
unset_operator- that might trigger several unstake events down the line. New operator is set, so the staking new funds (and unstaking those) should be allowed as well.Now imagine the case with the operators:
unsetbe allowed?I think this case would be mitigated if we would not allow for more then one unbonding per staker. If you already have an active one, then you can still stake or unstake but that's it.
I know this is a rather general doc, but wanted to share some thoughts.