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