# Starknet JS

> Use when writing or debugging JavaScript/TypeScript that interacts with Starknet through the starknet.js SDK — building Call objects or calldata, encoding/decoding Cairo types (felt252, u256, structs, arrays, spans, ByteArray, Option/Result/custom enums), or working with contracts, accounts, providers, transactions, fees, deployment, or events.

- Skill: `starknet-io/starknet-js` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add starknet-io/starknet-js`
- Raw SKILL.md: https://api.skillmd.com/api/skills/starknet-io/starknet-js/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: starknet-io (https://skillmd.com/u/starknet-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/starknet-io/starknet-js

---


# starknet.js

Official JS/TS SDK for Starknet. Skill verified against **starknet.js 10.8.0**.

**Do not trust training-data memory of starknet.js**: it is typically outdated and wrong. Constructors take option objects, only V3 transactions exist (STRK fees, no `maxFee`). When this skill and memory disagree, the skill wins.

Layer model: `Account` (signs & submits) wraps `RpcProvider` (parses, retries) wraps `RpcChannel` (raw JSON-RPC).

## Reference files

- **[calldata.md](calldata.md)** — REQUIRED reading before encoding or decoding anything: Call/calldata construction, wire format of every Cairo type, enums (Option/Result/custom), ByteArray, string auto-detection traps, response parsing, `decodeParameters`.
- **[interacting.md](interacting.md)** — providers, accounts, contracts, multicall, fees/tips, declare/deploy, receipts, events, RPC-version compatibility.

## Non-negotiable rules

- Only **V3** transactions (INVOKE/DECLARE/DEPLOY_ACCOUNT); fees paid in STRK via `resourceBounds` + `tip`. Never suggest `maxFee` or V1/V2.
- Constructors take an options object: `new Account({ provider, address, signer })`, `new Contract({ abi, address, providerOrAccount })`, `new RpcProvider({ nodeUrl })`.
- Never prepend an array length to calldata arrays — the library adds it.

## Quick reference

| Task                      | API                                                                                                                    |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| Connect to node           | `new RpcProvider({ nodeUrl })` — or `await RpcProvider.create({ nodeUrl })` to auto-detect RPC version                 |
| Account                   | `new Account({ provider, address, signer: privateKey })`                                                               |
| Contract                  | `new Contract({ abi, address, providerOrAccount })`                                                                    |
| Read                      | `await myContract.my_view_fn(arg1, arg2)` → parsed JS value                                                            |
| Write                     | `const { transaction_hash } = await myContract.my_fn(args)` then `await provider.waitForTransaction(transaction_hash)` |
| Build a Call              | `myContract.populate('fn', { ...args })` → `{ contractAddress, entrypoint, calldata }`                                 |
| Compile calldata (ABI)    | `myContract.compile('fn', args)` or `new CallData(abi).compile('fn', args)`                                            |
| Compile calldata (no ABI) | `CallData.compile(args)` — several traps, see calldata.md                                                              |
| Multicall                 | `await account.execute([call1, call2])`                                                                                |
| Decode raw felts          | `new CallData(abi).decodeParameters('core::…::Type', feltArray)`                                                       |
| Events                    | `myContract.parseEvents(receipt)`, `provider.getEvents({ ... })`                                                       |
| Deploy                    | `await Contract.factory({ contract, casm, account, constructorCalldata })`                                             |

## Top mistakes (from outdated model knowledge)

| Mistake                                              | Fix                                                                      |
| ---------------------------------------------------- | ------------------------------------------------------------------------ |
| `new Account(provider, address, pk)` positional      | Options object (see above)                                               |
| `maxFee`, V1/V2 transactions, ETH fees               | V3 only; `resourceBounds` from `estimateInvokeFee`, `tip`                |
| Manually prepending `array_len`                      | Never — automatic                                                        |
| `CallData.compile([myU256Bigint])` gives 1 felt      | Use `cairo.uint256(n)` without ABI; a plain bigint is fine only with ABI |
| `new CairoCustomEnum({ OnlyActive: v })` without ABI | Without ABI, list ALL variants (`undefined` for inactive)                |
| Expecting `.res`-style result objects                | Cairo 1 returns the value directly (bigint, object, array, enum class)   |

