Skip to content

PoX Contract hardcoded to pox-4 #5

@jannik-stacks

Description

@jannik-stacks

PoX contract is hardcoded to pox-4 — will silently break on the next upgrade

Right now, every stacking operation in this SDK targets pox-4 because the contract address and name are hardcoded in src/utils/constants.ts L76–L85:

export const poxInfo = {
  testnet: { contractAddress: "ST000000000000000000002AMW42H", contractName: "pox-4" },
  mainnet: { contractAddress: "SP000000000000000000002Q6VF78", contractName: "pox-4" },
};

This is then referenced in five places across src/services/stacks.service.ts (lines 291, 795, 850, 902, 956) — every stacking-related method (checkDelegationStatus, delegateStx, revokeStxDelegation, allowPoxContractCaller, soloStack) reads from this constant.

What breaks when pox-5 comes

When a new PoX contract activates:

  1. All stacking calls go to the old contract. stack-stx, delegate-stx, revoke-delegate-stx, allow-contract-caller — all five call sites target pox-4. The old contract will likely reject new stacking operations with an error, or worse, succeed but have no effect on the new reward set.

  2. Signer signatures become invalid. The signer signature domain is pox-4-signer (hardcoded in @stacks/stacking's pox4SignatureMessage()). A pox-5 contract will expect a different domain name. This one will also need updating in the upstream stacks.js lib, but the Fireblocks SDK calls this function in src/utils/helpers.ts L292 and would need to match whatever the new version expects.

  3. Delegation status checks return stale data. checkDelegationStatus() calls get-delegation-info on pox-4. Once pox-5 is active, the canonical delegation state lives in the new contract.

  4. No warning to the user. The SDK doesn't check whether the contract it's targeting is still the active one, so all of this happens silently.

What the SDK already has (but doesn't use)

The SDK already calls /v2/pox in StacksService.fetchPoxInfo() and the response includes contract_id (the currently active contract) and contract_versions. This data is used for eligibility checks and burn height calculations, but the actual contract address for stacking calls is pulled from the hardcoded constant instead.

Suggested fix

Since pox-5 is not finished yet, and will maybe differ in interface, it's fine to not support it immediately.

For now, I believe the best option is to check the active pox contract against the expected one and throw a failure if they don't match, so users don't work with a wrong contract.

This doesn't fix the problem but at least makes it loud instead of silent.

Affected code
File Lines What it does
src/utils/constants.ts 76–85 Hardcoded pox-4 addresses
src/services/stacks.service.ts 291 checkDelegationStatus
src/services/stacks.service.ts 795 delegateStx
src/services/stacks.service.ts 850 revokeStxDelegation
src/services/stacks.service.ts 902 allowPoxContractCaller
src/services/stacks.service.ts 956 soloStack
src/utils/helpers.ts 292 Signer signature generation (uses pox4SignatureMessage)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions