Skip to content
Open
Changes from 16 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
df646ac
feat: add sip to remove cool down cycle
friedger Apr 1, 2025
7015353
chore: remove solo stacking
friedger Apr 4, 2025
7546e84
fix: tpyes, corrections
friedger Apr 8, 2025
4405324
feat: update sip to auto extend
friedger Jun 14, 2025
cbca81f
fix typos and grammar
friedger Jun 15, 2025
3bbf442
improve mermaid graph
friedger Jun 15, 2025
31e8c92
prevent DDoS attack through small stacking amounts
friedger Jun 17, 2025
3ce2e0e
fix: typos, clarity
friedger Jun 17, 2025
0891e53
Update sips/sip-x/sip-x-improved-stacking.md
friedger Jun 30, 2025
895f3b7
chore: clarify the flow
friedger Jul 1, 2025
5373b69
Merge pull request #2 from friedger/feat/sip-improved-stacking-july-25
friedger Jul 1, 2025
8e15dc7
Update sips/sip-x/sip-x-improved-stacking.md
friedger Jul 31, 2025
8a10e82
add section about locked amount and events
friedger Jul 31, 2025
a2e7bae
clarify voting power and decreased stacked stx amount
friedger Jul 31, 2025
0ef1069
rename to sip 032
friedger Aug 14, 2025
d1a016d
rename folder
friedger Aug 14, 2025
5c1159d
fix: address comments, update to 3.3
friedger Aug 23, 2025
1cb837b
fix: clarify the events
friedger Aug 23, 2025
02d5c73
fix: use signer-accept also as event name
friedger Aug 23, 2025
876eaac
Update sips/sip-032/sip-032-improved-stacking.md
friedger Sep 15, 2025
aa83821
Update sips/sip-032/sip-032-improved-stacking.md
friedger Sep 15, 2025
02a4910
fix: clarify use of maximum allowed amount to stack
friedger Sep 25, 2025
0d587d2
Merge branch 'feat/sip-improved-stacking' of github.com:friedger/sips…
friedger Sep 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
232 changes: 232 additions & 0 deletions sips/sip-032/sip-032-improved-stacking.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
# Preamble

**SIP Number:** 032

**Title:** Improved Stacking Protocol

**Authors:**

- Friedger Müffke ([[email protected]](mailto:[email protected]))

**Consideration:** Technical

**Type:** Consensus

**Status:** Draft

**Created:** 2025-04-01

**License:** BSD 2-Clause

**Sign-off:**

**Discussions-To:**

- [Stacks Forum Discussions](https://forum.stacks.org/t/remove-cool-down-cycle-in-stacking/17899)

# Abstract

This SIP proposes a change to the stacking process so that Stackers can change their stacking settings without the so-called cooldown cycle. Furthermore, the relationship between Stackers
and signers is strengthened and stacking overall is simplified.

The specification defines that

- solo Stackers and delegating Stackers have to follow the same flow and use the same contract functions: All users delegate block voting power to signers.
- Stackers can change their stacking settings for the next cycle before the prepare phase, including the chosen signer (switch pools).
- locked Stacks tokens are locked for 1 cycle at a time and that the locking period is extended by another cycle if the user does not request to unlock tokens.
- delegated stacking tokens are locked immediately.
- stacking rewards are received by the Bitcoin address specified by the signer.
- locking Stacks tokens is protected by a new type of post conditions, enabling stacking through a contract in a single transaction.

# Introduction

## Current Situation

Currently, the stacking protocol has a few aspects that make using and integrating stacking harder than it could be:

- There are two groups of Stackers: solo Stackers and delegated Stackers. They use different sets of PoX contract functions.

- When using a contract for stacking, the contract needs to be added as an allowed PoX contract. This requires a separate transaction.

- Solo Stackers do all transactions with their cold wallet holding the whole STX balance. They need to make a transaction at least once every 12 cycles (6 months). In contrast, delegated Stackers need to do only a single transaction with their wallet for the entire stacking.

- Signers make off-chain agreements with pool operators regarding revenue sharing.

## Glossary

| Term | Definition |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Cooldown Cycle** | The period of 1 stacking cycle where Stackers cannot stack their STX tokens because their tokens are unlocked only at the beginning of a stacking cycle. |
| **Signer** | The node operators that verify and confirm proposed blocks by miners. |
| **Delegation (old)** | Delegating the management of stacking. |
| **Delegation (new)** | Delegating the voting power for proposed blocks. |
| **PoX Contract** | The smart contract that users interact with to lock their tokens. |

## Problem Statement

The current stacking process has two different paths, one for solo Stackers, one for delegated Stackers,
resulting in a more complex user experience and more complex code.

The process is defined by a prepare phase of 100 bitcoin blocks that is used to find an anchor block
for the next stacking cycle. Stackers must lock
their Stacks tokens before the
prepare phase, i.e. before the 2000th block of the current stacking cycle. Furthermore, Stacks
tokens are locked for a locking period that always ends at the beginning of a cycle, i.e. after the
prepare phase. Therefore, the current implementation of Stacking includes a period where Stacks
tokens are unlocked and ineligible for stacking rewards (Cooldown Cycle).

The current PoX contract allows users to lock Stacks tokens during the prepare phase. This is too late for the next cycle
and the tokens are locked without earning stacking rewards for one cycle.

The cooldown cycle for unstacking from a pool presents a problem for network decentralization.
As a Stacker, when I delegate to a signer and that signer does not perform and gets low yield,
I get penalized for switching. In today's model with cooldown cycles, users get double penalized
if a signer does not perform. Even if there are penalties for signers not performing / being down,
the switching costs are too high for users to switch (two weeks' worth of yield).

Furthermore, users cannot change their
stacking settings (decrease amount, change PoX reward address, etc.) without an unlocked cycle during which users cannot earn
stacking rewards.

## Proposed Solution

This SIP proposes a new Proof of Transfer (PoX) contract without
the flow for solo stacking and moves responsibilities from pool operators to signers who were introduced in the Nakamoto upgrade. It also
defines a new locking mechanism that allows users to switch from one signer to another with a single contract call and without a cooldown period.

# Specification

Applying these upgrades to the Stacks blockchain requires a consensus-breaking network upgrade,
in this case, a hard fork. Like other such changes, this will require a new Stacks epoch.
In this SIP, we will refer to this new epoch as Stacks 3.2.

## Delegated Stacking only

The following PoX contract functions shall be removed:

- stack-stx
- stack-increase
- stack-extend
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of removing them, could they instead wrap calls to the delegation functions? For example, stack-stx could be re-implemented to delegate STX to the caller, and have the caller stack and aggregate-commit them.


## Locking Post Conditions

The following type of post conditions shall be added to the current definition in SIP-005 (`TransactionPostCondition`)

- `LockingLimit(PostConditionPrincipal, FungibleConditionCode, u64)`

A transaction (using Deny mode) with this post condition will abort if the locked Stacks tokens of the principal do not satisfy the provided conditions. The logic for the conditions follows that of STX transfer.
Copy link
Contributor

Choose a reason for hiding this comment

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

Depending on the order in which this SIP and the Clarity 4 SIP (#218) get ratified, it may be useful for #218 to add an asset guard special form for PoX locking (cc @obycode).

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, yes. I had that thought at some point, but then forgot to include it. Thanks! I'll add it.


## Automatic Extend and Locking Period

The following function shall be removed:

- `delegate-stack-extend`

In addition, the stacking settings for each Stacker (user with delegation) shall be applied for the next stacking cycle if the user did not signal the end of stacking by calling `revoke-designation` 200 blocks before the start of the next cycle.

This results in the following structure of the stacking cycle:

- bitcoin block 1-1900: stacking as usual, user can signal change of stacking settings.
- bitcoin block 1901-2000: stacking as usual, signers can still aggregate stacking changes, changes to stacking are applied to the cycle after next.
- bitcoin block 2001-2100 (prepare phase): no rewards for Stackers, changes to stacking are applied to the cycle after next.

```mermaid
timeline
title Stacking Cycle Structure
section Earning
1 : Stacking as usual : User can signal change of stacking settings
... : Usual reward distribution
1901 : Stacking as usual : Auto extension : Signers can still aggregate stacking changes
section No earning
2001 : Prepare phase, no rewards: delegate txs applied only to cycle after next
2100 : End of cycle
```

That means the locking period is 1 cycle, with automatic extension for another cycle until the user decides to end stacking.

The amount of stacking STX can increase during the automatic extension up to the minimum of the balance and a user specified maximum amount.

The amount of stacked STX does not decrease during the automatic extension. If the user requested a decrease of the stacked amount, this difference is unlocked at the end of the stacking period.

## Semantic Change of Delegation
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm generally fine with renaming things. Would it also be possible to preserve the existing function names and signatures, and have them call the new functions? The reasoning here is that it would keep existing tool and scripts working.

Copy link
Contributor

Choose a reason for hiding this comment

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

+1 for backwards compatibility here


When signers verify and accept proposed blocks by miners, their voting power corresponds to the amount of stacked Stacks tokens (see SIP-021). The new stacking process changes the delegation flow as follows:

- `delegate-stx` shall be renamed to `designate`. The function takes the arguments: `amount`, `signature`, optional bitcoin `block-height` defining the end of stacking and auto extending, optional `pox-addr` defining that pox-address that the signer must use, optional `max-amount`. The `amount` defines how many Stacks tokens are locked immediately. The `max-amount` defines the maximum of Stacks tokens that can be locked in the future through auto extending. If provided the minimum of the user's stx balance and `max-amount` is locked. If omitted, the whole user's balance is locked. Users can use sponsored transactions to call revoke-delegate-stx in case there is no unlocked stx in the account. The argument `signature` is a signature of the signer indicating that the signer accepted the delegation of voting power. The signing follows the structured message signing with the parameters and topic `designate` as message. The public key of the signature can be used to identify the signer similar to the Stacks address of the pool operator in the previous PoX design.
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this the function signature?

(define-public (designate
   ;; The amount of uSTX to be locked immediately
   (amount uint)
   ;; Signature of the signer indicating that the signer accepted the delegation of voting power
   (signature (buff 65))
   ;; The Bitcoin block height at which this designation ends, if any.
   ;; N.B. the text says `block-height`, but this is a reserved word.
   ;; I'm using `end-block-height` in its place.
   (end-block-height (optional uint))
   ;; PoX address that the signer must use, to which PoX yield accrues
   (pox-addr (optional { version: (buff 1), hashbytes: (buff 32) }))
   ;; Maximum amount of uSTX that can be locked through future auto-extending
   (max-amount (optional uint))
   
   ;; code goes here
)

Copy link
Contributor

Choose a reason for hiding this comment

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

A few other questions:

  • Can you describe the SIP-018 payload that must be signed by the signer?
  • How does this method identify the signer public key to use, in order to authenticate signature? Can it be an argument to the function, like it is for stack-aggregation-commit and stack-aggregation-increase?
  • How does this method ensure that signatures cannot be reused? The current PoX code uses an auth-id, which must be included in the signature and which cannot be re-used across signatures from the same signer key.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Signatures should be created in the same way as Pox-4 using public key and auth-id, etc. I'll extend the section about signatures.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think it would be best to include the actual function signature in the SIP. Additionally, we will need signer-key as an argument

Copy link
Contributor

Choose a reason for hiding this comment

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

I totally understand the usage of max-amount here, but this differs from the current usage of max-amount. Currently, max-amount is used for the signer to say "you can't stack any more than this, because I don't want to have too much signing power". In this proposal, max-amount is for the stacker to say "you can only lock at most this much".

These are two different things, so we'll need to reconcile this fact. Either we add a new argument for the stacker's intention, or we remove the current usage of max-amount.

Additionally, from a UX perspective, we need to think about how max-amount is used in signature generation. When referring to the stacker's intention of "you can only lock at most X", we should probably just omit it from signature generation. Otherwise, signers will need to get this information ahead of time for generating signatures.

Copy link
Contributor

Choose a reason for hiding this comment

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

After further consideration, I think we need to keep both the old param and the new param, meaning we need to figure out what to call these params. My recommendation is:

  • max-amount: the same as before. This represents "the max amount a signer will allow a stacker to stack towards their signer key". For most signers, this will remain u128::max
  • allowance: the maximum amount that a stacker is allowing the signer to stack.

This would essentially mean replacing "max-amount" with "allowance" in this paragraph.

Copy link
Contributor

Choose a reason for hiding this comment

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

After further reading, I actually don't understand what max-amount means here. If I call designate with an amount of 1000, and a max-amount of 2000, what happens? It doesn't seem like signer-accept takes an amount or anything, so where does max-amount ever come into play?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have updated this section to clarify what it means when max-amount is omitted. In this case, amount is used as max-amount.

I also renamed max-amount to amount-allowance to avoid confusion about max-amount used during signature generation.

max-amount/amount-allowance is used during auto-extend where the stacking amount can be increased automatically as well.


- `revoke-delegate-stx` shall be renamed to `revoke-designation`. After calling this function, auto extension is stopped and Stacks tokens are unlocked for the user at the end of the current cycle. If a signer does not aggregate enough Stacks tokens to receive at least one reward slot (or does not commit at all) then the user's Stacks token are unlocked immediately through this call.

- `delegate-increase` is replaced by `update-max-amount`. It sets max-amount to the new value. This value is applied when the user's designation is extended for the next cycle. The amount can be smaller than the currently locked amount. As tokens are only locked for 1 cycle at a time, handling decreasing is now easy enough in comparison to the previous system with locking periods of up to 12 cycles.

- `delegate-extend` is removed because the locking period is automatically extended each cycle.

## Amount of Stacked STX

When a transaction locks or unlocks STX for the user a corresponding event shall be emitted. The following events used in PoX 4 are used:
Copy link
Contributor

@jcnelson jcnelson Aug 14, 2025

Choose a reason for hiding this comment

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

PoX 4 uses the function's name as the event's name (i.e. in the event tuple's name field). Is this mapping from a PoX 5 event to the old PoX 4 event correct?

  • designate --> delegate-stx
  • designate-increase --> stacks-aggregation-increase
  • designate-decrease --> (this is new, and has no PoX 4 analogue)
  • designate-extend --> similar to delegate-stx-extend, but is now automatic?
  • signer-accept --> stacks-aggregation-commit

Can you also explain what the event payloads will be?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, I see that stacks-aggregate-commit is due to be replaced with signer-register. Is signer-accept a new function in PoX 5? I don't see it defined in this document.

Copy link
Contributor Author

@friedger friedger Aug 23, 2025

Choose a reason for hiding this comment

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

signer-accept is defined in ## Amount of Stacked STX as event name. The function is signer-register.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I understand, I'll change signer-accept to a function 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.

The events in Pox 5 are similar (-->) in Pox 4

  • designate --> delegate-stx + delegate-stx-stack
  • designate-increase --> delegate-stx-increase but by user
  • designate-decrease --> (this is new, and has no PoX 4 analogue)
  • designate-extend --> similar to delegate-stx-extend, but is now automatic? Correct, however, there is no call from the user
  • signer-accept --> stacks-aggregation-commit

Payloads should be similar to pox-4, I'll spend some time to specify them as well.


- `designate` when the user designates a signer
- `designate-increase` when the amount of voting power is increased by a user
- `designate-decrease` when the amount of voting power is decreased by a user.
- `designate-extend` when the designation is extended by 1 cycle for a user.
- `signer-accept` when the signer accepts the designation from all users.

Furthermore, a function `get-stacker-info` shall return for the given principal

- the accepted stacked amount by the signer and the cycle of acceptance `(optional {amount: uint), cycle: uint})`
- the current stacked amount `uint`
- the current max-amount `(optional uint)`

When stackers designate the first time, the function `get-stacker-info` the accepted stacked amount is `none`.

## Role of Signers

### PoX Reward Addresses

`Stacks-aggregation-commit` is replaced by `signer-register`. Signers indicate their liveness and provide a PoX reward address and a Stacks address with this call. The bitcoin address shall be used to receive stacking rewards. It can be used by Stackers as `pox-addr` parameter during `delegate`/`designate` call.

The `signer-register` call can only be called between 1901st and 2000th block of a stacking cycle. During this period, Stackers can't change their stacking settings, therefore, no aggregate is required.

There is no change in the registration of PoX reward addresses. In particular, there can be more than one PoX reward address per signer.

### Signatures

The signatures provided by signers to Stackers or the network shall use one of the following topics

- designate
- register

The other topics are no longer used.

Signers handle two or three private keys:

1. one for signing blocks,
2. one for delegation approval and register transactions on the Stacks blockchain;
3. optionally, one for PoX reward address and reward distribution.

Note, for solo Stackers these keys can be just a single key. For more complex setup, the keys can be handled by different independent entities. Also, the PoX reward address can be a deposit address for sBTC of a Stacks smart contract. In this case, the signer does not hold a private key for the PoX reward address.
Copy link
Contributor

Choose a reason for hiding this comment

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

In all likelihood, solo stackers also have separate keys for signing, for cold storage STX, and for Bitcoin rewards.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, the PoX reward address can be a deposit address for sBTC of a Stacks smart contract.

This is very interesting. Is the taproot address guaranteed to be stable for long enough that it can be used as a PoX address? My understanding of the sBTC minting process is as follows:

  • The user creates a taproot address from two scripts -- a deposit script, and a reclaim script
    • The deposit script contains the WSTS aggregate public key's X coordinate, the maximum BTC fee, and the Stacks principal to which to mint the sBTC (it's <[8-byte-max-fee][up-to-159-byte-SIP-005-principal]> OP_DROP <signers-x-public-key> OP_CHECKSIG).
    • The reclaim script, which allows the sender to reclaim the BTC after a timeout (it's <locktime> OP_CSV <user-supplied-reclaim-script>).
  • The user creates a Bitcoin transaction which sends BTC to the taproot address
  • The user informs the Emily API service of the transaction
  • The Emily API service forwards the transaction to the sBTC signers
  • The sBTC signers, if possible, will spend the taproot UTXO via the deposit script. If the signers can't do this in time, then the user can spend it with the reclaim script.

The risk to users in supplying an sBTC deposit taproot address is that their maximum fee value and locktime value will need to remain set for at least one stacking cycle. The user would need to change their PoX address each time they want to change the maximum fee or advance the locktime. Can this SIP advise users on how to construct their sBTC deposit addresses for them to be suitable for their desired number of reward cycles?

Copy link
Contributor Author

@friedger friedger Aug 23, 2025

Choose a reason for hiding this comment

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

In all likelihood, solo stackers also have separate keys for signing, for cold storage STX, and for Bitcoin rewards.

I inserted "theoretically".

Also, the PoX reward address can be a deposit address for sBTC of a Stacks smart contract.

My intention was to highlight that this is possible with the new setup. However, explaining details how to implement this, should be part of a different resource, e.g. https://github.com/friedger/clarity-trustless-pool/


### Voting Power

The voting power for a signer is the sum of the accepted stacked STX, not the sum of the currently stacked STX because users can have descreased the stacking amount.

## Transition to PoX-5

A new PoX contract requires that all stacked Stacks tokens are unlocked and Stackers need to lock their Stacks token again using the new PoX-5 contract. The process shall be similar to the previous upgrades of the PoX contract. The PoX-4 contract shall be deactivated and the PoX-5 contract shall be activated at the beginning of epoch 3.2. All locked Stacks tokens shall be unlocked automatically one block after the beginning of epoch 3.2. Nevertheless, these tokens will earn rewards until the end of the cycle.

## Reference implementation

A reference implementation for creating signatures is available at ... (TODO). Note, that signers (or previously pool operators) no longer extend the locking period for each Stacker on-chain, instead signers provide a signature for each Stacker off-chain.

A reference implementation for a sidecar for signer node is available at ... (TODO).

# Related Work

The previous PoX process is described in [SIP 007](https://github.com/stacksgov/sips/blob/main/sips/sip-007/sip-007-stacking-consensus.md).

# Activation

This SIP requires a hard fork and shall be activated on Stacks 3.2, as defined by the SIP for epoch 3.2.

## Appendix

[1] https://github.com/stacks-network/stacks-core/issues/4912