--- name: starknet-js description: 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. --- # 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) |