Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added guides on estimating contract costs #750

Merged
merged 2 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
76 changes: 76 additions & 0 deletions content/docs/stacks/clarinet-js-sdk/quickstart.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,82 @@ Check out the [methods](/stacks/clarinet-js-sdk/references/methods) page for the
```console
npm run test
```
</Step>
<Step>
ryanwaits marked this conversation as resolved.
Show resolved Hide resolved
## Run your tests with code coverage and cost analysis

Clarinet can automatically also produce a code coverage report and cost analysis on the test you ran.

```json title="package.json"
"scripts": {
"test": "vitest run",
"test:report": "vitest run -- --coverage --costs", // [!code highlight]
"test:watch": "chokidar \"tests/**/*.ts\" \"contracts/**/*.clar\" -c \"npm run test:report\""
}
```

By running the `npm run test:report` command in your terminal, an `lcov.info` and `costs-reports.json` file will appear in your root folder:

<Files className='pointer-events-none'>
<Folder name="contracts" defaultOpen>
<File name="counter.clar" />
</Folder>
<Folder name="settings" />
<Folder name="tests" defaultOpen>
<File name="counter.test.ts" />
</Folder>
<File name=".gitignore" />
<File name="Clarinet.toml" />
<File name="costs-reports.json" className='bg-[hsl(var(--highlight))]' />
<File name="lcov.info" className='bg-[hsl(var(--highlight))]' />
<File name="package.json" />
<File name="tsconfig.json" />
<File name="vitest.config.js" />
</Files>

The `costs-reports.json` file will output the cost analysis for every function ran in your tests. Since the `count-up` function was used in our test, this is how the cost analysis output would look like for that function:

```json title="costs-reports.json"
{
"test_name": "tests/counter.test.ts__counter contract__increments the count of the user's principal by 1",
"contract_id": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.counter",
"method": "count-up",
"args": [],
"cost_result": {
"total": {
"write_length": 41,
"write_count": 1,
"read_length": 245,
"read_count": 5,
"runtime": 4752
},
"limit": {
"write_length": 15000000,
"write_count": 15000,
"read_length": 100000000,
"read_count": 15000,
"runtime": 5000000000
},
"memory": 40,
"memory_limit": 100000000
}
}
```

To access and read the `lcov.info` file in a human-readable format, we can first install the `lcov` [package](https://github.com/linux-test-project/lcov) that helps generate a graphical front-end for coverage testing tools.

```console title="Terminal"
brew install lcov
```

Then we'll run the `genhtml` command below with the following options. This command and its options will generate the actual html file, add in additional branch coverage, and will automatically store the files in a new folder called `coverage`.

```console title="Terminal"
genhtml lcov.info --branch-coverage -o coverage
```

You can then navigate into the new `coverage` folder and run the command `open index.html` to view your coverage report. In addition, a summary of the coverage report will be outputted in the terminal.

</Step>
</Steps>

Expand Down
85 changes: 85 additions & 0 deletions content/docs/stacks/clarinet/guides/estimate-costs.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
---
title: Estimating contract costs
description: Analyze execution costs pertaining to your contract.
---

import { File, Folder, Files } from "fumadocs-ui/components/files"

Transactions on the Stacks blockchain comes with execution costs that can dictate transaction fees for your users. To analyze the execution costs of your contract's functions, let's continue with the simple `counter` contract from the [Clarinet quickstart](/stacks/clarinet/quickstart) as an example.

```clarity
(define-map Counters principal uint)

(define-read-only (get-count (who principal))
(default-to u0 (map-get? Counters who))
)

(define-public (count-up)
(ok (map-set Counters tx-sender (+ (get-count tx-sender) u1)))
)
```

To start analyzing execution costs, first run the `clarinet console` command inside your project.

```console title="Terminal"
clarinet console
```

## Get Costs

The `::get_costs <expr>` command in the Clarinet console will display the cost analysis of the given expression as well as the return value of the given expression. The `::get_costs` command expects an expression, so make a `contract-call?` with our `count-up` function and see what happens.

```console title="Terminal"
::get_costs (contract-call? .counter count-up)
```

We should see a chart breaking down the different types of execution costs in the console. Here's the execution cost analysis returned from running the above command:

```console
>> ::get_costs (contract-call? .counter count-up)
+----------------------+-------------+----------------+-----------------+
| | Consumed | Limit | Percentage |
+----------------------+-------------+----------------+-----------------+
| Runtime | 4752 | 5000000000 | 0.00 % |
+----------------------+-------------+----------------+-----------------+
| Read count | 5 | 15000 | 0.03 % |
+----------------------+-------------+----------------+-----------------+
| Read length (bytes) | 245 | 100000000 | 0.00 % |
+----------------------+-------------+----------------+-----------------+
| Write count | 1 | 15000 | 0.01 % |
+----------------------+-------------+----------------+-----------------+
| Write length (bytes) | 41 | 15000000 | 0.00 % |
+----------------------+-------------+----------------+-----------------+


(ok true)
```

As you can see from the chart, execution costs are broken into 5 different categories, each with its own limit. The limit is pertaining to what each Stacks block is limited to.

<Callout title="Execution Cost Categories">
The below lists out each of the 5 different categories of Clarity execution costs. ([source](https://book.clarity-lang.org/ch12-00-runtime-cost-analysis.html))

- **Runtime** costs limits overall complexity of the code that can be executed. For example, negating a boolean value is less complex than calculating SHA512 hash, therefore (not false) will consume less runtime costs than (sha512 "hello world"). This category is also affected by contract size.
- **Read count** limits how many times we can read from memory or chain state to a extract piece of information. It is affected by reading constants, variables, intermediate variables created with let, maps, but also by some functions that needs to save intermediate results during execution.
- **Read length (bytes)** limits how much data we can read from memory or the chain. It is also affected by contract size. Calling into a contract using contract-call? Increases the read length by an amount equal to the contract size in bytes, every time. If you call into a contract with a length of 2,000 bytes twice, the read length is increased twice.
- **Write count** limits how many times we can write data into chain. It increments when writing to variables and maps.
- **Write length (bytes)** limits how much data we can write to the chain.

</Callout>

The percentage column shows what percentage of the execution costs your expression consumed out of the block limit given.

## Toggle costs

You can toggle the cost analysis to always be displayed after every expression execution in the console by toggling this feature on with the command `::toggle_costs`.

```console title="Terminal"
::toggle_costs
```

To turn it off, just run the `::toggle_costs` command again.

Check out the Clarity Book section on [runtime cost analysis](https://book.clarity-lang.org/ch12-00-runtime-cost-analysis.html) to read up on cost optimization techniques and recommendations.

To see how you can run automated cost analysis on your unit tests, check out our [quickstart](/stacks/clarinet-js-sdk/quickstart#run-your-tests-with-code-coverage-and-cost-analysis) section for the Clarinet JS SDK.
1 change: 1 addition & 0 deletions content/docs/stacks/clarinet/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"guides/add-a-contract",
"guides/validate-a-contract",
"guides/debug-a-contract",
"guides/estimate-costs",
"guides/run-a-local-devnet",
"guides/deploy-a-contract",
"guides/create-deployment-plans"
Expand Down
Loading