Skip to content

Commit

Permalink
fix: add getSecondsUntilStackingDeadline in addition to getSecondsUnt…
Browse files Browse the repository at this point in the history
…ilNextCycle
  • Loading branch information
janniks committed Jul 14, 2023
1 parent ee4df48 commit c6b31e2
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 33 deletions.
6 changes: 3 additions & 3 deletions packages/stacking/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ npm install @stacks/stacking
- [Client helpers](#client-helpers)
- [Will Stacking be executed in the next cycle?](#will-stacking-be-executed-in-the-next-cycle)
- [How long (in seconds) is a Stacking cycle?](#how-long-in-seconds-is-a-stacking-cycle)
- [How much time is left (in seconds) until the next cycle begins?](#how-much-time-is-left-in-seconds-until-the-next-cycle-begins)
- [How much estimated time is left (in seconds) to submit a stacking transaction for the upcoming reward cycle?](#how-much-estimated-time-is-left-in-seconds-to-submit-a-stacking-transaction-for-the-upcoming-reward-cycle)
- [Does account have sufficient STX to meet minimum threshold?](#does-account-have-sufficient-stx-to-meet-minimum-threshold)
- [Get PoX info](#get-pox-info)
- [Get Stacks node info](#get-stacks-node-info)
Expand Down Expand Up @@ -169,10 +169,10 @@ const cycleDuration = await client.getCycleDuration();
// 120
```

### How much time is left (in seconds) until the next cycle begins?
### How much estimated time is left (in seconds) to submit a stacking transaction for the upcoming reward cycle?

```typescript
const secondsUntilNextCycle = await client.getSecondsUntilNextCycle();
const seconds = await client.getSecondsUntilStackingDeadline();

// 600000
```
Expand Down
49 changes: 25 additions & 24 deletions packages/stacking/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -380,8 +380,7 @@ export class StackingClient {

/**
* Get account balance
*
* @returns promise resolves to a bigint if the operation succeeds
* @returns {Promise<bigint>} resolves to a bigint if the operation succeeds
*/
async getAccountBalance(): Promise<bigint> {
return this.getAccountStatus().then(res => {
Expand All @@ -391,8 +390,7 @@ export class StackingClient {

/**
* Get extended account balances
*
* @returns promise resolves to a bigint if the operation succeeds
* @returns {Promise<AccountExtendedBalances>} resolves to an AccountExtendedBalances response if the operation succeeds
*/
async getAccountExtendedBalances(): Promise<AccountExtendedBalances> {
const url = this.network.getAccountExtendedBalancesApiUrl(this.address);
Expand All @@ -401,16 +399,14 @@ export class StackingClient {

/**
* Get account balance of locked tokens
*
* @returns promise resolves to a bigint if the operation succeeds
* @returns {Promise<bigint>} resolves to a bigint if the operation succeeds
*/
async getAccountBalanceLocked(): Promise<bigint> {
return this.getAccountStatus().then(res => BigInt(res.locked));
}

/**
* Get reward cycle duration in seconds
*
* @returns {Promise<number>} that resolves to a number if the operation succeeds
*/
async getCycleDuration(): Promise<number> {
Expand All @@ -426,7 +422,6 @@ export class StackingClient {

/**
* Get the total burnchain rewards total for the set address
*
* @returns {Promise<TotalRewardsResponse | RewardsError>} that resolves to TotalRewardsResponse or RewardsError
*/
async getRewardsTotalForBtcAddress(): Promise<BurnchainRewardsTotal | RewardsError> {
Expand All @@ -436,7 +431,6 @@ export class StackingClient {

/**
* Get burnchain rewards for the set address
*
* @returns {Promise<RewardsResponse | RewardsError>} that resolves to RewardsResponse or RewardsError
*/
async getRewardsForBtcAddress(
Expand All @@ -448,7 +442,6 @@ export class StackingClient {

/**
* Get burnchain rewards holders for the set address
*
* @returns {Promise<RewardHoldersResponse | RewardsError>} that resolves to RewardHoldersResponse or RewardsError
*/
async getRewardHoldersForBtcAddress(
Expand All @@ -460,7 +453,6 @@ export class StackingClient {

/**
* Get PoX address from reward set by index
*
* @returns {Promise<RewardSetInfo | undefined>} that resolves to RewardSetInfo if the entry exists
*/
async getRewardSet(options: RewardSetOptions): Promise<RewardSetInfo | undefined> {
Expand All @@ -485,8 +477,10 @@ export class StackingClient {

/**
* Get number of seconds until next reward cycle
*
* @returns {Promise<number>} that resolves to a number if the operation succeeds
*
* See also:
* - {@link getSecondsUntilStackingDeadline}
*/
async getSecondsUntilNextCycle(): Promise<number> {
const poxInfoPromise = this.getPoxInfo();
Expand All @@ -504,6 +498,25 @@ export class StackingClient {
);
}

/**
* Get number of seconds until the end of the stacking deadline.
* This is the estimated time stackers have to submit their stacking
* transactions to be included in the upcoming reward cycle.
* @returns {Promise<number>} that resolves to a number if the operation succeeds
*
* See also:
* - {@link getSecondsUntilNextCycle}
*/
async getSecondsUntilStackingDeadline(): Promise<number> {
const poxInfoPromise = this.getPoxInfo();
const targetBlockTimePromise = this.getTargetBlockTime();

return Promise.all([poxInfoPromise, targetBlockTimePromise]).then(
([poxInfo, targetBlockTime]) =>
poxInfo.next_cycle.blocks_until_prepare_phase * targetBlockTime
);
}

/**
* Get information on current PoX operation
*
Expand Down Expand Up @@ -592,7 +605,6 @@ export class StackingClient {

/**
* Check if account has minimum require amount of Stacks for stacking
*
* @returns {Promise<boolean>} that resolves to a bool if the operation succeeds
*/
async hasMinimumStx(): Promise<boolean> {
Expand All @@ -603,9 +615,7 @@ export class StackingClient {

/**
* Check if account can lock stx
*
* @param {CanLockStxOptions} options - a required lock STX options object
*
* @returns {Promise<StackingEligibility>} that resolves to a StackingEligibility object if the operation succeeds
*/
async canStack({ poxAddress, cycles }: CanLockStxOptions): Promise<StackingEligibility> {
Expand Down Expand Up @@ -648,9 +658,7 @@ export class StackingClient {

/**
* Generate and broadcast a stacking transaction to lock STX
*
* @param {LockStxOptions} options - a required lock STX options object
*
* @returns {Promise<string>} that resolves to a broadcasted txid if the operation succeeds
*/
async stack({
Expand Down Expand Up @@ -737,9 +745,7 @@ export class StackingClient {

/**
* As a delegatee, generate and broadcast a transaction to create a delegation relationship
*
* @param {DelegateStxOptions} options - a required delegate STX options object
*
* @returns {Promise<string>} that resolves to a broadcasted txid if the operation succeeds
*/
async delegateStx({
Expand Down Expand Up @@ -774,9 +780,7 @@ export class StackingClient {

/**
* As a delegator, generate and broadcast transactions to stack for multiple delegatees. This will lock up tokens owned by the delegatees.
*
* @param {DelegateStackStxOptions} options - a required delegate stack STX options object
*
* @returns {Promise<string>} that resolves to a broadcasted txid if the operation succeeds
*/
async delegateStackStx({
Expand Down Expand Up @@ -870,9 +874,7 @@ export class StackingClient {

/**
* As a delegator, generate and broadcast a transaction to commit partially committed delegatee tokens
*
* @param {StackAggregationCommitOptions} options - a required stack aggregation commit options object
*
* @returns {Promise<string>} that resolves to a broadcasted txid if the operation succeeds
*/
async stackAggregationCommit({
Expand Down Expand Up @@ -938,7 +940,6 @@ export class StackingClient {

/**
* As a delegator, generate and broadcast a transaction to increase partial commitment committed delegatee tokens
*
* @param {StackAggregationIncreaseOptions} options - a required stack aggregation increase options object
* @category PoX-2
* @returns {Promise<string>} that resolves to a broadcasted txid if the operation succeeds
Expand Down
34 changes: 28 additions & 6 deletions packages/stacking/tests/stacking.test.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
import { bigIntToBytes, bytesToHex, hexToBytes } from '@stacks/common';
import { base58CheckDecode } from '@stacks/encryption';
import { StacksTestnet } from '@stacks/network';
import { StacksMainnet, StacksTestnet } from '@stacks/network';
import {
AnchorMode,
bufferCV,
ClarityType,
ReadOnlyFunctionOptions,
SignedContractCallOptions,
TupleCV,
bufferCV,
intCV,
noneCV,
ReadOnlyFunctionOptions,
responseErrorCV,
responseOkCV,
SignedContractCallOptions,
someCV,
standardPrincipalCV,
trueCV,
tupleCV,
TupleCV,
uintCV,
validateContractCall,
} from '@stacks/transactions';
import fetchMock from 'jest-fetch-mock';
import { StackingClient } from '../src';
import { PoXAddressVersion, StackingErrors } from '../src/constants';
import { decodeBtcAddress, poxAddressToBtcAddress } from '../src/utils';
import { V2_POX_REGTEST_POX_3 } from './apiMockingHelpers';
import { V2_POX_REGTEST_POX_3, setApiMocks } from './apiMockingHelpers';

const poxInfo = {
contract_id: 'ST000000000000000000002AMW42H.pox',
Expand Down Expand Up @@ -1084,3 +1085,24 @@ test('client operations with contract principal stacker', () => {
);
expect(async () => await client.getStatus()).not.toThrow();
});

test('getSecondsUntilStackingDeadline', async () => {
const network = new StacksMainnet({ url: 'http://localhost:3999' });
const client = new StackingClient('', network);

setApiMocks({
'/extended/v1/info/network_block_times': `{"testnet":{"target_block_time":120},"mainnet":{"target_block_time":600}}`,
'/v2/pox': `{"contract_id":"ST000000000000000000002AMW42H.pox-3","pox_activation_threshold_ustx":600058115845055,"first_burnchain_block_height":0,"current_burnchain_block_height":275,"prepare_phase_block_length":1,"reward_phase_block_length":4,"reward_slots":8,"rejection_fraction":3333333333333333,"total_liquid_supply_ustx":60005811584505576,"current_cycle":{"id":54,"min_threshold_ustx":1875190000000000,"stacked_ustx":0,"is_pox_active":false},"next_cycle":{"id":55,"min_threshold_ustx":1875190000000000,"min_increment_ustx":7500726448063,"stacked_ustx":0,"prepare_phase_start_block_height":279,"blocks_until_prepare_phase":4,"reward_phase_start_block_height":280,"blocks_until_reward_phase":5,"ustx_until_pox_rejection":14656114351294034000},"min_amount_ustx":1875190000000000,"prepare_cycle_length":1,"reward_cycle_id":54,"reward_cycle_length":5,"rejection_votes_left_required":14656114351294034000,"next_reward_cycle_in":5,"contract_versions":[{"contract_id":"ST000000000000000000002AMW42H.pox","activation_burnchain_block_height":0,"first_reward_cycle_id":0},{"contract_id":"ST000000000000000000002AMW42H.pox-2","activation_burnchain_block_height":107,"first_reward_cycle_id":22},{"contract_id":"ST000000000000000000002AMW42H.pox-3","activation_burnchain_block_height":111,"first_reward_cycle_id":23}]}`,
});

let seconds = await client.getSecondsUntilStackingDeadline();
expect(seconds).toBe(4 * 10 * 60); // four blocks until prepare phase

setApiMocks({
'/extended/v1/info/network_block_times': `{"testnet":{"target_block_time":120},"mainnet":{"target_block_time":600}}`,
'/v2/pox': `{"contract_id":"ST000000000000000000002AMW42H.pox-3","pox_activation_threshold_ustx":600058812952055,"first_burnchain_block_height":0,"current_burnchain_block_height":344,"prepare_phase_block_length":1,"reward_phase_block_length":4,"reward_slots":8,"rejection_fraction":3333333333333333,"total_liquid_supply_ustx":60005881295205576,"current_cycle":{"id":68,"min_threshold_ustx":1875190000000000,"stacked_ustx":0,"is_pox_active":false},"next_cycle":{"id":69,"min_threshold_ustx":1875190000000000,"min_increment_ustx":7500735161900,"stacked_ustx":0,"prepare_phase_start_block_height":344,"blocks_until_prepare_phase":0,"reward_phase_start_block_height":345,"blocks_until_reward_phase":1,"ustx_until_pox_rejection":5198637306263702000},"min_amount_ustx":1875190000000000,"prepare_cycle_length":1,"reward_cycle_id":68,"reward_cycle_length":5,"rejection_votes_left_required":5198637306263702000,"next_reward_cycle_in":1,"contract_versions":[{"contract_id":"ST000000000000000000002AMW42H.pox","activation_burnchain_block_height":0,"first_reward_cycle_id":0},{"contract_id":"ST000000000000000000002AMW42H.pox-2","activation_burnchain_block_height":107,"first_reward_cycle_id":22},{"contract_id":"ST000000000000000000002AMW42H.pox-3","activation_burnchain_block_height":111,"first_reward_cycle_id":23}]}`,
});

seconds = await client.getSecondsUntilStackingDeadline();
expect(seconds).toBe(0); // this time we are in the prepare phase
});

0 comments on commit c6b31e2

Please sign in to comment.