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 — 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 — 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 suggestmaxFeeor 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) |