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.
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).
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> \
--jsonAdjust 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.
Never paste a signer private key or seed phrase into a website, a chat, or an email. Generate the grant on the signer host.
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:
grant-signer-keyrecords the grant.register-signerbinds 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-manageris the contract principal the key is bound to. Every staker who later stakes through that manager relies on this one grant.auth-idis a replay guard. The tuple(signer-key, signer-manager, auth-id)can be consumed exactly once. Reusing it fails withERR_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
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?