Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
17 changes: 0 additions & 17 deletions docs/build/clarinet/local-blockchain-development.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ Useful flags:
| `--deployment-plan-path <path>` | Apply a specific deployment plan |
| `--use-on-disk-deployment-plan` | Use an existing plan without recomputing |
| `--use-computed-deployment-plan` | Recompute and overwrite the plan |
| `--package <path>` | Load a packaged Devnet configuration |
| `--from-genesis` | Skip the embedded Epoch 4.0 snapshot and boot from genesis, walking through every epoch |
| `--create-new-snapshot` | Boot from genesis and save a new global snapshot after the first Epoch 4.0 Stacks block |

Expand Down Expand Up @@ -318,22 +317,6 @@ clarinet devnet start

</details>

### Package deployment

Create reusable Devnet configurations:

```bash
$ clarinet devnet package --name demo-env
Packaging devnet configuration...
Created demo-env.json
```

Use a packaged configuration:

```bash
$ clarinet devnet start --package demo-env.json
```

## Common issues

<details>
Expand Down
10 changes: 5 additions & 5 deletions docs/build/clarinet/project-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,13 @@ The **package.json** defines your testing environment and dependencies:
},
"author": "",
"license": "ISC",
"dependencies": {
"@stacks/clarinet-sdk": "^3.9.1",
"@stacks/transactions": "^7.2.0",
"devDependencies": {
"@stacks/clarinet-sdk": "^4.0.0",
"@stacks/transactions": "^7.6.0",
"@types/node": "^24.4.0",
"chokidar-cli": "^3.0.0",
"vitest": "^4.0.7",
"vitest-environment-clarinet": "^3.0.0"
"vitest": "^5.0.5",
"vitest-environment-clarinet": "^4.0.0"
}
}
```
Expand Down
2 changes: 1 addition & 1 deletion docs/build/clarinet/testing-with-clarinet-sdk.md
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ const totalDeposits = simnet.getDataVar("defi", "total-deposits");
expect(totalDeposits).toBeUint(1000);

const balance = simnet.getMapEntry("defi", "balances", Cl.principal(wallet));
expect(balance).toBeUint(1000);
expect(balance).toBeSome(Cl.uint(1000)); // none if the entry does not exist
```

## Other Examples
Expand Down
45 changes: 38 additions & 7 deletions docs/build/clarinet/validation-and-analysis.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ Focus validation during development on a single contract file:
```bash
clarinet check contracts/nft.clar
```

Clarinet applies the `[linter]` settings from the `Clarinet.toml` given with `--manifest-path`, or from the nearest `Clarinet.toml` above the current directory. If there is no manifest, it uses the default lints. The project's `include` and `exclude` filters do not apply to a file you name explicitly.
{% endstep %}

{% step %}
Expand Down Expand Up @@ -153,10 +155,19 @@ Add the `--output=<format>` option to `clarinet check` to print diagnostics as J

Clarinet includes a built-in linter as part of `clarinet check` that flags common mistakes, inefficiencies, and unused code in Clarity contracts.

Clarinet v3.24.1 has 23 lints in four groups: `unused` (dead code), `perf` (inefficient code), `safety` (code that might not do what you intended) and `style` (naming conventions). Each lint can be configured on its own or through its group.
Clarinet 4.0 has 24 lints in five groups:

* `correctness`: code that is certainly wrong
* `unused`: dead code
* `perf`: inefficient code
* `safety`: code that might not do what you intended
* `style`: naming conventions

You can configure each lint on its own or through its group. The `correctness` lints are the exception: you cannot configure them.

| Identifier | Description | Lint group |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
| `call_checker` | Detects calls that pass the wrong number of arguments to a function. | `correctness` |
| `unused_binding` | Detects unused function parameters and `let` bindings. | `unused` |
| `unused_const` | Detects unused `define-constant` declarations. | `unused` |
| `unused_data_var` | Detects `define-data-var` declarations that are never used, never read, or never written (a value that is never written can be a constant). | `unused` |
Expand Down Expand Up @@ -195,13 +206,25 @@ Individual lints can also be disabled for a specific line using Clarity's annota

#### Configuration

By default, lints in the `unused`, `perf` and `safety` groups report at the `warning` level and `style` lints at the `notice` level. Change the level per lint or per group in `Clarinet.toml`.
By default, lints in the `unused`, `perf` and `safety` groups report at the `warning` level and `style` lints at the `notice` level. Change the level per lint or per group in the `[linter]` section of `Clarinet.toml`.

Lints in the `correctness` group always report at the `error` level and reject the contract. `[linter]` settings do not change them: `call_checker = "warning"`, `correctness = false` and `all = false` have no effect on these lints.

Each value is a level (`"error"`, `"warning"` or `"notice"`), `false` or `"off"` to turn the lint off, or `true` or `"on"` to turn it on at its default level. A lint that is already on keeps its current level, so `style = true` gives notices, not warnings. To get warnings, set `"warning"` explicitly.

**Files to analyze**

```toml
[linter]
include = ["contracts/*.clar"]
exclude = []
```

**Individual lint configuration**

```toml
[repl.analysis.lints]
noop = true # Defaults to "warning"
[linter.lints]
noop = true # Turns the lint on at its default level ("warning")
unused_const = "warning"
unused_data_var = "error"
unused_map = false
Expand All @@ -210,12 +233,20 @@ unused_map = false
**Lint group configuration**

```toml
[repl.analysis.lint_groups]
all = true # Sets the default level to "warning" for all lints
style = "notice" # Cosmetic lints only generate notices
[linter.groups]
all = true # Turns on all lints at their default levels
style = "warning" # Report style lints as warnings instead of notices
unused = "error" # Enforces removal of unused code
```

{% hint style="info" %}
**Migrating from Clarinet 3.x**

Clarinet 4.0 moved the linter settings out of `[repl.analysis]`. Rename `[repl.analysis.lints]` to `[linter.lints]` and `[repl.analysis.lint_groups]` to `[linter.groups]`. Clarinet 4.0 also removed the check-checker pass, so remove `passes = ["check_checker"]` and the `[repl.analysis.check_checker]` settings. Clarinet warns when `Clarinet.toml` still contains `[repl.analysis]` or an `analysis` key in `[project]`.

The check-checker annotations `#[filter(...)]`, `#[allow(unchecked_data)]` and `#[allow(unchecked_params)]` are still accepted, so existing contracts compile, but they have no effect.
{% endhint %}

## Runtime analysis

The Clarinet console has commands for inspecting behavior during execution.
Expand Down
1 change: 0 additions & 1 deletion docs/build/get-started/create-a-token/fungible-tokens.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,6 @@ Any token contract that wants to conform to the SIP-010 fungible token standard
(memo (optional (buff 34)))
)
(begin
;; #[filter(amount, recipient)]
(asserts! (or (is-eq tx-sender sender) (is-eq contract-caller sender)) ERR_NOT_TOKEN_OWNER)
(try! (ft-transfer? clarity-coin amount sender recipient))
(match memo to-print (print to-print) 0x)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ Any NFT contract that wants to conform to the SIP-009 non-fungible token standar
;; SIP-009 function: Transfer NFT token to another owner.
(define-public (transfer (token-id uint) (sender principal) (recipient principal))
(begin
;; #[filter(sender)]
(asserts! (is-eq tx-sender sender) ERR_NOT_TOKEN_OWNER)
(nft-transfer? Your-NFT-Name token-id sender recipient)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ Any SFT contract that wants to conform to the SIP-013 semi-fungible token standa
(ok none)
)

;; #[allow(unchecked_params)]
(define-public (transfer (token-id uint) (amount uint) (sender principal) (recipient principal))
(let
(
Expand Down
12 changes: 0 additions & 12 deletions docs/build/more-guides/price-oracles/pyth.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,6 @@ Below are how the contracts and mainfest files are setup in this example Clarine
(recipient principal)
)
(begin
;; #[filter(sender)]
(asserts! (is-eq tx-sender sender) ERR_NOT_TOKEN_OWNER)
(nft-transfer? benjamin token-id sender recipient)
)
Expand Down Expand Up @@ -198,17 +197,6 @@ path = 'contracts/nft.clar'
clarity_version = 4
epoch = 'latest'

[repl.analysis]
passes = ['check_checker']

[repl.analysis.lints]

[repl.analysis.check_checker]
strict = false
trusted_sender = false
trusted_caller = false
callee_filter = false

[repl.remote_data]
enabled = true
api_url = 'https://api.hiro.so'
Expand Down
2 changes: 0 additions & 2 deletions docs/cookbook/clarity/example-contracts/fungible-token.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@
(memo (optional (buff 34)))
)
(begin
;; #[filter(amount, recipient)]
(asserts! (or (is-eq tx-sender sender) (is-eq contract-caller sender))
ERR_NOT_TOKEN_OWNER
)
Expand Down Expand Up @@ -110,4 +109,3 @@ A complete implementation of a SIP-010 compliant fungible token called "Clarity
* Preventing unauthorized transfers with sender validation
* Returning standardized responses that comply with trait requirements
* Using constants for immutable contract configuration
* Filter annotations for security analysis (`#[filter]`)
2 changes: 0 additions & 2 deletions docs/cookbook/clarity/example-contracts/non-fungible-token.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
(recipient principal)
)
(begin
;; #[filter(sender)]
(asserts! (is-eq tx-sender sender) ERR_NOT_TOKEN_OWNER)
(nft-transfer? Your-NFT-Name token-id sender recipient)
)
Expand Down Expand Up @@ -96,4 +95,3 @@ A complete implementation of a SIP-009 compliant non-fungible token (NFT) contra
* Metadata URI management for off-chain content
* Using data variables to track minting state
* Returning standardized responses for trait compliance
* Filter annotations for security (`#[filter(sender)]`)
4 changes: 2 additions & 2 deletions docs/reference/clarinet-js-sdk/browser-sdk-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ const simnet = await initSimnet();
await simnet.initEmptySession();

// Execute Clarity code directly
const result = simnet.runSnippet("(+ 1 2)");
console.log(result); // 3
const { result } = simnet.execute("(+ 1 2)");
console.log(result); // Cl.int(3)
```
{% endcode %}

Expand Down
97 changes: 86 additions & 11 deletions docs/reference/clarinet-js-sdk/sdk-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ const count = simnet.getDataVar('counter', 'count');

### getMapEntry

`getMapEntry` retrieves a value from a contract map by its key.
`getMapEntry` retrieves a value from a contract map by its key. Like the node's map-entry endpoint, it returns `(some value)` for an existing entry and `none` otherwise, including when the map or contract does not exist.

Usage:

Expand All @@ -150,7 +150,7 @@ const hasParticipated = simnet.getMapEntry(
"Participants",
Cl.standardPrincipal(wallet)
);
// Returns: { type: 10, value: { type: 3 } }
// Returns: Cl.some(Cl.bool(true)), or Cl.none() if there is no entry
```

| Parameter | Type | Description |
Expand Down Expand Up @@ -222,6 +222,8 @@ const result = simnet.callPublicFn(
// Mines block and returns result
```

A returned `(err ...)` response does not throw. `callPublicFn`, `callPrivateFn`, `deployContract` and `transferSTX` throw when simnet rejects the transaction. They also throw when simnet includes the transaction but it fails with a runtime error or a post-condition abort. An included failure still consumes the sender's nonce and mines its block. To tell included failures from rejections, use [`mineBlock`](#mineblock).

### callPrivateFn

`callPrivateFn` calls private functions (testing only) and mines a block.
Expand All @@ -248,7 +250,9 @@ const result = simnet.callPrivateFn(

## Transfer STX

`transferSTX` transfers STX between addresses and mines a block.
`transferSTX` transfers STX between addresses and mines a block. Like `callPublicFn`, it advances one block before executing, even when simnet rejects the transfer.

Self-transfers, zero-amount transfers, and transfers that exceed the sender's unlocked balance throw without consuming a nonce. Inside `mineBlock`, these transfers return a `rejected` outcome instead.

Usage:

Expand Down Expand Up @@ -277,7 +281,9 @@ const transfer = simnet.transferSTX(

## Deploy contracts

`deployContract` deploys a new contract to simnet and mines a block.
`deployContract` deploys a new contract to simnet and mines a block. A successful deployment returns `(ok true)`, whatever the contract's final top-level expression evaluates to. To check what the contract did when it initialized, use read-only calls, `getDataVar` and events. To evaluate a snippet and get its value, use [`execute`](#execute).

An unsupported `clarityVersion` throws instead of falling back to the default version. Simnet rejects a duplicate contract name without consuming a nonce.

Usage:

Expand Down Expand Up @@ -312,12 +318,12 @@ const contract = simnet.deployContract(

### mineBlock

`mineBlock` mines a block with multiple transactions.
`mineBlock` mines a block with multiple transactions. It advances exactly one block, and all entries execute at that height. It first mines any signed transactions queued with [`broadcastTransaction`](#broadcasttransaction), then the entries in `txs`, in input order.

Usage:

```
mineBlock(txs: Tx[]): ParsedTransactionResult[]
mineBlock(txs?: Tx[]): BlockTransactionResult[]
```

```ts
Expand All @@ -328,8 +334,32 @@ const block = simnet.mineBlock([
tx.callPublicFn("counter", "increment", [], simnet.deployer),
tx.transferSTX(19000000, wallet, simnet.deployer),
]);

for (const outcome of block) {
if (outcome.status === "success") {
console.log(outcome.execution.result);
} else {
console.log(outcome.status, outcome.error);
}
}
```

Each result has one of three statuses. A failed entry does not stop the batch. Later entries still execute.

| Status | Meaning |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `success` | `execution` holds the parsed result, events and costs. |
| `included_failure` | Simnet included the transaction, but it failed. `error` describes the failure and `kind` is `abort_by_response`, `abort_by_post_condition`, `runtime_error` or `analysis_error`. `execution` is always present. The transaction consumes the nonce, and its payload writes roll back. |
| `rejected` | Simnet did not include the transaction. `error` gives the reason. Simnet commits no writes and no nonce changes. |

Results for signed transactions also have a `transaction` property with the `txid`, `fee` and `nonce`.

{% hint style="warning" %}
**Migrating from Clarinet SDK 3.x**

Before 4.0, each entry returned the execution result directly, and most failures threw and stopped the batch. Replace direct access such as `block[0].result` with a status check and `block[0].execution.result`.
{% endhint %}

### mineEmptyBlock

`mineEmptyBlock` mines an empty block and increases block height.
Expand Down Expand Up @@ -362,23 +392,68 @@ const newHeight = simnet.blockHeight;
// Returns: 6
```

## Signed transactions

### broadcastTransaction

`broadcastTransaction` submits a signed, serialized Stacks transaction to the simnet's local mempool. It runs the node's mempool admission checks but does not execute the transaction, charge fees, consume nonces, or advance the chain. Call `mineBlock()` to mine queued transactions.

Usage:

```
broadcastTransaction(rawHex: string): BroadcastTransactionResult
```

On acceptance it returns `{ txid }`. On rejection it returns `{ error, reason, reason_data?, txid? }`.

```ts
import { makeSTXTokenTransfer } from '@stacks/transactions';

const transaction = await makeSTXTokenTransfer({
network: "testnet",
senderKey,
recipient,
amount: 100n,
fee: 1000n,
nonce: simnet.getAccountNonce(sender),
});

const admission = simnet.broadcastTransaction(transaction.serialize());
if ("error" in admission) {
throw new Error(admission.reason);
}

const [outcome] = simnet.mineBlock();
if (outcome.status === "success") {
console.log(outcome.transaction?.txid, outcome.execution.result);
}
```

Admission checks signatures, network and epoch rules, nonces, unlocked funds, transfer validity, duplicate deployments, and public-function access and argument types. Admission rejects transactions that call read-only, private or missing functions. The minimum fee is 1 micro-STX per serialized byte. Set `fee` and `nonce` explicitly so Stacks.js does not request them from a remote node.

Admission does not guarantee inclusion, because `mineBlock` checks the current state again. A transaction with a future nonce stays in the queue until `mineBlock` mines the transactions with the earlier nonces. To execute a signed transaction directly, without mempool admission, pass it to `mineBlock` as `{ broadcastTx: rawHex }`.

## Utility methods

### runSnippet
### execute

`runSnippet` executes arbitrary Clarity code without deploying.
`execute` evaluates arbitrary Clarity code without deploying a contract. The `result` property holds the parsed Clarity value of the expression. If evaluation fails, `execute` throws, so wrap it in `try`/`catch` when you expect a failure.

Usage:

```
runSnippet(snippet: string): string | ClarityValue
execute(snippet: string): ParsedTransactionResult
```

```ts
const result = simnet.runSnippet('(stx-account tx-sender)');
// Returns account balance information
const { result } = simnet.execute('(stx-account tx-sender)');
// Returns a tuple with the account's locked, unlocked and unlock-height values
```

{% hint style="warning" %}
Clarinet SDK 4.0 removed `simnet.runSnippet(code)`. Replace it with `simnet.execute(code).result`. Unlike `runSnippet`, `execute` throws on failure instead of returning an `"error:"` string.
{% endhint %}

### getContractsInterfaces

`getContractsInterfaces` returns contract interfaces with function signatures and storage.
Expand Down
Loading
Loading