Skip to main content
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.
跳到安装 Skills Marketplace 发现并探索由社区构建的 Agent Skills
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/keep-starknet-strange/starknet-agentic --skill starknet-js命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
下载 Zip 下载中... keep-starknet-strange
keep-starknet-strange/starknet-agentic
打开 GitHub 仓库 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' ;
const provider = await RpcProvider .create ({
nodeUrl : 'https://rpc.starknet.lava.build'
});
Mainnet: https://rpc.starknet.lava.build
Sepolia: https://rpc.starknet-testnet.lava.build
const chainId = await provider.getChainId ();
const block = await provider.getBlock ('latest' );
const nonce = await provider.getNonceForAddress (accountAddress);
await provider.waitForTransaction (txHash);
const value = await provider.getStorageAt (contractAddress, storageKey);
Account Management
Account Creation (4 Steps) import { hash, ec, encode, CallData } from 'starknet' ;
const privateKey = '0x' + encode.buf2hex (ec.starkCurve .utils .randomPrivateKey ());
const publicKey = ec.starkCurve .getStarkKey (privateKey);
const classHash = '0x540d7f5ec7ecf317e68d48564934cb99259781b1ee3cedbbc37ec5337f8e688' ;
const constructorCalldata = CallData .compile ({ publicKey });
const address = hash.calculateContractAddressFromHash (publicKey, classHash, constructorCalldata, 0 );
Step 2: Fund the address with STRK before deployment.
import { Account } from 'starknet' ;
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'
});
Contract Interaction
Connect to Contract import { Contract } from 'starknet' ;
const contract = new Contract (abi, contractAddress, provider);
const writeContract = new Contract (abi, contractAddress, account);
Typed Contract (Type-Safe)
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 );
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
});
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' }
}
});
const tipStats = await provider.getEstimateTip ();
const tx = await account.execute (calls, { tip : tipStats.percentile_75 });
Transaction Receipt Handling const receipt = await provider.waitForTransaction (txHash);
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
);
const tx = await walletAccount.execute (calls);
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' ;
const calldata = new CallData (abi);
const compiled = calldata.compile ('transfer' , { recipient : '0x...' , amount : cairo.uint256 (1000n ) });
cairo.uint256 (1000n )
cairo.felt252 (1000 )
cairo.felt ('0x123' )
cairo.bool (true )
cairo.byteArray ('Hello' )
import { shortString } from 'starknet' ;
shortString.encodeShortString ('hello' )
shortString.decodeShortString ('0x...' )
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);
const balance = await erc20.balanceOf (account.address );
console .log ('Balance (wei):' , balance.toString ());
const amount = cairo.uint256 (1000000000000000000n );
const tx = await erc20.transfer (recipientAddress, amount);
await provider.waitForTransaction (tx.transaction_hash );
await erc20.approve (spenderAddress, cairo.uint256 (amount));
Utility Functions import { stark, ec, encode, num, hash } from 'starknet' ;
const privateKey = '0x' + encode.buf2hex (ec.starkCurve .utils .randomPrivateKey ());
const publicKey = ec.starkCurve .getStarkKey (privateKey);
num.toHex (123 );
num.toBigInt ('0x7b' );
hash.getSelectorFromName ('transfer' );
hash.calculateContractAddressFromHash (salt, classHash, calldata, deployer);
Contract Deployment
const { transaction_hash, contract_address } = await account.deploy ({
classHash : '0x...' ,
constructorCalldata : CallData .compile ({ owner : account.address }),
salt : stark.randomAddress (),
unique : true
});
const declareResponse = await account.declare ({
contract : compiledSierra,
casm : compiledCasm
});
await provider.waitForTransaction (declareResponse.transaction_hash );
const deployResponse = await account.deploy ({
classHash : declareResponse.class_hash ,
constructorCalldata : CallData .compile ({ owner : account.address })
});
const result = await account.declareAndDeploy ({
contract : compiledContract,
casm : compiledCasm,
constructorCalldata : CallData .compile ({ owner : account.address })
});
Outside Execution (SNIP-9) Execute transactions on behalf of another account (gasless/delegated):
const version = await account.getSnip9Version ();
const outsideTransaction = await account.getOutsideTransaction (
{ caller : executorAddress, execute_after : now, execute_before : now + 3600 },
calls,
'V2'
);
const result = await executorAccount.executeFromOutside (outsideTransaction);
Error Handling import { LibraryError , RpcError } from 'starknet' ;
try {
const tx = await account.execute (calls);
} catch (error) {
if (error instanceof RpcError ) {
console .error ('RPC error:' , error.code , error.message );
} else if (error instanceof LibraryError ) {
console .error ('Library error:' , error.message );
}
}
Logging & Configuration import { config, setLogLevel } from 'starknet' ;
config.set ('transactionVersion' , '0x3' );
config.get ('transactionVersion' );
setLogLevel ('DEBUG' );