Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 8 additions & 1 deletion src/lib/config/contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@ export const MAINNET_CONTRACTS = {
BNS: "SP000000000000000000002Q6VF78.bns",

// Stacking
// NOTE: pox-4 is superseded by pox-5 at Epoch 4.0 activation (stacks-core 4.0.0,
// Bitcoin height ~960230, ~2026-07-29). Existing pox-4 locks are unaffected, but new
// stack/delegate/extend/increase calls must target the currently active PoX contract.
// stacking.service.ts resolves this dynamically via /v2/pox `contract_id`; POX_4/POX_5
// here are fallbacks only — do not hardcode either into new call sites.
POX_4: "SP000000000000000000002Q6VF78.pox-4",
POX_5: "SP000000000000000000002Q6VF78.pox-5",

// ALEX DEX (SDK handles most operations, but we need pool contract for queries)
ALEX_AMM_POOL: "SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9.amm-swap-pool-v1-1",
Expand Down Expand Up @@ -165,8 +171,9 @@ export const TESTNET_CONTRACTS = {
// BNS
BNS: "ST000000000000000000002AMW42H.bns",

// Stacking
// Stacking (see mainnet note above re: pox-4 -> pox-5)
POX_4: "ST000000000000000000002AMW42H.pox-4",
POX_5: "ST000000000000000000002AMW42H.pox-5",

// ERC-8004 Identity & Reputation
IDENTITY_REGISTRY: "ST3YT0XW92E6T2FE59B2G5N2WNNFSBZ6MZKQS5D18.identity-registry-v2",
Expand Down
31 changes: 25 additions & 6 deletions src/lib/services/stacking.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,33 @@ export class StackingService {
return this.hiro.getPoxInfo();
}

/**
* Resolve the currently active PoX contract from the node (/v2/pox `contract_id`)
* rather than a hardcoded version. This is what makes stack/delegate/extend/increase
* calls survive PoX version transitions (e.g. pox-4 -> pox-5 at Epoch 4.0) without a
* code change. Falls back to the last-known POX_4 constant if the node call fails.
*/
private async getActivePoxContract(): Promise<string> {
try {
const poxInfo = await this.hiro.getPoxInfo();
if (poxInfo.contract_id) {
return poxInfo.contract_id;
}
} catch {
// fall through to static fallback
}
return this.contracts.POX_4;
}

/**
* Get stacking status for an address
* Note: Returns whether the address is stacking, but detailed amounts require proper CV parsing
*/
async getStackingStatus(address: string): Promise<StackingStatus> {
try {
const poxContract = await this.getActivePoxContract();
const result = await this.hiro.callReadOnlyFunction(
this.contracts.POX_4,
poxContract,
"get-stacker-info",
[{ type: "principal", value: address } as unknown as ClarityValue],
address
Expand Down Expand Up @@ -87,7 +106,7 @@ export class StackingService {
startBurnHeight: number,
lockPeriod: number
): Promise<TransferResult> {
const { address: contractAddress, name: contractName } = parseContractId(this.contracts.POX_4);
const { address: contractAddress, name: contractName } = parseContractId(await this.getActivePoxContract());

const functionArgs: ClarityValue[] = [
uintCV(amount),
Expand Down Expand Up @@ -123,7 +142,7 @@ export class StackingService {
extendCount: number,
poxAddress: { version: number; hashbytes: string }
): Promise<TransferResult> {
const { address: contractAddress, name: contractName } = parseContractId(this.contracts.POX_4);
const { address: contractAddress, name: contractName } = parseContractId(await this.getActivePoxContract());

const functionArgs: ClarityValue[] = [
uintCV(extendCount),
Expand All @@ -150,7 +169,7 @@ export class StackingService {
account: Account,
increaseAmount: bigint
): Promise<TransferResult> {
const { address: contractAddress, name: contractName } = parseContractId(this.contracts.POX_4);
const { address: contractAddress, name: contractName } = parseContractId(await this.getActivePoxContract());

const functionArgs: ClarityValue[] = [uintCV(increaseAmount)];

Expand Down Expand Up @@ -180,7 +199,7 @@ export class StackingService {
untilBurnHeight?: number,
poxAddress?: { version: number; hashbytes: string }
): Promise<TransferResult> {
const { address: contractAddress, name: contractName } = parseContractId(this.contracts.POX_4);
const { address: contractAddress, name: contractName } = parseContractId(await this.getActivePoxContract());

const functionArgs: ClarityValue[] = [
uintCV(amount),
Expand Down Expand Up @@ -208,7 +227,7 @@ export class StackingService {
* Revoke delegation
*/
async revokeDelegation(account: Account): Promise<TransferResult> {
const { address: contractAddress, name: contractName } = parseContractId(this.contracts.POX_4);
const { address: contractAddress, name: contractName } = parseContractId(await this.getActivePoxContract());

// No assets moved from sender (revokes delegation permission)
return callContract(account, {
Expand Down
Loading