diff --git a/docs/faq.md b/docs/faq.md deleted file mode 100644 index 55e7e10d7..000000000 --- a/docs/faq.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -title: FAQs ---- - -#### **What are Post Conditions?** - -- Any supplied post-conditions are always verified, regardless of "mode" (and abort the tx if any supplied PC evaluates to false). -- The "mode" (allow/deny) only applies to any asset (stx/ft/nft) transfer that is not mentioned in the post-conditions (can be thought of as "ALLOW-additional-asset-transfer" or "DENY-additional-asset-transfer") - -Example: In deny mode, an additional asset transfer (not covered by PCs) will abort the tx. In deny mode without PCs a tx will only fail due to PCs if an asset is transferred. - -Post-conditions are less a part of clarity (the language), but more a part of transactions. -Users could send the otherwise-identical transaction (Example: contract-call, executing a function on the blockchain) with OR without different post-conditions, in allow OR deny mode. -The PCs are managed by the user/wallet/app that's creating the tx; so they are a bit different from the other "safety" features of clarity (Example: asserts, try, https://book.clarity-lang.org/ch06-00-control-flow.html) - -#### **How to fix the regenerator-runtime?** - -If using @stacks/connect with vite, rollup, svelte, or vue a package `regenerator-runtime` needs to be manually added to successfully build the project. - -(Fix is in progress) - -#### **How to fix the BigInt or how to fix if BigInt doesn’t support X?** - -BigInt’s work in all modern browsers, but some bundlers try to optimize them incorrectly. If you are targeting browsers that are too outdated, BigInts might fail. -To solve this set your project `browserslist` to the following [package.json](https://github.com/hirosystems/stacks.js-starters/blob/efb93261b59494f4eb34a7cb5db5d82a84bd3b7c/templates/template-react/package.json#L34-L40) diff --git a/docs/feature-guides/authenticate-users-with-connect.md b/docs/feature-guides/authenticate-users-with-connect.md deleted file mode 100644 index 0b41da412..000000000 --- a/docs/feature-guides/authenticate-users-with-connect.md +++ /dev/null @@ -1,261 +0,0 @@ ---- -id: authenticate-users-with-connect -title: Connecting Wallets & Authenticating ---- - -import StacksjsStartersNote from '../includes/\_stacks.js-starters-note.mdx'; - - - ---- - -This guide explains how to connect to users' wallets and authenticate users with the [`@stacks/connect`](https://github.com/hirosystems/connect#readme) package of Stacks.js. - -## Installing Dependency - -To install the `@stacks/connect` package in your JavaScript/TypeScript project, run: - -``` -npm install @stacks/connect -``` - -## Authentication - -Authentication allows users to identify themselves in an app while retaining complete control over their credentials and personal details. This package can be integrated alone or used in conjunction with [transaction signing](/build-apps/transaction-signing) and [data storage](/build-apps/data-storage). The authentication package is a prerequisite for using these latter two packages. - -Users who register for your app can subsequently authenticate to any other app with support for the [Blockchain Naming System](https://docs.stacks.co/build-apps/references/bns) and vice versa. - -### Initiate `userSession` - -Apps keep track of user authentication state with the `userSession` object, initiated with the `UserSession` and `AppConfig` classes: - -```js -import { AppConfig, UserSession } from '@stacks/connect'; - -const appConfig = new AppConfig(['store_write', 'publish_data']); -const userSession = new UserSession({ appConfig }); -``` - -The main thing to decide here is what permission scopes your app needs from the user during authentication. - -Apps may request any of the following scopes: - -| Scope | Definition | -| -------------- | ------------------------------------------------------------------------------- | -| `store_write` | Read and write data to the user's Gaia hub in an app-specific storage bucket. | -| `publish_data` | Publish data so other app users can discover and interact with the user. | - -The default scopes are `['store_write']` if no `scopes` array is provided when initializing the `appConfig` object. - -We recommend you initiate the `userSession` object just once in your app and then reference it using imports where needed. - -### Initiate Authentication Flow - -Apps prompt both new and existing users to authenticate with the `showConnect` function: - -```js -import { AppConfig, UserSession, showConnect } from '@stacks/connect'; - -const appConfig = new AppConfig(['store_write', 'publish_data']); -const userSession = new UserSession({ appConfig }); - -function authenticate() { - showConnect({ - appDetails: { - name: 'My App', - icon: window.location.origin + '/my-app-logo.svg', - }, - redirectTo: '/', - onFinish: () => { - let userData = userSession.loadUserData(); - // Save or otherwise utilize userData post-authentication - }, - userSession: userSession, - }); -} -``` - -`showConnect` triggers the display of a popup that initiates the authentication process for users, one in which they'll authenticate with a _Secret Key_ that's used to encrypt their private data. - - - -![Popup displayed by showConnect function](/img/todos-get-started.png) - -The `showConnect` function accepts a several properties within a parameter object, such as: - -- The app's `name` and `icon` are provided as strings comprising the `appDetails` object property. -- The `redirectTo` string is used to provide a URL to which the user should be redirected upon successful authentication. The `onFinish` callback serves a similar purpose by handling successful authentication within the context of a popup window. -- The `userSession` object initiated above. - -Once the user selects the button in this popup, they are passed to the Stacks Wallet for authenticator with the `authRequest` token as a GET parameter. From there, they can confirm authentication and generate a new _Secret Key_ or Stacks identity before doing so, as needed, before coming back to the app. - -### Handle Pending Authentication - -Unless the user has confirmed authentication within the context of a popup window, they will get redirected back to the app via the `redirectTo` address provided above, at which point the app needs to handle the pending authentication state using the `authResponse` value provided as a GET parameter: - -```jsx -import { AppConfig, UserSession, showConnect } from '@stacks/connect'; - -const appConfig = new AppConfig(['store_write', 'publish_data']); -const userSession = new UserSession({ appConfig }); - -window.onload = function () { - if (userSession.isSignInPending()) { - userSession.handlePendingSignIn().then(userData => { - // Save or otherwise utilize userData post-authentication - }); - } else if (userSession.isUserSignedIn()) { - // Handle case in which user is already authenticated - } -}; -``` - -The `isSignInPending` method of the `userSession` object detects whether the user needs to handle a pending authentication state upon page load. - -The `handlePendingSignIn` method is then used to handle that state, returning a `userData` object with all the data needed to save the user's information into their session. - -The authenticated state can later be detected by the `isUserSignedIn` method in case any particular handling is needed. - -:::note -It's essential to implement `handlePendingSignIn` within the context of mobile apps. -::: - -If the user has indeed confirmed authentication in the context of a popup window, the authenticator will resolve the pending authentication state automatically with the app within the parent window. - -It will then trigger the `onFinish` function provided above, which can be used similarly to save the user's information into their session as retrieved with `userSession.loadUserData()`. - -## Advanced - -### How It Works - -The authentication flow with Stacks is similar to the typical client-server flow used by centralized sign in services (for example, OAuth). However, with Stacks, the authentication flow happens entirely client-side. - -An app and authenticator, such as [the Stacks Wallet](https://www.hiro.so/wallet/install-web), communicate during the authentication flow by passing back and forth two tokens. The requesting app sends the authenticator an `authRequest` token. Once a user approves authentication, the authenticator responds to the app with an `authResponse` token. - -These tokens are based on [a JSON Web Token (JWT) standard](https://tools.ietf.org/html/rfc7519) with additional support for the `secp256k1` curve used by Bitcoin and many other cryptocurrencies. They are passed via URL query strings. - -See the [`authRequest`](#authrequest-payload-schema) and [`authResponse`](#authresponse-payload-schema) payload schemas below for more details about what data they contain. - -When a user chooses to authenticate an app, it sends the `authRequest` token to the authenticator via a URL query string with an equally named parameter: - -`https://wallet.hiro.so/...?authRequest=j902120cn829n1jnvoa...` - -When the authenticator receives the request, it generates an `authResponse` token for the app using an _ephemeral transit key_ . The ephemeral transit key is just used for the particular instance of the app, to sign the `authRequest`. - -The app stores the ephemeral transit key during request generation. The public portion of the transit key is passed in the `authRequest` token. The authenticator uses the public portion of the key to encrypt an _app private key_ , which is returned via the `authResponse`. - -The authenticator generates the app private key from the user's _identity address private key_ , and the app's domain. The app's private key serves three functions: - -1. It is used to create credentials that give the app access to a storage bucket in the user's Gaia hub -2. It is used in the end-to-end encryption of files stored for the app in the user's Gaia storage. -3. It is a cryptographic secret that apps can use to perform other cryptographic functions. - -Finally, the app private key is deterministic, meaning that the same private key will always be generated for a given Stacks address and domain. - -The first two functions are particularly relevant to [data storage with Stacks.js](/build-apps/data-storage). - -[Learn more about keypairs](#key-pairs) used by authentication. - -### Key Pairs - -Authentication with Stacks makes extensive use of public key cryptography and ECDSA with the `secp256k1` curve in particular. - -The following sections describe the three public-private key pairs used, including how they're generated, where they're used and to whom private keys are disclosed. - -#### Transit Private Key - -The transit private is an ephemeral key used to encrypt secrets that -need to be passed from the authenticator to the app during the -authentication process. It is randomly generated by the app at the beginning of -the authentication response. - -The public key corresponding to the transit private key is stored in a single -element array in the `public_keys` key of the authentication request token. The -authenticator encrypts secret data such as the app's private key using this -public key and sends it back to the app when the user signs in to the app. The -transit private key signs the app authentication request. - -#### Identity Address Private Key - -The identity address private key is derived from the user's keychain phrase and -is the private key of the Stacks username the user chooses to use to sign in -to the app. It is a secret the user owns and never leaves the user's -instance of the authenticator. - -This private key signs the authentication response token for an app to indicate that the user approves signing in to that app. - -#### App Private Key - -The app private key is an app-specific private key generated from the -user's identity address private key using the `domain_name` as input. - -The app private key is securely shared with the app on each authentication, encrypted by the authenticator with the transit public key. Because the transit key is only stored on the client side, this prevents a man-in-the-middle attack where a server or internet provider could snoop on the app private key. - -### `authRequest` Payload Schema - -```jsx -const requestPayload = { - jti, // UUID - iat, // JWT creation time in seconds - exp, // JWT expiration time in seconds - iss, // legacy decentralized identifier generated from transit key - public_keys, // single entry array with public key of transit key - domain_name, // app origin - manifest_uri, // url to manifest file - must be hosted on app origin - redirect_uri, // url to which the authenticator redirects user on auth approval - must be hosted on app origin - version, // version tuple - do_not_include_profile, // a boolean flag asking authenticator to send profile url instead of profile object - supports_hub_url, // a boolean flag indicating gaia hub support - scopes, // an array of string values indicating scopes requested by the app -}; -``` - -### `authResponse` Payload Schema - -```jsx -const responsePayload = { - jti, // UUID - iat, // JWT creation time in seconds - exp, // JWT expiration time in seconds - iss, // legacy decentralized identifier (string prefix + identity address) - this uniquely identifies the user - private_key, // encrypted private key payload - public_keys, // single entry array with public key - profile, // profile object - username, // Stacks username (if any) - core_token, // encrypted core token payload - email, // email if email scope is requested & email available - profile_url, // url to signed profile token - hubUrl, // url pointing to user's gaia hub - version, // version tuple -}; -``` - -### Decode `authRequest` or `authResponse` - -To decode a token and see what data it holds: - -1. Copy the `authRequest` or `authResponse` string from the URL during authentication. -2. Navigate to [jwt.io](https://jwt.io/). -3. Paste the full token there. - - The output should look similar to the below: - - ```json - { - "jti": "f65f02db-9f42-4523-bfa9-8034d8edf459", - "iat": 1555641911, - "exp": 1555645511, - "iss": "did:btc-addr:1ANL7TNdT7TTcjVnrvauP7Mq3tjcb8TsUX", - "public_keys": ["02f08d5541bf611ded745cc15db08f4447bfa55a55a2dd555648a1de9759aea5f9"], - "domain_name": "http://localhost:8080", - "manifest_uri": "http://localhost:8080/manifest.json", - "redirect_uri": "http://localhost:8080", - "version": "1.3.1", - "do_not_include_profile": true, - "supports_hub_url": true, - "scopes": ["store_write", "publish_data"], - "private_key": "4447bfa55a55a2dd555648a1d02f08d759aea5f945cc15db08f" - } - ``` - - The `iss` property is a decentralized identifier or `did`. This identifies the user and the username to the app. The specific `did` is a `btc-addr`. diff --git a/docs/feature-guides/sign-messages.md b/docs/feature-guides/sign-messages.md deleted file mode 100644 index 3f6e0b46f..000000000 --- a/docs/feature-guides/sign-messages.md +++ /dev/null @@ -1,205 +0,0 @@ ---- -title: Sign Messages ---- - -import StacksjsStartersNote from '../includes/\_stacks.js-starters-note.mdx'; -import StacksProviderSection from '../includes/\_stacks.js-provider-section.mdx'; - - - ---- - -This guide explains how to prompt users to sign a message. Messaging signing can be used to have a user prove they control a particular address or to have a user authorize a particular action in your app. - -When signing a message, a user will be prompted via a popup from their wallet of choice, showing the message you want them to sign. - -The user can then click on the 'Sign' button, which will return the signature data and the user's publicKey to your app. You can then verify the signature by passing the signature data and the public key to the [`stacks.js`](https://github.com/hirosystems/stacks.js) `verifySignature` method. - -The message can be any utf-8 string. - -Internally the string will be hashed using `sha256` and signed with `secp256k1` using the user's privateKey - -## Install dependency - -@stacks/connect should be installed as a dependency. - -``` -npm install @stacks/connect -``` - -## Initiate session - -Users must be authenticated before they can sign a message from your app. Users can install an authenticator (e.g. a wallet) like [Leather](https://leather.io/) for that purpose. - -See the [authentication guide](https://docs.hiro.so/build-apps/authentication) before integrating the following message signing capabilities. - -## Prompt to sign a message - -Call the `openSignatureRequestPopup` function provided by the `connect`  package to trigger the display of the message signing prompt. - -```tsx -import { openSignatureRequestPopup } from '@stacks/connect'; -import { StacksTestnet } from '@stacks/network'; - -const message = 'Hello World'; - -openSignatureRequestPopup({ - message, - network: new StacksTestnet(), // for mainnet, `new StacksMainnet()` - appDetails: { - name: 'My App', - icon: window.location.origin + '/my-app-logo.svg', - }, - onFinish(data) { - console.log('Signature of the message', data.signature); - console.log('Use public key:', data.publicKey); - }, -}); -``` - -Several parameters are available for calling `openSignatureRequestPopup`. Here's the exact interface for them: - -```tsx -interface SignatureRequestOptions { - message: string; - onFinish?: (data: SignatureData) => void; - onCancel?: (data: SignatureData) => void; - appDetails: { - name: string; - icon: string; - }; - authOrigin?: string; - stxAddress?: string; - userSession?: UserSession; -} -``` - -## Getting the signed message back after completion - -The `openSignatureRequestPopup` signing method from `@stacks/connect` allows you to specify an `onFinish` callback. -This callback will be triggered after the user has successfully signed the message. - -You can get the message's signature via the arguments passed to `onFinish`. Your callback will be fired with a single argument, which is an object with the following properties: - -```ts -export interface SignatureData { - /* Hex encoded DER signature */ - signature: string; - /* Hex encoded private string taken from privateKey */ - publicKey: string; -} -``` - -```ts -const onFinish = (data: SignatureData) => { - console.log('Signature', data.signature); - console.log('PublicKey', data.publicKey); -}; -``` - -## How to verify a signature - -You can easily verify the signature using the [`@stacks/stacks.js`](https://github.com/hirosystems/stacks.js) package as seen in the following example. - -```ts -import { verifyMessageSignatureRsv } from '@stacks/encryption'; - -const message = 'Hello World'; - -openSignatureRequestPopup({ - // ... - onFinish({ publicKey, signature }) { - const verified = verifyMessageSignatureRsv({ message, publicKey, signature }); - if (verified) { - // Trigger a notification explaining signature is verified - } - }, -}); -``` - -## Specifying the network for a transaction {#network-option} - -All of the methods included on this page accept a `network` option. By default, Connect uses a testnet network option. You can import a network configuration from the [`@stacks/network`](https://stacks.js.org/modules/network.html) package. - -```ts -import { StacksTestnet, StacksMainnet } from '@stacks/network'; - -const testnet = new StacksTestnet(); -const mainnet = new StacksMainnet(); - -// use this in your message signing method: - -openSignatureRequestPopup({ - network: mainnet, - // other relevant options -}); -``` - -## Usage in React Apps - -Import the `useConnect` helper from [`connect-react`](https://github.com/hirosystems/connect) package to sign messages more seamlessly with React apps. -You must install a version >= 15.0.0 - -``` -npm install @stacks/connect-react -``` - -Use the function with the same parameters as outlined above. However, you don't have to specify `appDetails` since they are detected automatically if `useConnect` has been used already [for authentication](/build-apps/authentication#usage-in-react-apps). - -```tsx -import { useConnect } from '@stacks/connect-react'; - -const MyComponent = () => { - const { sign } = useConnect(); - - const onClick = async () => { - const options = { - /** See examples above */ - }; - await sign(options); - }; - - return Sign message; -}; -``` - -## Signature request / response payload - -Under the hood, `@stacks/connect` will serialize and deserialize data between your app and the Hiro Wallet. - -These payloads are tokens that conform to the [JSON Web Token (JWT) standard](https://tools.ietf.org/html/rfc7519) with additional support for the `secp256k1` curve used by Bitcoin and many other cryptocurrencies. - -### Signature Request Payload - -When an application triggers a message signing from `@stacks/connect`, the options of that signature request are serialized into a `signatureRequest` payload. The `signatureRequest` is similar to the [authRequest](https://docs.hiro.so/build-apps/authentication#authrequest-payload-schema) payload used for authentication. - -The signature request payload has the following schema, in addition to the standard JWT required fields: - -```ts -interface SignatureRequestPayload { - message: string; - publicKey: string; - /** - * Provide the Hiro Wallet with a suggested account to sign this transaction with. - * This is set by default if a `userSession` option is provided. - */ - stxAddress?: string; - appDetails?: AuthOptions['appDetails']; - network?: StacksNetwork; -} -``` - -### Signature Response payload - -After the user signs the message, a `signatureResponse` payload is sent back to your app. - -```ts -interface SignatureData { - /* Hex encoded DER signature */ - signature: string; - /* Hex encoded private string taken from privateKey */ - publicKey: string; -} -``` - - diff --git a/docs/feature-guides/sign-transactions.md b/docs/feature-guides/sign-transactions.md deleted file mode 100644 index 5a586caea..000000000 --- a/docs/feature-guides/sign-transactions.md +++ /dev/null @@ -1,415 +0,0 @@ ---- -title: Sign Transactions ---- - -import StacksjsStartersNote from '../includes/\_stacks.js-starters-note.mdx'; -import StacksProviderSection from '../includes/\_stacks.js-provider-section.mdx'; - - - ---- - -This guide explains how to prompt users to sign [transactions](https://docs.stacks.co/understand-stacks/transactions) and broadcast them to the Stacks blockchain by implementing the [`connect`](https://github.com/hirosystems/connect) package of Stacks.js. - -Transaction signing allows users to execute Clarity smart contracts that are relevant to your app and then handle the result as appropriate. - -Users can sign transactions that exchange fungible or non-fungible tokens with upfront guarantees that help them retain control over their digital assets. - -There are three types of transactions: - -1. STX transfer -2. Contract deployment -3. Contract execution - -See the public registry tutorial for a concrete example of these capabilities in practice. - -## Install dependency - -:::tip -To utilize the latest transaction signing with the Stacks Wallet, use version 5 of the `@stacks/connect` NPM package. -::: - -The following dependency must be installed: - -``` -npm install @stacks/connect -``` - -## Initiate session - -Users must authenticate to an app before the `connect` package works to prompt users to sign and broadcast transactions to the Stacks blockchain through a wallet such as [the Leather Wallet](https://leather.io/install-extension). - -See the [authentication guide](https://docs.hiro.so/build-apps/authentication) before integrating the following transaction signing capabilities in cases where `userSession.isUserSignedIn()` returns `true`. - -## Get the user's Stacks address - -After your user has authenticated with their Stacks Wallet, you can get their Stacks address from their `profile`. - -```ts -const profile = userSession.loadUserData().profile.stxAddress; - -const mainnetAddress = stxAddresses.mainnet; -// "SP2K5SJNTB6YP3VCTCBE8G35WZBPVN6TDMDJ96QAH" -const testnetAddress = stxAddresses.testnet; -// "ST2K5SJNTB6YP3VCTCBE8G35WZBPVN6TDMFEVESR6" -``` - -## Prompt to transfer STX - -Call the `openSTXTransfer` function provided by the `connect` package to trigger the display of a transaction signing prompt for transferring STX: - -```tsx -import { openSTXTransfer } from '@stacks/connect'; -import { StacksTestnet } from '@stacks/network'; - -openSTXTransfer({ - recipient: 'ST2EB9WEQNR9P0K28D2DC352TM75YG3K0GT7V13CV', - amount: '100', - memo: 'Reimbursement', - network: new StacksTestnet(), // for mainnet, `new StacksMainnet()` - appDetails: { - name: 'My App', - icon: window.location.origin + '/my-app-logo.svg', - }, - onFinish: data => { - console.log('Stacks Transaction:', data.stacksTransaction); - console.log('Transaction ID:', data.txId); - console.log('Raw transaction:', data.txRaw); - }, -}); -``` - -Several parameters are available for calling `openSTXTransfer`. Here's the exact interface for them: - -```tsx -interface STXTransferOptions { - recipient: string; - amount: string; - memo?: string; - network: StacksNetwork; - fee: number | string; - appDetails: { - name: string; - icon: string; - }; - onFinish: (data: FinishedTxData) => void; -} -``` - -| parameter | type | required | description | -| ---------- | ---------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| recipient | string | true | STX address for recipient of transfer | -| amount | string | true | Amount of microstacks (1 STX = 1,000,000 microstacks) to be transferred provided as string to prevent floating point errors. | -| appDetails | object | true | Dictionary that requires `name` and `icon` for app | -| onFinish | function | true | Callback executed by the app when a transaction has been signed and broadcasted. [Read more](#onFinish-option) | -| memo | string | false | Optional memo for inclusion with transaction | -| network | StacksNetwork | false | Specify the network on which this transaction should be completed. [Read more](#network-option) | -| fee | number \| string | false | Optional fee amount in microstacks (1 STX = 1,000,000 microstacks) for overwriting the wallet's default fee value. [Read more](https://forum.stacks.org/t/mempool-congestion-on-stacks-observations-and-next-steps-from-hiro/12325/5) | - -## Prompt to deploy smart contract - -Call the `openContractDeploy` function provided by the `connect` package to trigger the display of a transaction signing prompt for deploying a smart contract: - -```tsx -import { openContractDeploy } from '@stacks/connect'; - -const codeBody = '(begin (print "hello, world"))'; - -openContractDeploy({ - contractName: 'my-contract-name', - codeBody, - appDetails: { - name: 'My App', - icon: window.location.origin + '/my-app-logo.svg', - }, - onFinish: data => { - console.log('Stacks Transaction:', data.stacksTransaction); - console.log('Transaction ID:', data.txId); - console.log('Raw transaction:', data.txRaw); - }, -}); -``` - -Several parameters are available for calling `openContractDeploy`. Here's the exact interface for them: - -```tsx -interface ContractDeployOptions { - codeBody: string; - contractName: string; - network: StacksNetwork; - fee: number | string; - appDetails: { - name: string; - icon: string; - }; - onFinish: (data: FinishedTxData) => void; -} -``` - -| parameter | type | required | description | -| ------------ | ---------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| codeBody | string | true | Clarity source code for contract | -| contractName | string | true | Name for contract | -| appDetails | object | true | Dictionary that requires `name` and `icon` for app | -| onFinish | function | true | Callback executed by the app when a transaction has been signed and broadcasted. [Read more](#onFinish-option) | -| network | StacksNetwork | false | Specify the network that this transaction should be completed on. [Read more](#network-option) | -| fee | number \| string | false | Optional fee amount in microstacks (1 STX = 1,000,000 microstacks) for overwriting the wallet's default fee value. [Read more](https://forum.stacks.org/t/mempool-congestion-on-stacks-observations-and-next-steps-from-hiro/12325/5) | - -:::info -Contracts will deploy to the Stacks address of the authenticated user. -::: - -## Prompt to execute contract - -Call the `openContractCall` function provided by the `connect` package to trigger the display of a transaction signing prompt for executing a contract. - -As an example, consider this simple Clarity contract: - -```clarity -(define-public - (my-func - (arg-uint uint) - (arg-int int) - (arg-buff (buff 20)) - (arg-string-ascii (string-ascii 20)) - (arg-string-utf8 (string-utf8 20)) - (arg-principal principal) - (arg-bool bool) - ) - (ok u0) -) -``` - -To execute this function, invoke the `openContractCall` method. Use the `ClarityValue` types from `@stacks/transactions` to construct properly formatted arguments. - -```tsx -import { openContractCall } from '@stacks/connect'; -import { - uintCV, - intCV, - bufferCV, - stringAsciiCV, - stringUtf8CV, - standardPrincipalCV, - trueCV, -} from '@stacks/transactions'; - -const functionArgs = [ - uintCV(1234), - intCV(-234), - bufferCV(Buffer.from('hello, world')), - stringAsciiCV('hey-ascii'), - stringUtf8CV('hey-utf8'), - standardPrincipalCV('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6'), - trueCV(), -]; - -const options = { - contractAddress: 'ST22T6ZS7HVWEMZHHFK77H4GTNDTWNPQAX8WZAKHJ', - contractName: 'my-contract', - functionName: 'my-func', - functionArgs, - appDetails: { - name: 'My App', - icon: window.location.origin + '/my-app-logo.svg', - }, - onFinish: data => { - console.log('Stacks Transaction:', data.stacksTransaction); - console.log('Transaction ID:', data.txId); - console.log('Raw transaction:', data.txRaw); - }, -}; - -await openContractCall(options); -``` - -Several parameters are available for calling `openContractCall`. Here's the exact interface for them: - -```tsx -interface ContractCallOptions { - contractAddress: string; - functionName: string; - contractName: string; - functionArgs?: ClarityValue[]; - network: StacksNetwork; - fee: number | string; - appDetails: { - name: string; - icon: string; - }; - onFinish: (data: FinishedTxData) => void; -} -``` - -| parameter | type | required | description | -| --------------- | ---------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --- | -| contractAddress | string | true | Stacks address to which contract is deployed | -| contractName | string | true | Name of contract to sign | -| functionName | string | true | Name of function for signing / execution, which needs to be a [public function](https://docs.stacks.co/references/language-functions#define-public). | -| functionArgs | `ClarityValue[]` | true | Arguments for calling the function. [Learn more about constructing clarity values](https://github.com/blockstack/stacks.js/tree/master/packages/transactions#constructing-clarity-values). Defaults to `[]`. | -| appDetails | object | true | Dictionary that requires `name` and `icon` for app | -| onFinish | function | true | Callback executed by the app when a transaction has been signed and broadcasted. [Read more](#onFinish-option) | | -| network | StacksNetwork | false | Specify the network that this transaction should be completed on. [Read more](#network-option) | -| fee | number \| string | false | Optional fee amount in microstacks (1 STX = 1,000,000 microstacks) for overwriting the wallet's default fee value. [Read more](https://forum.stacks.org/t/mempool-congestion-on-stacks-observations-and-next-steps-from-hiro/12325/5) | - -## Getting the signed transaction back after completion {#onFinish-option} - -Each transaction signing method from `@stacks/connect` allows you to specify an `onFinish` callback. This callback will be triggered after the user has successfully broadcasted their transaction. The transaction will be broadcasted, but it will be pending until it has been mined on the Stacks blockchain. - -You can access some information about this transaction via the arguments passed to `onFinish`. Your callback will be fired with a single argument, which is an object with the following properties: - -```ts -interface FinishedTxData { - stacksTransaction: StacksTransaction; - txRaw: string; - txId: string; -} -``` - -The `StacksTransaction` type comes from the [`@stacks/transactions`](https://stacks.js.org/modules/transactions.html) library. - -The `txId` property can be used to provide a link to view the transaction in the explorer. - -```ts -const onFinish = data => { - const explorerTransactionUrl = 'https://explorer.stacks.co/txid/${data.txId}'; - console.log('View transaction in explorer:', explorerTransactionUrl); -}; -``` - -## Specifying the network for a transaction {#network-option} - -All of the methods included on this page accept a `network` option. By default, Connect uses a testnet network option. You can import a network configuration from the [`@stacks/network`](https://stacks.js.org/modules/network.html) package. - -```ts -import { StacksTestnet, StacksMainnet } from '@stacks/network'; - -const testnet = new StacksTestnet(); -const mainnet = new StacksMainnet(); - -// use this in your transaction signing methods: - -openSTXTransfer({ - network: mainnet, - // other relevant options -}); -``` - -## Usage in React Apps - -Import the `useConnect` from the [`connect-react`](https://github.com/hirosystems/connect) package to integrate transaction signing more seamlessly into React apps. - -``` -npm install @stacks/connect-react -``` - -Each transaction signing method is itself available as a function returned by `useConnect` though prefixed with `do` for consistency with React action naming standards: - -- `openContractCall` as `doContractCall` -- `openSTXTransfer` as `doSTXTransfer` -- `openContractDeploy` as `doContractDeploy` - -Use these functions with the same parameters as outlined above. However, you don't have to specify `appDetails` since they are detected automatically if `useConnect` has been used already [for authentication](/build-apps/authentication#usage-in-react-apps). - -```tsx -import { useConnect } from '@stacks/connect-react'; - -const MyComponent = () => { - const { doContractCall } = useConnect(); - - const onClick = async () => { - const options = { - /** See examples above */ - }; - await doContractCall(options); - }; - - return Call my contract; -}; -``` - -## Request testnet STX from faucet - -You may find it useful to request testnet STX from [the Explorer sandbox](https://explorer.stacks.co/sandbox/deploy?chain=testnet) while developing your app with the Stacks testnet. - -## Transaction request / response payload - -Under the hood, `@stacks/connect` will serialize and deserialize data between your app and the Stacks Wallet. - -These payloads are tokens that conform to the [JSON Web Token (JWT) standard](https://tools.ietf.org/html/rfc7519) with additional support for the `secp256k1` curve used by Bitcoin and many other cryptocurrencies. - -### Transaction Request Payload - -When an application triggers a transaction from `@stacks/connect`, the options of that transaction are serialized into a `transactionRequest` payload. The `transactionRequest` is similar to the [authRequest](/build-apps/authentication#authrequest-payload-schema) payload used for authentication. - -The transaction request payload has the following schema, in addition to the standard JWT required fields: - -```ts -interface TransactionRequest { - appDetails?: { - name: string; - icon: string; - }; - // 1 = "allow", 2 = "deny". - postConditionMode?: PostConditionMode; // number - // Serialized version of post conditions - postConditions?: string[]; - // JSON serialized version of `StacksNetwork` - // This allows the app to specify their default desired network. - // The user may switch networks before broadcasting their transaction. - network?: { - coreApiUrl: string; - chainID: ChainID; // number - }; - // `AnchorMode` defined in `@stacks/transactions` - anchorMode?: AnchorMode; // number - // The desired default stacks address to sign with. - // There is no guarantee that the transaction is signed with this address; - stxAddress?: string; - txType: TransactionDetails; // see below -} - -export enum TransactionTypes { - ContractCall = 'contract_call', - ContractDeploy = 'smart_contract', - STXTransfer = 'token_transfer', -} - -interface ContractCallPayload extends TransactionRequest { - contractAddress: string; - contractName: string; - functionName: string; - // Serialized Clarity values to be used as arguments in the contract call - functionArgs: string[]; - txType: TransactionTypes.ContractCall; -} - -interface ContractDeployPayload extends TransactinRequest { - contractName: string; - // raw source code for this contract - codeBody: string; - txType: TransactionTypes.ContractDeploy; -} - -interface StxTransferPayload extends TransactionRequest { - recipient: string; - // amount for this transaction, in microstacks - amount: string; - memo?: string; - txType: TransactionTypes.STXTransfer; -} -``` - -### Transaction Response payload - -After the user signs and broadcasts a transaction, a `transactionResponse` payload is sent back to your app. - -```ts -interface TransactionResponse { - txId: string; - // hex serialized version of this transaction - txRaw: string; -} -``` - - diff --git a/docs/feature-guides/store-data-securely.md b/docs/feature-guides/store-data-securely.md deleted file mode 100644 index bc4295f9d..000000000 --- a/docs/feature-guides/store-data-securely.md +++ /dev/null @@ -1,170 +0,0 @@ ---- -title: Store Data Securely ---- - -import StacksjsStartersNote from '../includes/\_stacks.js-starters-note.mdx'; - - - -This guide explains how to save and retrieve data for users with [Gaia](https://docs.stacks.co/build-apps/references/gaia) by implementing the [`connect`](https://github.com/hirosystems/connect/) and [`storage`](https://stacks.js.org/modules/storage.html) packages of Stacks.js. - -Data storage provides a way for users to save both public and private data off-chain while retaining complete control over it. - -Storing data off the Stacks blockchain ensures that apps can provide users with high performance and high availability for data reads and writes without the involvement of centralized parties that could compromise their privacy or accessibility. - -See the To-dos app tutorial for a concrete example of this feature in practice. - -## Install dependencies - -The following dependencies must be installed: - -``` -npm install @stacks/connect @stacks/storage -``` - -## Initiate session - -Users must authenticate to an app before the `storage` package will work to save or retrieve data on their behalf. - -See the authentication guide before proceeding to integrate the following data storage capabilities in cases where `userSession.isUserSignedIn()` returns `true`. - -## Save data for session user - -Gaia serves as a key-value store in which data is saved and retrieved as files to and from Gaia hubs owned by, or managed for, users. - -The default Gaia hub for users who authenticate to apps with [the Stacks Wallet](https://www.hiro.so/wallet/install-web) is run by Hiro PBC at `https://gaia.blockstack.org/`. It supports files up to 25 megabytes in size. - -:::tip -Hiro recommends breaking data instances greater than 25 MB into several files, saving them individually, and recomposing them on retrieval. -::: - -These files can comprise any type of data such as text, image, video or binary. - -Files are often saved as strings that represent stringified JSON objects and contain a variety of properties for a particular model. - -To save a file, first instantiate a `storage` object using the `userSession` object for an authenticated user. Then proceed to call its `putFile` method with relevant parameters: - -```js -import { AppConfig, UserSession } from '@stacks/connect'; -import { Storage } from '@stacks/storage'; - -const appConfig = new AppConfig(['store_write', 'publish_data']); -const userSession = new UserSession({ appConfig }); -const storage = new Storage({ userSession }); - -let fileName = 'car.json'; - -let fileData = { - color: 'blue', - electric: true, - purchaseDate: '2019-04-03', -}; - -const options = { - encrypt: true, -}; - -let fileUrl = storage.putFile(fileName, JSON.stringify(fileData), options).then(() => { - // Handle any execution after data has been saved -}); -``` - -The `options` parameter object contains an `encrypt` property that when set to `true` indicates that the data should be encrypted with the user's app private key before saved to their Gaia hub. All data will be encrypted as such by default if the `encrypt` property or the `options` object itself is omitted entirely. - -If the `encrypt` property is set to `false`, the data will be saved completely unencrypted and available to everyone online with public access to the user's Gaia hub. - -Whereas saving privately encrypted data is possible for all authenticated apps with the [`store_write`](https://stacks.js.org/enums/auth.AuthScope.html#store_write) scope, the user must have previously granted the [`publish_data`](https://stacks.js.org/enums/auth.AuthScope.html#publish_data) scope as well during authentication for the app to save publicly unencrypted data. - -The `putFile` method returns the URL where the the file can be retrieved from the user's Gaia hub, as used here to set the value of `fileUrl`. - -:::info -You'll need to save an entirely new string of modified data using `putFile` with the same `fileName` every time you want to update a record. There is no separate update method. -::: - -## Get data for session user - -To retrieve data previously saved for a user with an app, call the `getFile` method available from the `storage` object: - -```js -import { AppConfig, UserSession } from '@stacks/connect'; -import { Storage } from '@stacks/storage'; - -const appConfig = new AppConfig(['store_write', 'publish_data']); -const userSession = new UserSession({ appConfig }); -const storage = new Storage({ userSession }); - -let fileName = 'car.json'; - -const options = { - decrypt: true, -}; - -storage.getFile(fileName, options).then(fileData => { - // Handle any execution that uses decrypted fileData -}); -``` - -Note how the `decrypt` property in the `options` object here should implement the same boolean value as used for `encrypt` initially upon saving the data with `putFile`. The `decrypt` property will default to `true` if omitted. - -Encrypted files need `decrypt` set to `true` so the app knows to decrypt the data with the user's app private key before made available in the callback here as `fileData`. - -## Get data for other user - -Apps can also retrieve public data saved by users other than the one with the active session, granted those users have registered usernames via the [Blockchain Naming System](https://docs.stacks.co/build-apps/references/bns). - -Simply indicate the username of such a user in the `options` object: - -```js -import { AppConfig, UserSession } from '@stacks/connect'; -import { Storage } from '@stacks/storage'; - -const appConfig = new AppConfig(['store_write', 'publish_data']); -const userSession = new UserSession({ appConfig }); -const storage = new Storage({ userSession }); - -let fileName = 'car.json'; - -const options = { - username: 'markmhendrickson.id.blockstack', -}; - -storage.getFile(fileName, options).then(fileData => { - // Handle any execution that uses decrypted fileData -}); -``` - -This `getFile` call will retrieve data found at the given `fileName` path from the storage bucket of the Gaia hub that maps to the user who registered the given `username` and this particular app as hosted at the current domain. - -Set an additional `app` property within `options` to retrieve data for a user as saved by an app hosted at a separate domain: - -```js -const options = { - app: 'https://example.org', - username: 'markmhendrickson.id.blockstack', -}; -``` - -This will cause the `getFile` call to retrieve data found in a separate storage bucket for the indicated app on the user's Gaia hub. - -## Delete data for session user - -Call the `deleteFile` method on `storage` to remove data found at a particular file path for the active session user: - -```js -import { AppConfig, UserSession } from '@stacks/connect'; -import { Storage } from '@stacks/storage'; - -const appConfig = new AppConfig(['store_write', 'publish_data']); -const userSession = new UserSession({ appConfig }); -const storage = new Storage({ userSession }); - -let fileName = 'car.json'; - -storage.deleteFile(fileName).then(() => { - // Handle any execution after file has been deleted -}); -``` - -:::info -Apps can save and delete data only for the active session user. -::: diff --git a/docs/feature-guides/update-profile.md b/docs/feature-guides/update-profile.md deleted file mode 100644 index d8455bbbe..000000000 --- a/docs/feature-guides/update-profile.md +++ /dev/null @@ -1,217 +0,0 @@ ---- -title: Update User Profile ---- - -import StacksjsStartersNote from '../includes/\_stacks.js-starters-note.mdx'; -import StacksProviderSection from '../includes/\_stacks.js-provider-section.mdx'; - - - -This guide explains how to change the universal profile of an authenticated user. - -When a user creates a new account with Hiro Wallet a basic profile is created and stored on the user's own storage hub. The basic profile contains -only a public key. It can be extended to contain personal information like an avatar,name and description. It is always cryptographically signed by the user's key, the so-called owner key. - -:::info -Hiro provides a hosting services for storage hubs. Learn about hosting a storage hub at [this tutorial](https://docs.hiro.so/tutorials/gaia-amazon-deploy). -::: - -For users with BNS names, the profile can be read by any user. This extended profile can be used by any application to show a personalized user card -like this: - -![image](https://user-images.githubusercontent.com/1449049/215344771-455d3345-b890-49d0-9cfa-fd1f92bf5b1e.png) - -In order to update the public profile, apps can make request to the Stacks wallet. These requests are reviewed and confirmed by the user in the wallet similar to transaction signing. - -## Install dependency - -:::tip -In order to utilize the latest profile updating with the Hiro Wallet, use a version >= 7.1.0 of the `@stacks/connect` NPM package. -::: - -The following dependency must be installed: - -``` -npm install @stacks/connect -``` - -This also installs the NPM package `@stacks/profile`. It contains the data type `PublicPersonProfile` used for the public profile. - -## Initiate session - -Users must authenticate to an app before you request message signing. Users can install an authenticator like [the Hiro Wallet](https://www.hiro.so/wallet/install-web). - -See the [authentication guide](https://docs.hiro.so/build-apps/authentication) before proceeding to integrate the following message signing capabilities. - -## Prompt to update the profile - -After the user chose the content of the profile, create a `PublicPersonProfile` object from that data and call the `openProfileUpdateRequestPopup` function provided by the `connect` package to trigger the display of the profile update prompt. - -```tsx -import { openProfileUpdateRequestPopup } from '@stacks/connect'; - -const profile = { - '@type': 'Person', - '@context': 'https://schema.org', - name: 'Friedger', - image: [ - { '@type': 'ImageObject', name: 'avatar', contentUrl: 'https://friedger.de/profile.png' }, - ], -}; - -openProfileUpdateRequestPopup({ - profile, - appDetails: { - name: 'My App', - icon: 'https://example-app.com/my-app-logo.svg', - }, - onFinish(data) { - console.log('Profile published', data); - }, -}); -``` - -Several parameters are available for calling `openProfileUpdateRequestPopup`. Here is the exact interface for them: - -```tsx -interface ProfileUpdateRequestOptions { - profile: PublicPersonProfile; - onFinish?: (data: PublicPersonProfile) => void; - onCancel?: () => void; - appDetails: { - name: string; - icon: string; - }; - authOrigin?: string; - stxAddress?: string; - userSession?: UserSession; -} -``` - -After the profile was updated, the user can share the profile with other users. - -## Lookup a Public Profile - -The public profile for a given BNS name can be looked up using -the [`stacks.js`](https://github.com/hirosystems/stacks.js) [`lookupProfile`](https://stacks.js.org/functions/_stacks_auth.lookupProfile) method. - -The functions takes an object of type `ProfileLookupOptions` - -```tsx -export interface ProfileLookupOptions { - username: string; - zoneFileLookupURL?: string; - network?: StacksNetworkName | StacksNetwork; -} -``` - -The function returns a promise with the data of the public profile if the data could be retrieved from the BNS name owner's storage and if the retrieved JSON token was successfully verified. - -The recommended schema for the profile is as follows: - -```tsx -export interface PublicPersonProfile extends PublicProfileBase { - '@type': 'Person'; - name?: string; - givenName?: string; - familyName?: string; - description?: string; - image?: { '@type': 'ImageObject'; name?: string; contentUrl?: string; [k: string]: unknown }[]; - website?: { - '@type'?: string; - url?: string; - [k: string]: unknown; - }[]; - account?: { - '@type'?: string; - service?: string; - identifier?: string; - proofType?: string; - proofUrl?: string; - proofMessage?: string; - proofSignature?: string; - [k: string]: unknown; - }[]; - worksFor?: { - '@type'?: string; - '@id'?: string; - [k: string]: unknown; - }[]; - knows?: { - '@type'?: string; - '@id'?: string; - [k: string]: unknown; - }[]; - address?: { - '@type'?: string; - streetAddress?: string; - addressLocality?: string; - postalCode?: string; - addressCountry?: string; - [k: string]: unknown; - }; - birthDate?: string; - taxID?: string; - [k: string]: unknown; -} -``` - -## Usage in React Apps - -Import the `useConnect` helper from [`connect-react`](https://github.com/hirosystems/connect) package to update profiles more seamlessly with React apps. -You must install a version >= 21.0.0 - -``` -npm install @stacks/connect-react -``` - -Use the function with the same parameters as outlined above. However, you don't have to specify `appDetails` since they are detected automatically if `useConnect` has been used already [for authentication](/build-apps/authentication#usage-in-react-apps). - -```tsx -import { useConnect } from '@stacks/connect-react'; - -const MyComponent = () => { - const { doProfileUpdate } = useConnect(); - - const onClick = async () => { - const options = { - /** See description above */ - }; - await doProfileUpdate(options); - }; - - return Update Profile; -}; -``` - -## Profile Update Request / Response Payload - -Under the hood, `@stacks/connect` will serialize and deserialize data between your app and the Hiro Wallet. - -These payloads are tokens that conform to the [JSON Web Token (JWT) standard](https://tools.ietf.org/html/rfc7519) with additional support for the `secp256k1` curve used by Bitcoin and many other cryptocurrencies. - -### Profile Update Request Payload - -When an application triggers a profile update from `@stacks/connect`, the options of that profile update request are serialized into a `profileUpdateRequest` payload. The `profileUpdateRequest` is similar to the [authRequest](https://docs.hiro.so/build-apps/authentication#authrequest-payload-schema) payload used for authentication. - -The profile update request payload has the following schema, in addition to the standard JWT required fields: - -```ts -interface ProfileUpdatePayload { - profile: PublicPersonProfile; - publicKey: string; - /** - * Provide the Hiro Wallet with a suggested account to sign this transaction with. - * This is set by default if a `userSession` option is provided. - */ - stxAddress?: string; - appDetails?: AuthOptions['appDetails']; - network?: StacksNetwork; -} -``` - -### Profile Update Response payload - -After the user confirms the update, a `profileUpdateResponse` payload of type `PublicProfile` is sent back to your app. It contains the updated profile as confirmed by the user. Note, that this profile can be different to the requested profile by the app because the user might have modified the profile in the wallet before confirming the changes. - - diff --git a/docs/getting-started.md b/docs/getting-started.md deleted file mode 100644 index 2544f8336..000000000 --- a/docs/getting-started.md +++ /dev/null @@ -1,179 +0,0 @@ ---- -title: Getting Started ---- - -import StacksjsStartersNote from './includes/\_stacks.js-starters-note.mdx'; - - - ---- - -To introduce the different functionalities offered by Stacks.js, we'll walk through a few examples and concepts important to building on the Stacks blockchain. - -## Networks - -Typically, we speak of "mainnet" and "testnet" as the networks of Stacks. Most wallets will be configured to "mainnet" by default, this is the actual blockchain that holds real STX tokens. -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 before rolling out applications and contracts to mainnet. -There is even Devnet/Mocknet for working in a local development environment for development. -Stacks.js functions can be configured to use whichever network you want. - -```js -import { StacksMainnet, StacksTestnet } from '@stacks/network'; -const mainnet = new StacksMainnet(); -const testnet = new StacksTestnet(); -``` - -The constructors can also be passed a custom URL to an API, if you want to use a different API than the default. - -```js -import { StacksMainnet } from '@stacks/network'; -const network = new StacksMainnet({ url: 'https://www.mystacksnode.com/' }); -``` - -## Accounts and Addresses - -:::info Connect 🌐 -For web apps, you can request the user's address via Stacks Connect. [Read more](https://connect.stacks.js.org/modules/_stacks_connect#quotconnectquot-aka-authentication-showconnect) -::: - -Stacks.js uses the concept of an "account" to represent a user's identity on the blockchain. An account is identified by a unique address. The address is derived from the account's public key, which is derived from the account's private key. - -A normal mainnet address starts with `SP`, and a testnet address starts with `ST`. -e.g. `SP3FGQ8Z7JY9BWYZ5WM53E0M9NK7WHJF0691NZ159`, `ST2F4BK4GZH6YFBNHYDDGN4T1RKBA7DA1BJZPJEJJ` - -```js -import { generateSecretKey } from '@stacks/wallet-sdk'; - -const mnemonic = generateSecretKey(); -// aunt birth lounge misery utility blind holiday walnut fuel make gift parent gap picnic exact various express sphere family nerve oil drill engage youth - -const wallet = await generateWallet({ - secretKey: mnemonic, - password: 'secretpassword', -}); - -const account = wallet.accounts[0]; -const mainnetAddress = getStxAddress({ account, transactionVersion: TransactionVersion.Mainnet }); -const testnetAddress = getStxAddress({ account, transactionVersion: TransactionVersion.Testnet }); -``` - -## Transactions - -The following shows how to create a simple transaction (STX transfer) using Stacks.js in different environments. - -### Using Connect 🌐 - -```js -import { openSTXTransfer } from '@stacks/connect'; -import { StacksTestnet } from '@stacks/network'; -import { AnchorMode } from '@stacks/transactions'; - -openSTXTransfer({ - network: new StacksTestnet(), - - recipient: 'ST39MJ145BR6S8C315AG2BD61SJ16E208P1FDK3AK', // which address we are sending to - amount: 10000, // tokens, denominated in micro-STX - anchorMode: AnchorMode.Any, - - onFinish: response => console.log(response.txid), - onCancel: () => console.log('User canceled'), -}); -``` - -### Using a private key 🔑 - -Note that if we're not using another wallet, we need to provide the sender's private key for signing. -Treat the private key as a secret and never expose it to the public! - -```js -import { makeSTXTokenTransfer } from '@stacks/transactions'; - -const tx = await makeSTXTokenTransfer({ - recipient: 'ST39MJ145BR6S8C315AG2BD61SJ16E208P1FDK3AK', // which address we are sending to - amount: 10000, // tokens, denominated in micro-STX - anchorMode: 'any', - senderKey: 'c3a2d3...0b1c2', // private key (typically derived from a mnemonic) -}); -``` - -## Anchor Mode / Block Type - -In the examples above, we used `AnchorMode.Any` to indicate that the transaction can be "mined" in different ways. -Stacks has two types of blocks: microblocks and (anchor) blocks. - -- **Microblocks** (off-chain) are faster, but less reliable. Microblocks can be confirmed quickly but are not final until the microblock is included in an anchor block. -- **Anchor Blocks** (on-chain) are the normal Stacks block. They are slower, but more reliable. Anchor blocks are final and cannot be reverted. - - - -```js -// AnchorMode options -anchorMode: "offChainOnly" | "onChainOnly" | "any", -``` - -## Post Conditions - -In Stacks, transactions can have "post conditions". -These are additional security to ensure the transaction was executed as expected. - -Post conditions can't say anything about the end-state after a transaction, but they can verify that certain things happened during the transaction. - -More precisely, post conditions can verify that: - -- STX tokens were transferred from an address -- FTs/NFTs we transferred from an address - -:::caution -Post conditions aren't perfect and can't always guarantee the receival of FTs/NFTs, since they only check senders. -::: - -An example adding a post condition (of an address sending 1000 uSTX). - -```js -import { Pc } from '@stacks/transactions'; - -const tx = await makeSTXTokenTransfer({ - // ... - postConditions: [ - Pc.principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6').willSendEq(1000).ustx(), - ], -}); -``` - -### Post Condition "Mode" - -_...aka "allow transfer of unspecified assets?"_ - -In addition to the post conditions itself, we can also specify a "mode" for the transaction to verify asset transfers. -The mode can be either `Allow` or `Deny`. - -- `Allow` means that the transaction can transfer any asset (assuming no conflicting post conditions). -- `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. -::: - -## 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) -::: - -A finalized transaction can be broadcasted to the network or serialized (to a byte representation) using Stacks.js. - -```js -import { bytesToHex } from '@stacks/common'; -import { makeSTXTokenTransfer, broadcastTransaction, AnchorMode } from '@stacks/transactions'; - -const broadcastResponse = await broadcastTransaction(transaction); -const txId = broadcastResponse.txid; - -const serializedTx = tx.serialize(); // Uint8Array -const serializedTxHex = bytesToHex(serializedTx); // hex string -``` diff --git a/docs/how-to-guides/how-to-integrate-stacking-delegation.md b/docs/how-to-guides/how-to-integrate-stacking-delegation.md deleted file mode 100644 index 5afddcf93..000000000 --- a/docs/how-to-guides/how-to-integrate-stacking-delegation.md +++ /dev/null @@ -1,204 +0,0 @@ ---- -title: Integrate Stacking Delegation ---- - -import StacksjsStartersNote from '../includes/\_stacks.js-starters-note.mdx'; - - - -In this guide, you'll learn how to integrate the Stacking delegation flow by interacting with the respective smart contract, as well as reading data from the Stacks blockchain. - -This guide highlights the following capabilities: - -- As an account holder: delegate STX tokens -- As a delegator: Stack STX token on behalf of the account holder -- As a delegator: Commit to Stacking with all delegated STX tokens - -## Prerequisites - -First, you'll need to understand the [Stacking delegation mechanism](https://docs.stacks.co/understand-stacks/stacking). - -You'll also need [NodeJS](https://nodejs.org/en/download/) `12.10.0` or higher to complete this tutorial. You can verify your installation by opening up your terminal and run the following command: - -```sh -node --version -``` - -Finally, you need to have access to at least two accounts (STX account holder and delegator). For testing purposes on the testnet, you can use the CLI to generate them: - -```sh -stacks make_keychain -t > account.json -stacks make_keychain -t > delegator.json -``` - -You can use the faucet to obtain testnet STX tokens for the test account. Replace `` below with your address: - -```sh -curl -XPOST "https://api.testnet.hiro.so/extended/v1/faucets/stx?address=&stacking=true" -``` - -## Step 1: Integrate libraries - -Install the stacking, network, transactions libraries, and bn.js for large number handling: - -```shell -npm install --save @stacks/stacking @stacks/network @stacks/transactions bn.js -``` - -:::info -See additional [Stacking library reference](https://github.com/blockstack/stacks.js/tree/master/packages/stacking) -::: - -## Step 2: Delegate STX tokens - -To get started, delegate STX tokens as an account holder. - -```js -import { getNonce } from '@stacks/transactions'; -import { StacksTestnet, StacksMainnet } from '@stacks/network'; -import { StackingClient } from '@stacks/stacking'; -import BN from 'bn.js'; - -// for mainnet: const network = new StacksMainnet(); -const network = new StacksTestnet(); - -// the stacker STX address -const address = 'ST3XKKN4RPV69NN1PHFDNX3TYKXT7XPC4N8KC1ARH'; - -const client = new StackingClient(address, network); - -// how much to stack, in microSTX -const amountMicroStx = new BN(100000000000); - -// STX address of the delegator -const delegateTo = 'ST2MCYPWTFMD2MGR5YY695EJG0G1R4J2BTJPRGM7H'; - -// burn height at which the delegation relationship should be revoked (optional) -const untilBurnBlockHeight = 5000; - -// hash of bitcoin address that the delegator has to use to receive the pool's rewards (optional) -const poxAddress = undefined; - -// private key of the account holder for transaction signing -const privateKey = 'd48f215481c16cbe6426f8e557df9b78895661971d71735126545abddcd5377001'; - -const delegetateResponse = await client.delegateStx({ - amountMicroStx, - delegateTo, - untilBurnBlockHeight, // optional - poxAddress, // optional - privateKey, -}); - -// { -// txid: '0xf6e9dbf6a26c1b73a14738606cb2232375d1b440246e6bbc14a45b3a66618481', -// } -``` - -This method calls the [`delegate-stx`](https://docs.stacks.co/references/stacking-contract#delegate-stx) method of the Stacking contract. Note, that the amount can be higher or lower than the current account balance. Delegation does not yet lock the STX tokens, users can still transfer them. - -:::tip -To avoid handling private keys, it is recommended to use the [Stacks Wallet](https://www.hiro.so/wallet) to sign the delegation transaction -::: - -**Congratulations!** With the completion of this step, you successfully learnt how to use the Stacking library to delegate STX tokens as an account holder. - -## Optional: Revoke delegation rights - -Delegators will be able to Stack STX tokens on the account holder's behalf until either the set burn height is reached or the account holder revokes the rights. - -To revoke delegation rights, the account holder can call the `revokeDelegatestx` method. - -```js -const revokeResponse = await client.revokeDelegateStx(privateKey); - -// { -// txid: '0xf6e9dbf6a26c1b73a14738606cb2232375d1b440246e6bbc14a45b3a66618481', -// } -``` - -This method calls the [`revoke-delegate-stx`](https://docs.stacks.co/references/stacking-contract#revoke-delegate-stx) method of the Stacking contract. - -:::tip -To avoid handling private keys, it is recommended to use the [Stacks Wallet](https://www.hiro.so/wallet) to sign the revoke transaction -::: - -## Step 3: Stack delegated STX tokens - -With an established delegation relationship, the delegator can stack STX tokens on behalf of the account holder. This happens usually in a different client app than the delegation. - -```js -// block height at which to stack -const burnBlockHeight = 2000; - -// the delegator initiates a different client -const delegatorAddress = 'ST22X605P0QX2BJC3NXEENXDPFCNJPHE02DTX5V74'; - -// number cycles to stack -const cycles = 3; - -// delegator private key for transaction signing -const delegatorPrivateKey = 'd48f215481c16cbe6426f8e557df9b78895661971d71735126545abddcd5377001'; - -// the BTC address for reward payouts; either to the delegator or to the BTC address set by the account holder -// must start with "1" or "3". Native Segwit (starts with "bc1") is not supported -const delegatorBtcAddress = 'msiYwJCvXEzjgq6hDwD9ueBka6MTfN962Z'; - -// if you call this method multiple times in the same block, you need to increase the nonce manually -let nonce = getNonce(delegatorAddress, network); -nonce = nonce.add(new BN(1)); - -const delegatorClient = new StackingClient(delegatorAddress, network); - -const delegetateStackResponses = await delegatorClient.delegateStackStx({ - stacker: address, - amountMicroStx, - poxAddress: delegatorBtcAddress, - burnBlockHeight, - cycles, - privateKey: delegatorPrivateKey, - nonce, // optional -}); - -// { -// txid: '0xf6e9dbf6a26c1b73a14738606cb2232375d1b440246e6bbc14a45b3a66618481', -// } -``` - -This function calls the [`delegate-stack-stx`](https://docs.stacks.co/references/stacking-contract#delegate-stack-stx) method of the Stacking contract to lock up the STX token from the account holder. - -The delegator must call this method multiple times (for all stackers), until enough tokens are locked up to participate in Stacking. This is the first part of delegated stacking for the delegator. - -:::info -Reward slots are assigned based on the number of STX tokens locked up for a specific Bitcoin reward address -::: - -## Step 4: Commit to Stacking - -As soon as pooling completes (minimum STX token threshold reached), the delegator needs to confirm participation for the next one or more cycles: - -```js -// reward cycle id to commit to -const rewardCycle = 12; - -const delegetateCommitResponse = await delegatorClient.stackAggregationCommit({ - poxAddress: delegatorBtcAddress, // this must be the delegator bitcoin address - rewardCycle, - privateKey: delegatorPrivateKey, -}); - -// { -// txid: '0xf6e9dbf6a26c1b73a14738606cb2232375d1b440246e6bbc14a45b3a66618481', -// } -``` - -This method calls the [`stack-aggregation-commit`](https://docs.stacks.co/references/stacking-contract#stack-aggregation-commit) function of the Stacking contract. This call also includes locked Stacks from previous cycles. This is the second part of delegated stacking for the delegator. - -This method has to be called once for each reward cycle, even if all account holders have already locked their Stacks for several cycles in a row. If no new account holders are added to the pool, then this method call can be made even several cycles before the actual rewards cycle. - -Locking delegated Stacks together with a aggregation commits can be done several times before the cycle starts as long as the minimum increment amount of locked Stacks is met. - -**Congratulations!** With the completion of this step, you successfully learnt how to use the Stacking library to ... - -- Stack STX token on behalf of an account holder -- Commit to Stacking with all delegated STX tokens diff --git a/docs/how-to-guides/how-to-integrate-stacking.md b/docs/how-to-guides/how-to-integrate-stacking.md deleted file mode 100644 index 6453f622d..000000000 --- a/docs/how-to-guides/how-to-integrate-stacking.md +++ /dev/null @@ -1,336 +0,0 @@ ---- -title: Integrating Stacking ---- - -import StacksjsStartersNote from '../includes/\_stacks.js-starters-note.mdx'; - - - -In this tutorial, you'll learn how to integrate Stacking by interacting with the respective smart contract, as well as reading data from the Stacks blockchain. - -This tutorial highlights the following capabilities: - -- Generate Stacks accounts -- Display stacking info -- Verify stacking eligibility -- Add stacking action -- Display stacking status - -:::tip -Alternatively to integration using JS libraries, you can use the [Rust CLI](https://gist.github.com/kantai/c261ca04114231f0f6a7ce34f0d2499b) or [JS CLI](/tutorials/stacking-using-cli). -::: - -## Prerequisites - -First, you'll need to understand the [Stacking mechanism](https://docs.stacks.co/understand-stacks/stacking). - -You'll also need [NodeJS](https://nodejs.org/en/download/) `12.10.0` or higher to complete this tutorial. You can verify your installation by opening up your terminal and run the following command: - -```bash -node --version -``` - -## Overview - -In this tutorial, we'll implement the Stacking flow laid out in the [Stacking guide](https://docs.stacks.co/understand-stacks/stacking#stacking-flow). - -## Step 1: Integrate libraries - -Install the stacking, network, transactions libraries and bn.js for large number handling: - -```shell -npm install --save @stacks/stacking @stacks/network @stacks/transactions bn.js -``` - -:::info -See additional [Stacking library reference](https://github.com/blockstack/stacks.js/tree/master/packages/stacking) -::: - -## Step 2: Generating an account and initialization - -To get started, let's create a new, random Stacks 2.0 account: - -```js -import { - makeRandomPrivKey, - privateKeyToString, - getAddressFromPrivateKey, - TransactionVersion, -} from '@stacks/transactions'; - -import { StackingClient } from '@stacks/stacking'; - -import { StacksTestnet, StacksMainnet } from '@stacks/network'; - -import BN from 'bn.js'; - -// generate random key or use an existing key -const privateKey = privateKeyToString(makeRandomPrivKey()); - -// get Stacks address -// for mainnet, remove the TransactionVersion -const stxAddress = getAddressFromPrivateKey(privateKey, TransactionVersion.Testnet); - -// instantiate the Stacker class for testnet -// for mainnet, use `new StacksMainnet()` -const client = new StackingClient(stxAddress, new StacksTestnet()); -``` - -:::info -Review the [accounts guide](https://docs.stacks.co/understand-stacks/accounts) for more details -::: - -## Step 3: Display stacking info - -In order to inform users about the upcoming reward cycle, we can use the following methods to obtain information for Stacking: - -With the obtained PoX info, you can present whether Stacking has been executed in the next cycle, when the next cycle begins, the duration of a cycle, and the minimum microstacks required to participate: - -```js -// will Stacking be executed in the next cycle? -const stackingEnabledNextCycle = await client.isStackingEnabledNextCycle(); -// true or false - -// how long (in seconds) is a Stacking cycle? -const cycleDuration = await client.getCycleDuration(); -// 120 - -// how much time is left (in seconds) until the next cycle begins? -const secondsUntilNextCycle = await client.getSecondsUntilNextCycle(); -// 600000 -``` - -:::note -Cycle duration and participation thresholds will differ between mainnet and testnet -::: - -You can also retrieve the raw PoX and core information using the methods below if required: - -```js -const poxInfo = await client.getPoxInfo(); - -// poxInfo: -// { -// contract_id: 'ST000000000000000000002AMW42H.pox', -// first_burnchain_block_height: 0, -// min_amount_ustx: 83335083333333, -// prepare_cycle_length: 30, -// rejection_fraction: 3333333333333333, -// reward_cycle_id: 17, -// reward_cycle_length: 120, -// rejection_votes_left_required: 0, -// total_liquid_supply_ustx: 40000840000000000 -// } - -const coreInfo = await client.getCoreInfo(); - -// coreInfo: -// { -// peer_version: 385875968, -// pox_consensus: 'bb88a6e6e65fa7c974d3f6e91a941d05cc3dff8e', -// burn_block_height: 2133, -// stable_pox_consensus: '2284451c3e623237def1f8caed1c11fa46b6f0cc', -// stable_burn_block_height: 2132, -// server_version: 'blockstack-core 0.0.1 => 23.0.0.0 (HEAD:a4deb7a+, release build, linux [x86_64])', -// network_id: 2147483648, -// parent_network_id: 3669344250, -// stacks_tip_height: 1797, -// stacks_tip: '016df36c6a154cb6114c469a28cc0ce8b415a7af0527f13f15e66e27aa480f94', -// stacks_tip_consensus_hash: 'bb88a6e6e65fa7c974d3f6e91a941d05cc3dff8e', -// unanchored_tip: '6b93d2c62fc07cf44302d4928211944d2debf476e5c71fb725fb298a037323cc', -// exit_at_block_height: null -// } - -const targetBlocktime = await client.getTargetBlockTime(); - -// targetBlocktime: -// 120 -``` - -Users need to have sufficient Stacks (STX) tokens to participate in Stacking. This can be verified easily: - -```js -const hasMinStxAmount = await client.hasMinimumStx(); -// true or false -``` - -For testing purposes, you can use the faucet to obtain testnet STX tokens. Replace `` below with your address: - -```shell -curl -XPOST "https://api.testnet.hiro.so/extended/v1/faucets/stx?address=&stacking=true" -``` - -You'll have to wait a few minutes for the transaction to complete. - -Users can select how many cycles they would like to participate in. To help with that decision, the unlocking time can be estimated: - -```js -// this would be provided by the user -let numberOfCycles = 3; - -// the projected datetime for the unlocking of tokens -const unlockingAt = new Date(new Date().getTime() + secondsUntilNextCycle); -unlockingAt.setSeconds(unlockingAt.getSeconds() + cycleDuration * numberOfCycles); -``` - -## Step 4: Verify stacking eligibility - -At this point, your app shows Stacking details. If Stacking will be executed and the user has enough funds, the user should be asked to provide input for the amount of microstacks to lockup and a Bitcoin address to receive the pay out rewards. - -With this input, and the data from previous steps, we can determine the eligibility for the next reward cycle: - -```js -// user supplied parameters -// BTC address must start with "1" or "3". Native Segwit (starts with "bc1") is not supported -let btcAddress = '1Xik14zRm29UsyS6DjhYg4iZeZqsDa8D3'; -let numberOfCycles = 3; - -const stackingEligibility = await client.canStack({ - poxAddress: btcAddress, - cycles: numberOfCycles, -}); - -// stackingEligibility: -// { -// eligible: false, -// reason: 'ERR_STACKING_INVALID_LOCK_PERIOD', -// } -``` - -:::note -The eligibility check assumes the user will be stacking the maximum balance available in the account. The eligibility check is a read-only function call to the PoX smart contract which does not require broadcasting a transaction -::: - -If the user is eligible, the stacking action should be enabled on the UI. If not, the respective error message should be shown to the user. - -## Step 5: Lock STX to stack - -Next, the Stacking action should be executed. - -```js -// set the amount to lock in microstacks -const amountMicroStx = new BN(100000000000); - -// set the burnchain (BTC) block for stacking lock to start -// you can find the current burnchain block height from coreInfo above -// and adding 3 blocks to provide a buffer for transaction to confirm -const burnBlockHeight = 2133 + 3; - -// execute the stacking action by signing and broadcasting a transaction to the network -client - .stack({ - amountMicroStx, - poxAddress: btcAddress, - cycles: numberOfCycles, - privateKey, - burnBlockHeight, - }) - .then(response => { - // If successful, stackingResults will contain the txid for the Stacking transaction - // otherwise an error will be returned - if (response.hasOwnProperty('error')) { - console.log(response.error); - throw new Error('Stacking transaction failed'); - } else { - console.log(`txid: ${response}`); - // txid: f6e9dbf6a26c1b73a14738606cb2232375d1b440246e6bbc14a45b3a66618481 - return response; - } - }); -``` - -The transaction completion will take several minutes. Only one stacking transaction from each account/address is active at any time. Multiple/concurrent stacking actions from the same account will fail. - -## Step 6: Confirm lock-up - -The new transaction will not be completed immediately. It'll stay in the `pending` status for a few minutes. We need to poll the status and wait until the transaction status changes to `success`. We can use the [Stacks Blockchain API client library](/get-started/stacks-blockchain-api#javascript-client-library) to check transaction status. - -```js -const { TransactionsApi } = require('@stacks/blockchain-api-client'); -const tx = new TransactionsApi(apiConfig); - -const waitForTransactionSuccess = txId => - new Promise((resolve, reject) => { - const pollingInterval = 3000; - const intervalID = setInterval(async () => { - const resp = await tx.getTransactionById({ txId }); - if (resp.tx_status === 'success') { - // stop polling - clearInterval(intervalID); - // update UI to display stacking status - return resolve(resp); - } - }, pollingInterval); - }); - -// note: txId should be defined previously -const resp = await waitForTransactionSuccess(txId); -``` - -:::info -More details on the lifecycle of transactions can be found in the [transactions guide](https://docs.stacks.co/understand-stacks/transactions#lifecycle) -::: - -Alternatively to the polling, the Stacks Blockchain API client library offers WebSockets. WebSockets can be used to subscribe to specific updates, like transaction status changes. Here is an example: - -```js -const client = await connectWebSocketClient('ws://api.hiro.so/'); - -// note: txId should be defined previously -const sub = await client.subscribeAddressTransactions(txId, event => { - console.log(event); - // update UI to display stacking status -}); - -await sub.unsubscribe(); -``` - -## Step 6: Display Stacking status - -With the completed transactions, Stacks tokens are locked up for the lockup duration. During that time, your app can display the following details: unlocking time, amount of Stacks locked, and bitcoin address used for rewards. - -```js -const stackingStatus = await client.getStatus(); - -// If stacking is active for the account, you will receive the stacking details -// otherwise an error will be thrown -// stackingStatus: -// { -// stacked: true, -// details: { -// amount_microstx: '80000000000000', -// first_reward_cycle: 18, -// lock_period: 10, -// burnchain_unlock_height: 3020, -// pox_address: { -// version: '00', -// hashbytes: '05cf52a44bf3e6829b4f8c221cc675355bf83b7d' -// } -// } -// } -``` - -:::note -The `pox_address` property is the PoX contract's internal representation of the reward BTC address. -::: - -To display the unlocking time, you need to use the `firstRewardCycle` and the `lockPeriod` fields. - -**Congratulations!** With the completion of this step, you successfully learnt how to ... - -- Generate Stacks accounts -- Display stacking info -- Verify stacking eligibility -- Add stacking action -- Display stacking status - -## Optional: Rewards - -Currently, the Stacking library does not provide methods to get the paid rewards for a set address. However, the [Stacks Blockchain API exposes endpoints](https://docs.hiro.so/api#tag/Burnchain) to get more details. - -As an example, if you want to get the rewards paid to `btcAddress`, you can make the following API call: - -```shell -# for mainnet, replace `testnet` with `mainnet` -curl 'https://api.testnet.hiro.so/extended/v1/burnchain/rewards/' -``` diff --git a/docs/how-to-guides/how-to-migrate-from-blockstack.js.md b/docs/how-to-guides/how-to-migrate-from-blockstack.js.md deleted file mode 100644 index 2623bd117..000000000 --- a/docs/how-to-guides/how-to-migrate-from-blockstack.js.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Migrate from Blockstack ---- - -To migrate your app from blockstack.js to Stacks.js, follow the steps in the [migration guide](https://github.com/hirosystems/stacks.js/blob/master/.github/MIGRATION.md). diff --git a/docs/how-to-guides/how-to-use-stacks-connect-with-angular.md b/docs/how-to-guides/how-to-use-stacks-connect-with-angular.md deleted file mode 100644 index 541f6a91f..000000000 --- a/docs/how-to-guides/how-to-use-stacks-connect-with-angular.md +++ /dev/null @@ -1,219 +0,0 @@ ---- -title: Angular Authenticator ---- - -In this tutorial, you'll learn how to work with Stacks Connect when using [Angular](https://angular.io/) as your framework of choice. It builds on what you've learnt in the [Authentication Overview](/build-apps/authentication). - -:::note -This article presumes some familiarity with [Angular](https://angular.io/), as well as [Reactive Extensions (RxJS)](https://rxjs.dev/). -::: - -### Prerequisites - -We'll be using the [Angular CLI](https://cli.angular.io/) to scaffold the project, so make sure you've got the latest version installed. We're using version `10.2.0`. - -```sh -npm install --global @angular/cli -``` - -## Step 1: Scaffold and run - -Use the `ng new` command to scaffold a new project. We've named ours `ng-stacks-connect`. - -```sh -ng new --minimal --inline-style --inline-template -``` - -You'll be asked to enter some preferences: whether your app with use routing, and whether you want to use a CSS preprocessor like SASS. For sake of this tutorial, we're keeping things simple. No routing. No preprocessing. - -Inside the newly created `ng-stacks-connect` directory, let's boot up the development server which defaults to [localhost:4200](http://localhost:4200). - -```sh -cd ng-stacks-connect -ng serve -``` - -## Step 2: Add Stacks connect - -```sh -npm install --save @stacks/connect blockstack -``` - -:::info -We're also installing the `blockstack` package, as it's a [peer dependency](https://docs.npmjs.com/cli/v7/configuring-npm/package-json#peerdependencies) of Stacks Connect -::: - -## Step 3: Declare missing globals - -Some dependencies of these packages were written for a Nodejs environment. In a browser environment, tools such as Webpack (v4) often abstract the polyfilling of Nodejs specific APIs. Using the Angular CLI, this must be done manually. - -:::info -`Buffer`, for example, is a global class in a Nodejs environment. In the browser is it `undefined` so we must declare it to avoid runtime exceptions -::: - -Add the following snippet to your `src/polyfills.ts` - -```typescript -(window as any).global = window; -(window as any).process = { - version: '', - env: {}, -}; -global.Buffer = require('buffer').Buffer; -``` - -This does 3 things: - -1. Declares `global` to `window` -2. Declares a global `Buffer` class -3. Declares a global `process` object - -## Step 4: Authentication flow - -Now everything's set up, we're ready to create our auth components - -We can use the CLI's generator to scaffold components. - -### Sign-in button - -```sh -ng generate component -``` - -Enter the name: `stacks-sign-in-button`. You'll find the newly generated component in `src/app/stacks-sign-in-button/stacks-sign-in-button.component.ts`. - -Here's our Sign In button component. Let's replace this example with the following code. - -```typescript -import { Component, OnInit, Output, EventEmitter } from '@angular/core'; - -@Component({ - selector: 'app-stacks-sign-in-button', - template: ` `, -}) -export class StacksSignInButtonComponent { - @Output() onSignIn = new EventEmitter(); -} -``` - -### Connecting Stacks connect - -Let's add this button to our `app-root` component (`app.component.ts`) and wire up the `(onSignIn)` event. Make sure to import `Subject` from `rxjs`. - -```typescript -@Component({ - selector: 'app-root', - template: ``, -}) -export class AppComponent { - stacksAuth$ = new Subject(); -} -``` - -Here we're using an Rxjs `Subject` to represent a stream of sign in events. `stacksAuth$` will emit when we should trigger the sign in action. - -### Authentication - -First, describe the auth options we need to pass to Connect. [Learn more about `AuthOptions` here](/build-apps/authentication). Let's modify the default component to look like this: - -```typescript -import { Component } from '@angular/core'; -import { AuthOptions, FinishedData } from '@stacks/connect'; -import { ReplaySubject, Subject } from 'rxjs'; -import { switchMap } from 'rxjs/operators'; - -@Component({ - selector: 'app-root', - template: ` - - -
{{ authResponse$ | async | json }}
-
- `, -}) -export class AppComponent { - stacksAuth$ = new Subject(); - authResponse$ = new ReplaySubject(1); - - authOptions: AuthOptions = { - finished: response => this.authResponse$.next(response), - appDetails: { - name: 'Angular Stacks Connect Demo', - icon: 'http://placekitten.com/g/100/100', - }, - }; - - ngOnInit() { - this.stacksAuth$ - .pipe(switchMap(() => import('@stacks/connect'))) - .subscribe(connectLibrary => connectLibrary.showBlockstackConnect(this.authOptions)); - } -} -``` - -Let's run through what's going on. In the `authOptions` field, we're using the `finished` handler to emit a value to the `authResponse$` which uses a `ReplaySubject` to persist the latest response. - -:::info -A [`ReplaySubject`](https://rxjs.dev/api/index/class/ReplaySubject) is an Observable that starts without an initial value, but replays the latest x emissions when subscribed to -::: - -For initial load performance, we're using `import("@stacks/connect")` to only load the Stacks Connect library when it's needed. The `switchMap` operators "switches" out the `stacksAuth$` event for the library. - -The output of `authResponse$` can be added to the template for debugging purposes. This uses Angular's `async` and `json` pipes. - -### Loading text - -One problem with the current implementation is that there's a network delay while waiting to load the Connect library. Let's keep track of the loading state and display some text in the sign in button component. You'll need to `import { tap, switchMap } from 'rxjs/operators';`. - -```typescript -// src/app/app.component.ts -isLoadingConnect$ = new BehaviorSubject(false); - -ngOnInit() { - this.stacksAuth$ - .pipe( - tap(() => this.isLoadingConnect$.next(true)), - switchMap(() => import("@stacks/connect")), - tap(() => this.isLoadingConnect$.next(false)) - ) - .subscribe(connectLibrary => - connectLibrary.showBlockstackConnect(this.authOptions) - ); -} -``` - -We can keep track of it with a [BehaviorSubject](https://rxjs.dev/api/index/class/BehaviorSubject), which always emits its initial value when subscribed to. - -Let's add a `loading` input to the `StacksSignInButtonComponent` component. - -```typescript highlight=3,6 -@Component({ - selector: 'app-stacks-sign-in-button', - template: ` - - `, -}) -export class StacksSignInButtonComponent { - @Input() loading: boolean; - @Output() onSignIn = new EventEmitter(); -} -``` - -Then, pass the `isLoadingConnect$` Observable into the component, and hide it when the user has already authenticated. - -```html -// Edit src/app/app.component.ts - -``` - -### Next steps - -This tutorial has shown you how to integrate Stacks Connect with an Angular application. You may want to consider abstracting the Stacks Connect logic behind an [Angular service](https://angular.io/guide/architecture-services), or using [Material Design](https://material.angular.io/) to theme your app. diff --git a/docs/includes/_stacks.js-provider-section.mdx b/docs/includes/_stacks.js-provider-section.mdx deleted file mode 100644 index 3a838a59e..000000000 --- a/docs/includes/_stacks.js-provider-section.mdx +++ /dev/null @@ -1,31 +0,0 @@ -## StacksProvider injected variable - -When users have the [Hiro Wallet](https://www.hiro.so/wallet/install-web) extension installed, the extension will inject a global `StacksProvider` variable into the JavaScript context of your web app. This allows your JavaScript code to hook into the extension, and make authentication, transaction and signature requests. `@stacks/connect` automatically detects and uses this global variable for you. - -At the moment, only the Hiro Wallet extension and the Xverse built-in browswer includes a `StacksProvider`, however, ideally more wallets (and mobile wallets) will support this format, so that your app can be compatible with any Stacks wallet that has functionality to embed web applications. - -In your web application, you can check to see if the user has a compatible wallet installed by checking for the presence of `window.StacksProvider`. - -Here is the interface for the `StacksProvider` variable. - -```tsx -interface StacksProvider { - transactionRequest(payload: string): Promise; - authenticationRequest(payload: string): Promise; - signatureRequest(payload: string): Promise; - structuredDataSignatureRequest(payload: string): Promise; - profileUpdateRequest(payload: string): Promise; - getProductInfo: - | undefined - | (() => { - version: string; - name: string; - meta?: { - tag?: string; - commit?: string; - [key: string]: any; - }; - [key: string]: any; - }); -} -``` diff --git a/docs/includes/_stacks.js-starters-note.mdx b/docs/includes/_stacks.js-starters-note.mdx deleted file mode 100644 index 1f147ea5e..000000000 --- a/docs/includes/_stacks.js-starters-note.mdx +++ /dev/null @@ -1,3 +0,0 @@ -:::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. -::: diff --git a/docs/overview.md b/docs/overview.md deleted file mode 100644 index 8d2b8f763..000000000 --- a/docs/overview.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -title: Overview ---- - -# Stacks.js Overview - -Stacks.js is an SDK for building on the Stacks blockchain. -It's a collection of various JavaScript libraries allowing developers to interact with the Stacks blockchain or allow their users to. - - - -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 -- 🔑 **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. -Developers can build web apps, which prompt the user for an action (e.g. sign a transaction), and then the wallet will handle the rest. -The wallet will act in the security, and best interest of the user, and the user will be able to review the transaction before signing. - -Nevertheless, direct private key access is needed for some use cases. -Developers can build simple scripts and tools intended for "offline" use. -Users may use the Stacks.js CLI directly to send a transaction. -Backends may need to automate signing without direct user interaction. -In these cases, developers can use the same libraries used by Stacks wallets for account handling and transaction signing. - ---- - -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 -- **Data storage**: Save and retrieve data for users with [Gaia](https://docs.stacks.co/build-apps/references/gaia) - -All three of these integrations can be used together to create powerful new user experiences that rival or exceed those of traditional apps while protecting your users' digital rights. - -import StacksjsStartersNote from './includes/\_stacks.js-starters-note.mdx'; - - - -## References - - - -Below is a list of all Stacks.js libraries and a few JS libraries and helpers maintained by Hiro: - -### Connecting Wallets - -- [`@stacks/connect`](https://connect.stacks.js.org/) Connect web application to Stacks wallet browser extensions. [Github](https://github.com/hirosystems/connect) - -### Stacks Primitives - -- [`@stacks/transactions`](https://stacks.js.org/modules/_stacks_transactions) Construct, decode transactions and work with Clarity smart contracts on the Stacks blockchain. [Github](https://github.com/hirosystems/stacks.js/tree/master/packages/transactions) -- [`@stacks/wallet-sdk`](https://stacks.js.org/modules/_stacks_wallet_sdk) Library for building wallets, managing accounts, and handling keys for the Stacks blockchain. [Github](https://github.com/hirosystems/stacks.js/tree/master/packages/wallet-sdk) -- [`@stacks/storage`](https://stacks.js.org/modules/_stacks_storage) Store and fetch files with Gaia, the decentralized storage system. [Github](https://github.com/hirosystems/stacks.js/tree/master/packages/storage) -- [`@stacks/encryption`](https://stacks.js.org/modules/_stacks_encryption) Encryption functions used by stacks.js packages. [Github](https://github.com/hirosystems/stacks.js/tree/master/packages/encryption) -- [`@stacks/auth`](https://stacks.js.org/modules/_stacks_auth) Construct and decode authentication requests for Stacks apps. [Github](https://github.com/hirosystems/stacks.js/tree/master/packages/auth) -- [`@stacks/profile`](https://stacks.js.org/modules/_stacks_profile) Functions for manipulating user profiles. [Github](https://github.com/hirosystems/stacks.js/tree/master/packages/profile) -- [`@stacks/network`](https://stacks.js.org/modules/_stacks_network) Network and API library for working with Stacks blockchain nodes. [Github](https://github.com/hirosystems/stacks.js/tree/master/packages/network) -- [`@stacks/common`](https://stacks.js.org/modules/_stacks_common) Common utilities used by stacks.js packages. [Github](https://github.com/hirosystems/stacks.js/tree/master/packages/common) - -### Native Smart Contract Interaction - -- [`@stacks/bns`](https://stacks.js.org/modules/_stacks_bns) Library for interacting with the BNS contract. [Github](https://github.com/hirosystems/stacks.js/tree/master/packages/bns) -- [`@stacks/stacking`](https://stacks.js.org/modules/_stacks_stacking) Library for PoX stacking. [Github](https://github.com/hirosystems/stacks.js/tree/master/packages/stacking) - -### Others - -- [`@stacks/cli`](/references/stacks-cli) Command line interface to interact with auth, storage, and Stacks transactions. [Github](https://github.com/hirosystems/stacks.js/tree/master/packages/cli) -- [`@stacks/blockchain-api-client`](https://hirosystems.github.io/stacks-blockchain-api/client/) Auto-generated REST and websocket API for all endpoints provided by the Stacks Blockchain API. [Github](https://github.com/hirosystems/stacks-blockchain-api/tree/master/client) -- `@stacks/keychain` _DEPRECATED: replaced by `@stacks/wallet-sdk`._ - -## Development - -There is a main [Stacks.js monorepo](https://github.com/hirosystems/stacks.js) containing most of the packages, but a few others often also considered as a part of "Stacks.js". - -Are we missing anything? -Feel free to open issues in the Github repositories. diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md deleted file mode 100644 index b723ee840..000000000 --- a/docs/troubleshooting.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -title: Troubleshooting ---- - -## Common Pitfall: regenerator-runtime - -If using @stacks/connect with vite, rollup, svelte, or vue, a package `regenerator-runtime` needs to be manually added to build the project successfully. - -`npm install --save-dev regenerator-runtime.`