SkillAgentSearch skills...

starknet-js

Reference for building Starknet applications using starknet.js v9.x SDK, including contract interaction, account management, transaction handling, fee estimation, wallet integration, and paymaster flows.

Install / Use

npx skills add internet-court/internet-court-skill --skill starknet-js

Installs into whichever agent you are using.

About this skill
📄

SKILL.md

Installable skill definition

Quality Score

96/100

Category

Legal

Supported Platforms

Universal

Our assessment of starknet-js

starknet-js scores 96/100 on our quality scale, 12th of 104 Legal skills we index (top 12%).

Its SKILL.md is 13 KB long, well organised into 29 sections with 32 code examples: a thorough specification that gives an agent plenty to work with.

With 6,129 GitHub stars, it is one of the more widely adopted skills in the catalogue.

Substance
30/30
Structure
20/20
Description
15/15
Adoption
16/20
Freshness
15/15

Maintenance, license and trust

  • The repository was last updated 39 days ago, so starknet-js is actively maintained.
  • No license is declared. By default that means all rights are reserved: you can read it, but reusing or redistributing it is not clearly permitted. Ask the author before building on it commercially.
  • Its trust signals score 88/100, with 1 caution from licensing, adoption, age or documentation. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.

starknet-js compared with similar skills

All 4 of these similar skills score higher than starknet-js; compare them before choosing.

SkillScoreStarsUpdatedFormat
starknet-js (this skill)by internet-court966.1k39d agoSKILL.md
headroomby headroomlabs-ai10074.0k1d agoCLAUDE.md
rufloby ruvnet10073.4ktodayCLAUDE.md
algorithmic-artby anthropics100177.9k5d agoSKILL.md
pptxby anthropics100177.9k5d agoSKILL.md

Frequently asked questions

How do I install starknet-js?
Run npx skills add internet-court/internet-court-skill --skill starknet-js. The install tabs above show the steps for each supported agent.
Which AI agents does starknet-js work with?
It is written for Universal, as a SKILL.md file. Other agents that read the same format can often use it too.
Is starknet-js safe to use?
It declares no license and scores 88/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
Is starknet-js still maintained?
The repository was last updated 39 days ago, so starknet-js is actively maintained.

name: starknet-js description: "Reference for building Starknet applications using starknet.js v9.x SDK, including contract interaction, account management, transaction handling, fee estimation, wallet integration, and paymaster flows." license: Apache-2.0 metadata: author: 0xlny version: "1.0.0" org: keep-starknet-strange compatibility: "Node.js 18+, TypeScript 5+, npm package: starknet@^9.0.0" keywords:

  • starknet
  • starknet-js
  • sdk
  • typescript
  • smart-contracts
  • account-abstraction
  • paymaster
  • multicall
  • snip-9
  • snip-12
  • erc-20
  • erc-721
  • wallet
  • rpc
  • fee-estimation allowed-tools:
  • Bash
  • Read
  • Write
  • Glob
  • Grep
  • Task user-invocable: true

starknet.js v9.x SDK

Related modules: skills catalog.

When to Use

  • Building Starknet apps with provider, account, contract, wallet, or paymaster flows.

When NOT to Use

  • Cairo contract authoring, deployment-only runbooks, or security audits.

Quick Start

npm install starknet

Minimal setup to read from Starknet:

import { RpcProvider, Contract } from 'starknet';

const provider = await RpcProvider.create({ nodeUrl: 'https://rpc.starknet.lava.build' });
const contract = new Contract(abi, contractAddress, provider);
const result = await contract.get_balance();

Core Architecture

Provider -> Account -> Contract
   |          |          |
Network   Identity   Interaction
  • Provider: Read-only network connection (RpcProvider)
  • Account: Extends Provider with signing and transaction capabilities
  • Contract: Type-safe interface to deployed contracts

Use Provider for read operations, Account for write operations.

Provider Setup

import { RpcProvider } from 'starknet';

// Recommended: Auto-detect RPC spec version
const provider = await RpcProvider.create({
  nodeUrl: 'https://rpc.starknet.lava.build'
});

Networks:

  • Mainnet: https://rpc.starknet.lava.build
  • Sepolia: https://rpc.starknet-testnet.lava.build

Key Methods:

const chainId = await provider.getChainId();
const block = await provider.getBlock('latest');
const nonce = await provider.getNonceForAddress(accountAddress);
await provider.waitForTransaction(txHash);

// Read storage directly
const value = await provider.getStorageAt(contractAddress, storageKey);

Account Management

Account Creation (4 Steps)

Step 1: Compute address

import { hash, ec, encode, CallData } from 'starknet';

// IMPORTANT: `stark.randomAddress()` returns an address-like random felt and is NOT a private key.
// Use a real stark curve private key generator.
const privateKey = '0x' + encode.buf2hex(ec.starkCurve.utils.randomPrivateKey());
const publicKey = ec.starkCurve.getStarkKey(privateKey);

// NOTE: account class hashes are network/account-type dependent.
// Treat this as an example only (verify the correct class hash for your setup).
const classHash = '0x540d7f5ec7ecf317e68d48564934cb99259781b1ee3cedbbc37ec5337f8e688'; // example

const constructorCalldata = CallData.compile({ publicKey });
const address = hash.calculateContractAddressFromHash(publicKey, classHash, constructorCalldata, 0);

Step 2: Fund the address with STRK before deployment.

Step 3: Deploy

import { Account } from 'starknet';

// NOTE: Account constructor signature varies across starknet.js versions.
// If this doesn't typecheck for your version, refer to the official docs.
const account = new Account({ provider, address, signer: privateKey, cairoVersion: '1' });
const { transaction_hash } = await account.deployAccount({
  classHash,
  constructorCalldata,
  addressSalt: publicKey
});
await provider.waitForTransaction(transaction_hash);

Step 4: Use the account for transactions.

Connect to Existing Account

const account = new Account({
  provider,
  address: '0x123...',
  signer: privateKey,
  cairoVersion: '1'  // Optional, auto-detected if omitted
});

Contract Interaction

Connect to Contract

import { Contract } from 'starknet';

const contract = new Contract(abi, contractAddress, provider);  // Read-only
const writeContract = new Contract(abi, contractAddress, account);   // Read-write

Typed Contract (Type-Safe)

// Get full TypeScript autocomplete and type checking from ABI
const typedContract = contract.typedv2(abi);
const balance = await typedContract.balanceOf(userAddress);

Read State

const balance = await contract.get_balance();
const userBalance = await contract.balanceOf(userAddress);

Write (Execute)

const tx = await contract.increase_balance(100);
await provider.waitForTransaction(tx.transaction_hash);

Multicall (Batch Transactions)

import { CallData, cairo } from 'starknet';

const calls = [
  {
    contractAddress: tokenAddress,
    entrypoint: 'approve',
    calldata: CallData.compile({ spender: bridgeAddress, amount: cairo.uint256(1000n) })
  },
  {
    contractAddress: bridgeAddress,
    entrypoint: 'deposit',
    calldata: CallData.compile({ amount: cairo.uint256(1000n) })
  }
];

const tx = await account.execute(calls);

Using populate() for type-safety:

const approveCall = tokenContract.populate('approve', {
  spender: bridgeAddress,
  amount: cairo.uint256(1000n)
});
const depositCall = bridgeContract.populate('deposit', { amount: cairo.uint256(1000n) });
const tx = await account.execute([approveCall, depositCall]);

Parse Events

const receipt = await provider.getTransactionReceipt(txHash);
const events = contract.parseEvents(receipt);
const transferEvents = contract.parseEvents(receipt, 'Transfer');

Transaction Simulation

Simulate before executing to catch reverts and inspect state changes:

const simResult = await account.simulateTransaction(
  [{ type: 'INVOKE', payload: calls }],
  { skipValidate: false }
);

console.log('Fee estimate:', simResult[0].fee_estimation);
console.log('Trace:', simResult[0].transaction_trace);

// Check state changes before execution
const trace = simResult[0].transaction_trace;
if (trace?.state_diff) {
  console.log('Storage changes:', trace.state_diff.storage_diffs);
}

Fee Estimation

const fee = await account.estimateInvokeFee(calls);
console.log({
  overallFee: fee.overall_fee,
  resourceBounds: fee.resourceBounds  // V3: l1_gas, l2_gas, l1_data_gas
});

Execute with custom bounds:

const tx = await account.execute(calls, {
  resourceBounds: {
    l1_gas: { amount: '0x2000', price: '0x1000000000' },
    l2_gas: { amount: '0x0', price: '0x0' },
    l1_data_gas: { amount: '0x1000', price: '0x1000000000' }
  }
});

With priority tip:

const tipStats = await provider.getEstimateTip();
const tx = await account.execute(calls, { tip: tipStats.percentile_75 });

Transaction Receipt Handling

const receipt = await provider.waitForTransaction(txHash);

// Status check helpers
if (receipt.isSuccess()) {
  console.log('Transaction succeeded');
} else if (receipt.isReverted()) {
  console.log('Reverted:', receipt.revert_reason);
} else if (receipt.isRejected()) {
  console.log('Rejected');
} else if (receipt.isError()) {
  console.log('Error');
}

Wallet Integration

Connect to browser wallets (ArgentX, Braavos):

import { connect } from '@starknet-io/get-starknet';
import { WalletAccount } from 'starknet';

const selectedWallet = await connect({ modalMode: 'alwaysAsk' });
const walletAccount = await WalletAccount.connect(
  { nodeUrl: 'https://rpc.starknet.lava.build' },
  selectedWallet
);

// Use like regular Account
const tx = await walletAccount.execute(calls);

// Event handlers
walletAccount.onAccountChange((accounts) => console.log('New account:', accounts[0]));
walletAccount.onNetworkChanged((chainId) => console.log('Network changed:', chainId));

Paymaster (Gas Sponsorship)

Setup paymaster for sponsored or alternative gas token transactions:

import { PaymasterRpc, Account } from 'starknet';

const paymaster = new PaymasterRpc({ nodeUrl: 'https://sepolia.paymaster.avnu.fi' });
const account = new Account({ provider, address, signer: privateKey, paymaster });

Sponsored (dApp pays gas):

const tx = await account.executePaymasterTransaction(calls, { feeMode: { mode: 'sponsored' } });

Alternative token (e.g., USDC):

const tokens = await account.paymaster.getSupportedTokens();
const feeDetails = { feeMode: { mode: 'default', gasToken: USDC_ADDRESS } };
const estimate = await account.estimatePaymasterTransactionFee(calls, feeDetails);
const tx = await account.executePaymasterTransaction(calls, feeDetails, estimate.suggested_max_fee_in_gas_token);

Message Signing (SNIP-12)

const typedData = {
  types: {
    StarknetDomain: [
      { name: 'name', type: 'shortstring' },
      { name: 'version', type: 'shortstring' },
      { name: 'chainId', type: 'shortstring' },
      { name: 'revision', type: 'shortstring' }
    ],
    Message: [{ name: 'content', type: 'shortstring' }]
  },
  primaryType: 'Message',
  domain: { name: 'MyDapp', version: '1', chainId: 'SN_SEPOLIA', revision: '1' },
  message: { content: 'Hello Starknet' }
};

const signature = await account.signMessage(typedData);
const msgHash = await account.hashMessage(typedData);
const isValid = ec.starkCurve.verify(signature, msgHash, publicKey);

CallData & Cairo Types

import { CallData, cairo, CairoCustomEnum, CairoOption, CairoOptionVariant } from 'starknet';

// Compile with ABI
const calldata = new CallData(abi);
const compiled = calldata.compile('transfer', { recipient: '0x...', amount: cairo.uint256(1000n) });

// Cairo type helpers - always use BigInt (n suffix) for token amounts
cairo.uint256(1000n)          // { low, high } - ALWAYS use BigInt for precision
cairo.felt252(1000)           // BigInt
cairo.felt('0x123')           // hex to felt
cairo.bool(true)              // Cairo bool
cairo.byteArray('Hello')      // ByteArray for long strings

// Short strings (<= 31 chars)
import { shortString } from 'starknet';
shortString.encodeShortString('hello')  // felt252
shortString.decodeShortString('0x...')  // 'hello'

// Enums and Options
const myEnum = new CairoCustomEnum({ Variant1: { value: 123 } });
const some = new CairoOption(CairoOptionVariant.Some, value);

Important: Always use BigInt (e.g., 1000n) for token amounts and balances. Never use Number() or parseFloat() on wei values -- JavaScript numbers lose precision above 2^53.

ERC-20 Token Operations

const erc20 = new Contract(erc20Abi, tokenAddress, account);

// Read balance (returns BigInt - do NOT convert with Number())
const balance = await erc20.balanceOf(account.address);
console.log('Balance (wei):', balance.toString());

// Transfer (use BigInt for amount)
const amount = cairo.uint256(1000000000000000000n); // 1 token (18 decimals)
const tx = await erc20.transfer(recipientAddress, amount);
await provider.waitForTransaction(tx.transaction_hash);

// Approve + transferFrom pattern
await erc20.approve(spenderAddress, cairo.uint256(amount));

Utility Functions

import { stark, ec, encode, num, hash } from 'starknet';

// Key generation
const privateKey = '0x' + encode.buf2hex(ec.starkCurve.utils.randomPrivateKey());
const publicKey = ec.starkCurve.getStarkKey(privateKey);

// Number conversions
num.toHex(123);           // '0x7b'
num.toBigInt('0x7b');     // 123n

// Hashing
hash.getSelectorFromName('transfer');
hash.calculateContractAddressFromHash(salt, classHash, calldata, deployer);

Contract Deployment

// Deploy via UDC
const { transaction_hash, contract_address } = await account.deploy({
  classHash: '0x..

Truncated for display — read the full file on GitHub.

Related Skills

View on GitHub
GitHub Stars6.1k
CategoryLegal
Updated1mo ago
Forks110

Languages

TypeScript

Trust signals

88/100

From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.

1 medium