diff --git a/docs/getting-started.md b/docs/getting-started.md
index 521f9563e..3218c8ac3 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -2,9 +2,9 @@
title: Getting Started
---
-:::info Connect 🌐
-[Stacks.js starters](https://docs.hiro.so/stacksjs-starters) offer working templates with Stacks Connect pre-installed for a quick and easy way to get started with building Stacks enabled web apps.
-:::
+import StacksjsStartersNote from './includes/\_stacks.js-starters-note.mdx';
+
+
---
@@ -16,7 +16,9 @@ Typically we speak of "mainnet" and "testnet" as the networks of Stacks. Most wa
As the name suggests, "testnet" is a network for testing.
It's a separate blockchain state that holds test tokens, which have no value.
-Developers are encouraged to use testnet for testing and development, before rolling out applications and contracts to mainnet. Stacks.js functions can be configured to use wichever network you want.
+Developers are encouraged to use testnet for testing, before rolling out applications and contracts to mainnet.
+For development, there is even Devnet/Mocknet for working in a local development environment.
+Stacks.js functions can be configured to use wichever network you want.
```js
import { StacksMainnet, StacksTestnet } from '@stacks/network';
@@ -152,16 +154,18 @@ The mode can be either `Allow` or `Deny`.
- `Deny` means the transaction will fail if any asset transfers (not specified in the post conditions) are attempted.
:::note
-In either case, all post conditions will still be checked. By default, transactions are set to `Deny` mode, for additional security.
+In either case, all post conditions will still be checked.
+By default, transactions are set to `Deny` mode, for additional security.
:::
## Broadcasting
:::info Connect 🌐
-For web apps via Stacks Connect, the users' wallet will broadcast the transaction and return a txid. [Read more](https://connect.stacks.js.org/modules/_stacks_connect)
+For web apps via Stacks Connect, the users' wallet will broadcast the transaction and return a txid.
+[Read more](https://connect.stacks.js.org/modules/_stacks_connect)
:::
-A finalized transaction can be broadcasted to the network or serialized (to bytes representation) using Stacks.js.
+A finalized transaction can be broadcasted to the network or serialized (to a byte representation) using Stacks.js.
```js
import { bytesToHex } from '@stacks/common';
diff --git a/docs/includes/_stacks.js-starters-note.mdx b/docs/includes/_stacks.js-starters-note.mdx
index 8829f43b5..1f147ea5e 100644
--- a/docs/includes/_stacks.js-starters-note.mdx
+++ b/docs/includes/_stacks.js-starters-note.mdx
@@ -1,3 +1,3 @@
:::info Connect 🌐
-Use our prebuilt [Stacks.js starter templates](/stacksjs-starters) to kickstart your frontend web application development with your preferred JavaScript framework.
+[Stacks.js starters](https://docs.hiro.so/stacksjs-starters) offer working templates with Stacks Connect pre-installed for a quick and easy way to get started with building Stacks enabled web apps.
:::
diff --git a/docs/overview.md b/docs/overview.md
index ed7b7adf1..becc82186 100644
--- a/docs/overview.md
+++ b/docs/overview.md
@@ -11,7 +11,7 @@ It's a collection of various JavaScript libraries allowing developers to interac
There are two main ways developers build applications on the Stacks blockchain:
-- 🔒 **Without Direct Private Key Access**: For example a web app that allows users to interact with the Stacks blockchain using their Stacks wallet (browser extension or mobile). Read More in the Connect Guide
+- 🔒 **Without Direct Private Key Access**: For example, a web app that allows users to interact with the Stacks blockchain using their Stacks wallet (browser extension or mobile). Read More in the Connect Guide
- 🔑 **With Private Key Access**: For example, managing funds with the Stacks.js CLI, building a backend (which can sign transactions directly).
Most users interact via their favorite Stacks wallet.
@@ -28,7 +28,7 @@ In these cases, developers can use the same libraries used by Stacks wallets for
There are three main integrations used by Stacks enabled applications:
-
+
- **Authentication**: Register and sign users in with identities on the Stacks blockchain
- **Transaction signing**: Prompt users to sign and broadcast transactions to the Stacks blockchain
diff --git a/package.json b/package.json
index b59bda265..afeccb860 100644
--- a/package.json
+++ b/package.json
@@ -4,6 +4,7 @@
"workspaces": [
"packages/**"
],
+ "license": "MIT",
"prettier": "@stacks/prettier-config",
"scripts": {
"bootstrap": "lerna bootstrap",
diff --git a/packages/stacking/README.md b/packages/stacking/README.md
index 9780e2f1d..112d912fe 100644
--- a/packages/stacking/README.md
+++ b/packages/stacking/README.md
@@ -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)
@@ -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
```
diff --git a/packages/stacking/src/index.ts b/packages/stacking/src/index.ts
index 04a5079e6..a00368488 100644
--- a/packages/stacking/src/index.ts
+++ b/packages/stacking/src/index.ts
@@ -360,7 +360,7 @@ export class StackingClient {
/**
* Get stacks node target block time
*
- * @returns {Promise} that resolves to a number if the operation succeeds
+ * @returns {Promise} resolves to a number if the operation succeeds
*/
async getTargetBlockTime(): Promise {
const url = this.network.getBlockTimeInfoUrl();
@@ -380,8 +380,7 @@ export class StackingClient {
/**
* Get account balance
- *
- * @returns promise resolves to a bigint if the operation succeeds
+ * @returns {Promise} resolves to a bigint if the operation succeeds
*/
async getAccountBalance(): Promise {
return this.getAccountStatus().then(res => {
@@ -391,8 +390,7 @@ export class StackingClient {
/**
* Get extended account balances
- *
- * @returns promise resolves to a bigint if the operation succeeds
+ * @returns {Promise} resolves to an AccountExtendedBalances response if the operation succeeds
*/
async getAccountExtendedBalances(): Promise {
const url = this.network.getAccountExtendedBalancesApiUrl(this.address);
@@ -401,8 +399,7 @@ export class StackingClient {
/**
* Get account balance of locked tokens
- *
- * @returns promise resolves to a bigint if the operation succeeds
+ * @returns {Promise} resolves to a bigint if the operation succeeds
*/
async getAccountBalanceLocked(): Promise {
return this.getAccountStatus().then(res => BigInt(res.locked));
@@ -410,8 +407,7 @@ export class StackingClient {
/**
* Get reward cycle duration in seconds
- *
- * @returns {Promise} that resolves to a number if the operation succeeds
+ * @returns {Promise} resolves to a number if the operation succeeds
*/
async getCycleDuration(): Promise {
const poxInfoPromise = this.getPoxInfo();
@@ -426,7 +422,6 @@ export class StackingClient {
/**
* Get the total burnchain rewards total for the set address
- *
* @returns {Promise} that resolves to TotalRewardsResponse or RewardsError
*/
async getRewardsTotalForBtcAddress(): Promise {
@@ -436,7 +431,6 @@ export class StackingClient {
/**
* Get burnchain rewards for the set address
- *
* @returns {Promise} that resolves to RewardsResponse or RewardsError
*/
async getRewardsForBtcAddress(
@@ -448,7 +442,6 @@ export class StackingClient {
/**
* Get burnchain rewards holders for the set address
- *
* @returns {Promise} that resolves to RewardHoldersResponse or RewardsError
*/
async getRewardHoldersForBtcAddress(
@@ -460,7 +453,6 @@ export class StackingClient {
/**
* Get PoX address from reward set by index
- *
* @returns {Promise} that resolves to RewardSetInfo if the entry exists
*/
async getRewardSet(options: RewardSetOptions): Promise {
@@ -485,8 +477,10 @@ export class StackingClient {
/**
* Get number of seconds until next reward cycle
+ * @returns {Promise} resolves to a number if the operation succeeds
*
- * @returns {Promise} that resolves to a number if the operation succeeds
+ * See also:
+ * - {@link getSecondsUntilStackingDeadline}
*/
async getSecondsUntilNextCycle(): Promise {
const poxInfoPromise = this.getPoxInfo();
@@ -504,6 +498,26 @@ 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} resolves to a number of seconds if the operation succeeds.
+ * **⚠️ Attention**: The returned number of seconds can be negative if the deadline has passed and the prepare phase has started.
+ *
+ * See also:
+ * - {@link getSecondsUntilNextCycle}
+ */
+ async getSecondsUntilStackingDeadline(): Promise {
+ 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
*
@@ -592,7 +606,6 @@ export class StackingClient {
/**
* Check if account has minimum require amount of Stacks for stacking
- *
* @returns {Promise} that resolves to a bool if the operation succeeds
*/
async hasMinimumStx(): Promise {
@@ -603,9 +616,7 @@ export class StackingClient {
/**
* Check if account can lock stx
- *
* @param {CanLockStxOptions} options - a required lock STX options object
- *
* @returns {Promise} that resolves to a StackingEligibility object if the operation succeeds
*/
async canStack({ poxAddress, cycles }: CanLockStxOptions): Promise {
@@ -648,9 +659,7 @@ export class StackingClient {
/**
* Generate and broadcast a stacking transaction to lock STX
- *
* @param {LockStxOptions} options - a required lock STX options object
- *
* @returns {Promise} that resolves to a broadcasted txid if the operation succeeds
*/
async stack({
@@ -737,9 +746,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} that resolves to a broadcasted txid if the operation succeeds
*/
async delegateStx({
@@ -774,9 +781,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} that resolves to a broadcasted txid if the operation succeeds
*/
async delegateStackStx({
@@ -870,9 +875,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} that resolves to a broadcasted txid if the operation succeeds
*/
async stackAggregationCommit({
@@ -938,7 +941,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} that resolves to a broadcasted txid if the operation succeeds
diff --git a/packages/stacking/tests/stacking.test.ts b/packages/stacking/tests/stacking.test.ts
index cb06b96dd..d230508e0 100644
--- a/packages/stacking/tests/stacking.test.ts
+++ b/packages/stacking/tests/stacking.test.ts
@@ -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',
@@ -1084,3 +1085,34 @@ 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
+
+ // warning: manually changed response to negative value
+ 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":-50,"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).toBeLessThan(0); // negative (deadline passed)
+ expect(seconds).toBe(-50 * 10 * 60); // this time we are in the prepare phase
+});
diff --git a/packages/transactions/README.md b/packages/transactions/README.md
index c7744acdb..562b36f41 100644
--- a/packages/transactions/README.md
+++ b/packages/transactions/README.md
@@ -294,7 +294,7 @@ const result = await callReadOnlyFunction(options);
## Constructing Clarity Values
-Building transactions that call functions in deployed clarity contracts requires you to construct valid Clarity Values to pass to the function as arguments. The [Clarity type system](https://github.com/blockstack/stacks-blockchain/blob/master/sip/sip-002-smart-contract-language.md#clarity-type-system) contains the following types:
+Building transactions that call functions in deployed clarity contracts requires you to construct valid Clarity Values to pass to the function as arguments. The [Clarity type system](https://github.com/stacksgov/sips/blob/master/sip/sip-002-smart-contract-language.md#clarity-type-system) contains the following types:
- `(tuple (key-name-0 key-type-0) (key-name-1 key-type-1) ...)`
- a typed tuple with named fields.
@@ -386,7 +386,7 @@ Three types of post conditions can be added to transactions:
2. Fungible token post condition
3. Non-Fungible token post condition
-For details see: https://github.com/blockstack/stacks-blockchain/blob/master/sip/sip-005-blocks-and-transactions.md#transaction-post-conditions
+For details see: https://github.com/stacksgov/sips/blob/main/sips/sip-005/sip-005-blocks-and-transactions.md#transaction-post-conditions
### STX post condition