Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/workflows/deploy-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ on:
push:
branches:
- main
- dev
workflow_dispatch:

jobs:
Expand All @@ -19,11 +18,13 @@ jobs:
with:
node-version-file: 'package.json'
- name: Install dependencies
working-directory: ./docs
run: yarn install --immutable
- name: Run codegen
run: yarn codegen
- name: Run build
working-directory: ./docs
run: yarn build
- name: Run docs build
run: yarn build:docs
- name: Upload build artifacts
uses: actions/upload-pages-artifact@v3
with:
Expand Down
9 changes: 5 additions & 4 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
- name: Run build
run: yarn build
- name: Setup testing environment
run: yarn test:setup
run: yarn test:setup:up
- name: Run tests
run: yarn test:sdk
test-snippets:
Expand All @@ -36,8 +36,9 @@ jobs:
node-version-file: 'package.json'
- name: Install dependencies
run: yarn install --immutable
- name: Install /docs dependencies
working-directory: ./docs
run: yarn install --immutable
- name: Run codegen
run: yarn codegen
- name: Build code
run: yarn build
- name: Test snippets
run: yarn test:snippets
73 changes: 73 additions & 0 deletions docs/docs/core/_tx-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
```mermaid
---
config:
look: classic
layout: dagre
---

flowchart TB
S_Un["Status: Unsigned"] ==> A_Si{"Signing"}
A_Si ==> S_Si["Status: Signed"]
S_Si ==> E_Si(["Event: signed"])
E_Si ==> A_Se{"Sending"}
A_Se ==> S_Se["Status: Sent"]
S_Se ==> E_Se(["Event: sent"])
E_Se ==> A_Bl{"Waiting for block inclusion"}
A_Bl ==> S_Bl["Status: InBlock"]
S_Bl ==> E_Bl(["Event: in_Block"])
E_Bl ==> A_Fi{"Waiting for finalization"}
A_Fi ==> S_Fi["Status: Finalized"]
S_Fi ==> E_Fi(["Event: finalized"])
E_Fi ==> A_Pr{"Waiting for processing"}
A_Pr ==> E_Pr(["Event: processed_by..."])
A_Si --> E_Si_er(["Event: error"])
A_Se --> S_Re["Status: Rejected"]
S_Re --> E_Se_er(["Event: error"])
A_Bl --> S_Us["Status: Usurped"] & S_Dr["Status: Dropped"] & S_Iv["Status: Invalid"]
S_Us --> E_Bl_er(["Event: error"])
S_Dr --> E_Bl_er
S_Iv --> E_Bl_er
A_Fi --> E_Re(["Event: retracted"]) & S_Ft["Status: FinalityTimeout"]
E_Re --> A_Bl
S_Ft --> E_Fi_er(["Event: error"])
S_Bl --> E_Bl_de(["Event: error (DispatchError)"])
E_Bl_de --> E_Bl
S_Fi --> E_Fi_de(["Event: error (DispatchError)"])
E_Fi_de --> E_Fi
E_Si_er ~~~ S_Re
E_Bl_er ~~~ S_Ft
A_Si:::Sky
S_Un:::Pine
S_Si:::Pine
E_Si:::Aqua
A_Se:::Pine
A_Se:::Sky
S_Se:::Pine
E_Se:::Aqua
A_Bl:::Sky
S_Bl:::Pine
E_Bl:::Aqua
A_Fi:::Sky
S_Fi:::Pine
E_Fi:::Aqua
A_Pr:::Sky
E_Pr:::Aqua
E_Si_er:::Peach
S_Re:::Rose
E_Se_er:::Peach
S_Us:::Rose
S_Dr:::Rose
S_Iv:::Rose
E_Bl_er:::Peach
E_Re:::Ash
S_Ft:::Rose
E_Fi_er:::Peach
E_Bl_de:::Peach
E_Fi_de:::Peach
classDef Pine stroke-width:1px, stroke-dasharray:none, stroke:#254336, fill:#27654A, color:#FFFFFF
classDef Peach stroke-width:1px, stroke-dasharray:none, stroke:#FBB35A, fill:#FFEFDB, color:#8F632D
classDef Sky stroke-width:1px, stroke-dasharray:none, stroke:#374D7C, fill:#E2EBFF, color:#374D7C
classDef Aqua stroke-width:1px, stroke-dasharray:none, stroke:#46EDC8, fill:#DEFFF8, color:#378E7A
classDef Rose stroke-width:1px, stroke-dasharray:none, stroke:#FF5978, fill:#FFDFE5, color:#8E2236
classDef Ash stroke-width:1px, stroke-dasharray:none, stroke:#999999, fill:#EEEEEE, color:#000000
```
203 changes: 203 additions & 0 deletions docs/docs/core/assets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
---
sidebar_position: 4
---

import consts from '@site/src/consts'
import { GhLink } from '@site/src/components/GhLink';
import { GlossaryLink } from '@site/src/components/Glossary';
import CodeBlock from '@theme/CodeBlock';
import hapiToJoy from '!!raw-loader!../../src/snippets/assets/hapiToJoy.ts';
import joyToHapi from '!!raw-loader!../../src/snippets/assets/joyToHapi.ts';
import treasuryAccounts from '!!raw-loader!../../src/snippets/assets/treasuryAccounts.ts';
import videoCosts from '!!raw-loader!../../src/snippets/assets/createVideoCosts.ts';
import extrinsicBalancesEffect from '!!raw-loader!../../src/snippets/assets/extrinsicBalancesEffect.ts';
import batchSupport from '!!raw-loader!../../src/snippets/assets/batchSupport.ts';
import mergingCosts from '!!raw-loader!../../src/snippets/assets/mergingCosts.ts';
import balances from '!!raw-loader!../../src/snippets/assets/balances.ts';

# Assets module

The assets module provides a set of utilities related to balances, fees, vesting, locks and stakes on Joystream.

Some of the available features include:

- Retrieving account balances available for different purposes (ie. making transfers, paying different kinds of fees, staking etc.)
- Conversion between `JOY` and `HAPI`, supporting multiple variable types (`number`, `BigInt`, `string`, `BN`)
- Establishing all costs associated with executing a specific runtime extrinsic (tx fees, platform fees, bloat bonds, deposits, transfers etc.), how they would
affect existing balances and whether an account has sufficient funds to cover them.

## Conversion

### HAPI to JOY

<CodeBlock language="typescript" live>{hapiToJoy}</CodeBlock>

### JOY to HAPI

<CodeBlock language="typescript" live>{joyToHapi}</CodeBlock>

## Constants

The following constants can be imported from `@joystream/sdk-core/assets`:

```typescript
// Number of decimal places that JOY token supports
export const JOY_DECIMALS = 10

// How much HAPI (smallest JOY token units) makes up 1 JOY
export const HAPI_PER_JOY = 10 ** JOY_DECIMALS

// Joystream existential deposit (in HAPI)
export const EXISTENTIAL_DEPOSIT = BigInt(266_666_560)
```

## Treasury accounts

The assets module exports treasury accounts of different runtime modules, which are typically used to store bloat bonds and other deposits:

<CodeBlock language="typescript" live>{treasuryAccounts}</CodeBlock>

## AssetsManager

The main way of interacting with the assets module is through the `AssetsManager` class.
It allows you to retrieve account balances, estimate extrinsic costs and more...

### Initializing

#### Standalone

```typescript
import { createApi } from '@joystream/sdk-core/chain'
import { AssetsManager } from '@joystream/sdk-core/assets'

const api = await createApi(`wss://mainnet.joystream.dev/rpc`)
const assets = new AssetsManager(api)
```

#### via JoystreamToolbox

```typescript
import { createJoystreamToolbox } from '@joystream/sdk-core/toolbox'

const joystreamToolbox = await createJoystreamToolbox({
nodeWsEndpoint: 'wss://mainnet.joystream.dev/rpc',
// ...
})
const { assets } = joystreamToolbox
```

### Checking account balances

The `AssetsManager` provides a simple way to retrieve account balances in a format that's typically more useful (than, for example, `api.derive.balances.all`) in context of Joystream:

<CodeBlock language="typescript" live>{balances}</CodeBlock>

#### Balances type

The balances are represented by the following abstraction:

```typescript
export type Balances = {
// All funds, including locked and reserved
total: bigint
// All funds EXCEPT reserved
free: bigint
// All funds that can be used to pay transaction fees and other fee-like costs
feeUsable: bigint
// All funds that are free to be transferred to another account
transferrable: bigint
}
```

This representation is directly tied to the [abstraction of a `Cost`](#costs-interface) which is described later in this document.

:::info
The `Balances` representation is still a work in progress and will be expanded with information about vesting and active stakes/locks in the future.
:::

### Estimating extrinsic costs

Imagine a user of your application wants to add a new video to Joystream.

Executing `content.createVideo` extrinsic involves paying multiple different costs, such as:

- **Transaction fee** - based on the extrinsic arguments and size,
- **Data fee** - based on the size of associated video assets (thumbnail, video media file, subtitles etc.),
- **Data object bloat bond** - based on the number of assets associated with the video and the current bloat bond value in the runtime storage module,
- **Video bloat bond** - based on the value in the runtime content module.

Typically you would need to calculate those costs in advance in order to:

- Inform the user about them,
- Validate whether the user has sufficient balance to cover them.

`AssetsManager` provides a unified interface for dealing with a variety of different costs and allows you display detailed summaries and validate balances without having to write your own logic for each Joystream extrinsic separately.

Take a look at the following example:

<CodeBlock language="typescript" live>{videoCosts}</CodeBlock>

#### Costs interface

If you run the code above, you will notice that each of the listed costs conforms to the following interface:

```typescript
export interface Cost {
// What kind of cost is this (for example: MembershipFee)
kind: CostKind
// Whether paying this cost requires the account to stay alive
// (and therefore its totalBalance to stay above EXISTENTIAL_DEPOSIT)
requiresKeepAlive: boolean
// What happens with the funds? (ie. are they burned? deposisted? transferred?)
destiny: FundsDestiny
// Which balance type is used to pay the the cost (eg. free, feeUsable, transferrable)
paidFrom: BalanceType
// Value in HAPI
value: bigint
}
```

For more details and exact definitions of `CostKind`, `BalanceType` and `FundsDestiny` types, see <GhLink to="packages/core/src/assets/types.ts" />.

Having such detiled and flexible abstraction of a `Cost` enables multiple other features that `AssetsManager` provides (see more examples below).

#### Checking how costs affect balances

`AssetsManager` allows you to check how the estimated extrinsic costs will affect different types of balances of a given account:

<CodeBlock language="typescript" live>{extrinsicBalancesEffect}</CodeBlock>

#### Ensuring sufficient balances

To check whether an account has sufficient balances to cover all provided [costs](#costs-interface), you can simply use the `canPay` method:

```typescript
const hasSufficientFunds = await assets.canPay(alice, costs)

if (!hasSufficientFunds) {
console.log("Can't cover the extrinsic costs!")
} else {
console.log('OK!')
}
```

#### Estimating costs of multiple extrinsics

If your application/script sends multiple extrinsics at once, either because it runs some operations in batches or deals with more complex, multi-step workflows, you may want to estimate the costs of all those extrinsics together beforehand.

:::warning
**Not all costs can be accurately predicted in advance!** (especially if one of the extrinsics you send affects the cost(s) of another)

Imagine a scenario where you batch multiple `projectToken.buyOnAmm` calls for the same token.
Each of those calls will increase the price of the token and affect the costs of subsequent calls. `AssetsManager` will not take those intermediate runtime state changes into account, leading to an underestimation of the total cost.
:::

`AssetsManager` supports all available batch extrinsics (`utility.batch`, `utility.forceBatch`, `utility.batchAll`), so if you're using them to group your extrinsics together, you can simply pass the batch extrinsic to the `costsOf` method:

<CodeBlock language="typescript" live>{batchSupport}</CodeBlock>

Alternatively, if you're sending extrinsics one-by-one, you can estimate the costs separately and then merge them into a single array:

<CodeBlock language="typescript" live>{mergingCosts}</CodeBlock>

This costs representation will work perfectly fine with methods like `canPay`, `estimateBalancesAfter` etc.
File renamed without changes
File renamed without changes
Loading
Loading