For the complete documentation index, see llms.txt. This page is also available as Markdown.

Generate a Signer Signature

How to produce the one-time signer-key grant that ties your signer to a signer-manager contract under PoX-5.

Updated for Stacks 4.x and PoX-5.

PoX-5 uses a one-time signer-key grant. You generate one grant per signer-manager contract and submit it once. Every staking call routed through that manager then relies on it until you revoke it. There is nothing to generate per transaction or per cycle.

The grant is what connects your signer node, the signer-manager contract that stakers stake to, and the pox-5 contract. Without an active grant, stake and every related call against your manager fails with ERR_SIGNER_KEY_GRANT_NOT_FOUND (u17).

Doing the whole setup from the command line? Deploy a Signer Manager Contract runs this step in sequence with the deploy, registration and admin rotation.

Generate the grant

Run this on the signer host, where the private key already lives:

stacks-signer generate-staking-signature \
  --config /etc/stacks-signer/config.toml \
  --signer-manager <manager-principal> \
  --auth-id <unique-id> \
  --json

Adjust the config path to match your setup. Replace <manager-principal> with your signer-manager's contract principal. Replace <unique-id> with a uint you have not used before for this signer key and manager pair.

The command prints JSON. It contains your public key and the signature. It does not contain your private key, which is what makes it safe to move to whatever you use to build the next transaction.

For testing, the signer key helper produces the same JSON offline. It is useful for seeing how the grant is built. For a real deployment, prefer the CLI so the private key never leaves the host.

Submit it on-chain

The grant is signed off-chain. It is submitted on-chain by the signer-manager contract itself, in two calls:

  1. grant-signer-key records the grant.

  2. register-signer binds the manager to the key.

Both require contract-caller to equal the signer-manager. An account cannot submit either on the contract's behalf. Trying fails with ERR_UNAUTHORIZED_SIGNER_REGISTRATION (u26).

The reference signer-manager wraps both into a single register-self entrypoint, so in practice this is one transaction. See Deploy a Signer Manager Contract.

The register-self page takes a completed grant and builds that transaction for you.

What the grant contains

The signed message:

  • signer-manager is the contract principal the key is bound to. Every staker who later stakes through that manager relies on this one grant.

  • auth-id is a replay guard. The tuple (signer-key, signer-manager, auth-id) can be consumed exactly once. Reusing it fails with ERR_SIGNER_KEY_GRANT_USED (u12). Pick a fresh value to issue a new grant.

Those two fields are the whole scope. A grant authorises a signer-manager and stands until you revoke it.

The chain-id in the domain comes from the network field in your signer config. A grant generated against the wrong network fails at grant-signer-key with ERR_INVALID_SIGNATURE_PUBKEY (u14), which reads as a wrong-key problem rather than a wrong-network one.

One grant covers every entrypoint

Entrypoint
Needs an active grant?

stake

Yes

stake-update

Yes, the same grant

register-for-bond

Yes, the same grant

update-bond-registration

Yes. Rotating to a different signer-manager requires that manager to hold its own grant

register-signer

No. Gated on contract-caller == signer-manager instead

Lock period is bounded by MAX_NUM_CYCLES (96) at the stake and register-for-bond level, not by anything in the grant.

Revoke a grant

revoke-signer-grant removes the binding. It takes (signer-manager, signer-key) in that order.

It must be sent directly by the Stacks principal derived from the signer key. No SIP-018 message is needed, and it cannot be forwarded through another contract. Calling it from any other principal fails with ERR_UNAUTHORIZED (u1).

Revoking is not a kill switch. It stops the manager accepting new stake. Existing positions are left intact and wind down as their locks expire.

Using @stacks/bitcoin-staking

The SDK exposes the same flow for tooling:

The package also provides buildSignerGrantMessage, computeSignerGrantHash, and verifySignerGrant as pure functions, plus fetchVerifySignerKeyGrant and fetchSignerKeyGrantUsed for reading on-chain state.

What changed from PoX-4

If you ran a signer before Epoch 4.0, you generated a fresh signature for every call, scoped to that call:

The command was renamed from generate-stacking-signature to generate-staking-signature. Five scoping flags collapsed into a single --signer-manager. The result is reusable, so you run it once per manager rather than once per transaction.

There are no --method, --max-amount, --period, --reward-cycle, or --pox-address flags, because a grant authorises a signer-manager rather than a specific call.

Last updated

Was this helpful?