PIP-35: Introduce a secure way to transfer a staked app to a new account



We suggest introducing a new operation “app transfer” to enable us to transfer the stake of the existing app to a new account without triggering unstaking.

Motivation / Rationale

In PIP-6.2 [1], the Foundation was delegated the permission to limit access to app stake slots by controlling the parameter app/MaxApplications.

With the Gateway-verse initiative, the Foundation will be taking the app stakes that were exclusively accessed by Grove and distributing them to new gateways. Because gateways are charged the GatewayFeePerRelay for every relay submitted by their app stakes, it makes sense that new gateways will want fresh app stakes that they know have not been exposed to another entity. This means the Foundation needs to be able to rotate the existing app stakes.

When an app stake is unstaked, however, there is a chance for anyone to steal that app slot by submitting a stake tx in the same block because the order of transactions a validator includes in a block is not guaranteed. This makes it difficult to securely rotate the app stakes.

This proposal removes that barrier and introduces a new way to transfer the ownership of a staked app with a single transaction without exposing the slot to risk of being stolen.

[1] PIP-6.2: Settlers of New Chains


The new “app transfer” operation can be achieved using the existing AppMsgStake transaction, without introducing a new transaction type.

Here’s the current MsgStake message to stake an app [1].

type MsgStake struct {
	PubKey crypto.PublicKey `json:"pubkey" yaml:"pubkey"`
	Chains []string         `json:"chains" yaml:"chains"`
	Value  sdk.BigInt       `json:"value" yaml:"value"`

And a transaction to be submitted to the network encapsulates a message in stdTx [2],

type StdTx struct {
	Msg       sdk.Msg      `json:"msg" yaml:"msg"`
	Fee       sdk.Coins    `json:"fee" yaml:"fee"`
	Signature StdSignature `json:"signature" yaml:"signature"`
	Memo      string       `json:"memo" yaml:"memo"`
	Entropy   int64        `json:"entropy" yaml:"entropy"`

where StdSignature is defined as below [3].

type StdSignature struct {
	posCrypto.PublicKey        `json:"pub_key" yaml:"pub_key"`
	Signature           []byte `json:"signature" yaml:"signature"`

This means every transaction contains two public key fields MsgStake.PubKey and StdTx.Signature.PublicKey. The former is a public key of a new app to be staked and the latter is the signer of a transaction. In the current implementation, both public keys must be identical, otherwise the transaction is rejected.

We can utilize these duplicated fields. If MsgStake.PubKey is a new public key and the transaction is signed by one of the existing staked app, the network can assume it’s a transaction to transfer the app to the public key specified in the message. The proposed patch allows such a transaction to be accepted and the world state is updated accordingly.

[1] https://github.com/pokt-network/pocket-core/blob/353dd1047a5621a6dbc8351b28196cd39f5e7445/x/apps/types/msg.go#L25-L29
[2] https://github.com/pokt-network/pocket-core/blob/353dd1047a5621a6dbc8351b28196cd39f5e7445/x/auth/types/stdtx.go#L71-L77
[3] https://github.com/pokt-network/pocket-core/blob/353dd1047a5621a6dbc8351b28196cd39f5e7445/x/auth/types/stdtx.go#L224-L227

Dissenting Opinions

This is a security improvement by introducing a secure way to transfer an app which cannot be done securely. We believe there is no downside in the proposal.


  • New unit tests to verify the new features will be included in the PR.
  • We perform end-to-end testing, where we submit a transaction to transfer the app stake correctly, on Pocket Testnet before upgrading Mainnet.
  • We prepare upgrade plan after this proposal is approved.


This proposal includes a draft GitHub PR above.


Here’s an example command to submit a tx with the new command *apps transfer".

export POKT=<path to pocket>
export CURRENT_APP=<current app address>
export NEW_APP_PUBKEY=<new public key to transfer the app to>
export NETWORK=mainnet
$POKT apps transfer $CURRENT_APP $NEW_APP_PUBKEY $NETWORK 10000 "Transfer app!"

Why does this new command take a public key for a destination account the stake is trasferred to?

It’s because as shown above, the message AppMsgStake holds a public key. We cannot convert an address into a public key unless its private key is imported into the local keybase store. In a typical app-transfer scenario, a destination account is owned by a different entity and its private key shouldn’t be shared.


  • If this proposal is approved, and after testing has taken place for BETA-0.11.0 on Testnet, RC-0.11.0 will be published for node runner adoption. Anyone can monitor node runner adoption of RC-0.11.0 using the Servicers and Validators version pie charts displayed here or the equivalent “Staked Tokens by Node Version” chart displayed here.
  • Once ≥67% of validator power has updated to this version, an upgrade height will be selected by the Foundation Directors based on the preferences expressed by node runners (e.g. through Discord polls) and they will pass this height to the chain through the pocket gov upgrade transaction.
  • The upgrade height chosen by the Foundation Directors will be communicated in advance to ensure node runners are prepared to react if necessary. A time will be targeted that maximizes the availability of node runners, accounting for all time zones and working hours.
  • Once the network is upgraded, the new feature will be enabled by the Foundation submitting the pocket gov enable txs and working with the protocol developers to ensure there are no issues.


There will be no external audit, refer to Viability.


Copyright and related rights waived via CC0.


This seems like a sensible improvement to app stakes to close a potential vulnerability.

As app stakes become more visible and used as we switch to a multi-gateway future, vulnerabilities and potential issues will increasingly come to light and should be acted on appropriately.

I support these proposed changes.