Skip to content
Merged
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
15 changes: 12 additions & 3 deletions modules/sdk-coin-stx/src/lib/contractBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
} from '@stacks/transactions';
import { InvalidParameterValueError } from '@bitgo/sdk-core';
import { Transaction } from './transaction';
import { contractPrincipalCVFromString, isValidAddress, standardPrincipalCVFromString } from './utils';
import { contractPrincipalCVFromString, isExpired, isValidAddress, standardPrincipalCVFromString } from './utils';
import { ClarityValueJson } from './iface';
import { Utils } from '.';
import { CONTRACT_NAME_SENDMANY, VALID_POX5_CONTRACT_FUNCTION_NAMES, VALID_STAKING_CONTRACT_NAMES } from './constants';
Expand Down Expand Up @@ -61,6 +61,15 @@ export class ContractBuilder extends AbstractContractBuilder {
return this;
}

/**
* Caller-supplied pairs plus this network's active (non-expired) pairs. Network pairs are
* pair-scoped (name + address), so a canonical name never binds to a foreign address.
*/
private getAllowedStakingContracts(): ReadonlyArray<{ name: string; address: string }> {
const networkPairs = (this._coinConfig.network as BitgoStacksNetwork).allowedStakingContracts ?? [];
return [...this._allowedStakingContracts, ...networkPairs.filter((pair) => !isExpired(pair.expiresAt))];
}

/** True when the current (name, address) is a canonical pair or an explicitly allowlisted pair. */
private isContractPairValid(): boolean {
const isCanonicalName =
Expand All @@ -72,7 +81,7 @@ export class ContractBuilder extends AbstractContractBuilder {
if (isCanonicalName && isCanonicalAddress) {
return true;
}
return this._allowedStakingContracts.some(
return this.getAllowedStakingContracts().some(
(contract) => contract.name === this._contractName && contract.address === this._contractAddress
);
}
Expand Down Expand Up @@ -106,7 +115,7 @@ export class ContractBuilder extends AbstractContractBuilder {
throw new InvalidParameterValueError('Invalid address');
}
const isCanonicalAddress = Utils.isValidContractAddress(address, this._coinConfig.network as BitgoStacksNetwork);
const isAllowedAddress = this._allowedStakingContracts.some((contract) => contract.address === address);
const isAllowedAddress = this.getAllowedStakingContracts().some((contract) => contract.address === address);
if (!isCanonicalAddress && !isAllowedAddress) {
throw new InvalidParameterValueError('Invalid contract address');
}
Expand Down
12 changes: 8 additions & 4 deletions modules/sdk-coin-stx/src/lib/pox5Builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
FUNCTION_NAME_UNSTAKE,
FUNCTION_NAME_UPDATE_BOND_REGISTRATION,
} from './constants';
import { contractPrincipalCVFromString, standardPrincipalCVFromString } from './utils';
import { contractPrincipalCVFromString, isExpired, standardPrincipalCVFromString } from './utils';

type Integer = bigint | number | string;
type ByteValue = Buffer | Uint8Array | string;
Expand Down Expand Up @@ -167,10 +167,14 @@ export class Pox5Builder extends ContractBuilder {
}

public static isValidContractCall(coinConfig: Readonly<CoinConfig>, payload: ContractCallPayload): boolean {
return (
(coinConfig.network as BitgoStacksNetwork).stakingContractAddress === addressToString(payload.contractAddress) &&
payload.contractName.content === CONTRACT_NAME_POX5
const network = coinConfig.network as BitgoStacksNetwork;
const address = addressToString(payload.contractAddress);
const isCanonical = network.stakingContractAddress === address;
// A configured (non-expired) network pair may point pox-5 at a different deployer.
const isConfiguredPair = (network.allowedStakingContracts ?? []).some(
(pair) => pair.name === CONTRACT_NAME_POX5 && pair.address === address && !isExpired(pair.expiresAt)
);
return (isCanonical || isConfiguredPair) && payload.contractName.content === CONTRACT_NAME_POX5;
Comment thread
Doddanna17 marked this conversation as resolved.
}

registerForBond(params: Pox5RegisterForBondParams): this {
Expand Down
18 changes: 18 additions & 0 deletions modules/sdk-coin-stx/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,24 @@ export function isValidContractAddress(addr: string, network: BitgoStacksNetwork
);
}

/**
* Whether a configured staking-contract allowance has expired.
*
* @param {string} [expiresAt] - ISO-8601 date (e.g. `2027-03-31`). Undefined ⇒ never expires.
* @returns {boolean} - true when the entry is in the past and must no longer be honoured; an
* unparseable expiry fails closed (treated as expired)
*/
export function isExpired(expiresAt?: string): boolean {
if (!expiresAt) {
return false;
}
const expiry = Date.parse(expiresAt);
if (Number.isNaN(expiry)) {
return true;
}
return Date.now() > expiry;
Comment thread
Doddanna17 marked this conversation as resolved.
}

/**
* Check if the name is one of valid contract names
*
Expand Down
Loading
Loading