Skip to main content
Version: 8.6.0

Contract Instance

This guide explains how to connect to and interact with smart contracts on Starknet using Starknet.js.

Quick Start​

import { Contract, RpcProvider, Account } from 'starknet';

// For read-only access
const readOnlyContract = new Contract({
abi: contractAbi,
address: contractAddress,
providerOrAccount: myProvider, // Provider for reading
});

// For read-write access
const readWriteContract = new Contract({
abi: contractAbi,
address: contractAddress,
providerOrAccount: myAccount, // Account for writing
});

Prerequisites​

Before connecting to a contract, you need:

  • ✅ A configured Provider (for read-only) or Account (for read-write) - see Provider guide and Account guide
  • ✅ The contract's address
  • ✅ The contract's ABI (Application Binary Interface)

Loading Contract ABI​

Important

Use Starknet.js's json utility to correctly parse contract artifacts with BigInt values.

import fs from 'fs';
import { json } from 'starknet';

const contractArtifact = json.parse(fs.readFileSync('./path/to/contract.json').toString('ascii'));
const abi = contractArtifact.abi;

From Network (Fallback)​

// ⚠️ Network intensive - avoid in production
const { abi } = await myProvider.getClassAt(contractAddress);
// Save for future use
fs.writeFileSync('./contract-abi.json', json.stringify(abi, null, 2));

Creating Contract Instances​

Read-Only Access (Provider)​

const contract = new Contract({
abi: contractAbi,
address: contractAddress,
providerOrAccount: myProvider, // Provider instance
});

// Only view functions work
const balance = await contract.get_balance();

Read-Write Access (Account)​

const contract = new Contract({
abi: contractAbi,
address: contractAddress,
providerOrAccount: myAccount, // Account instance
});

// Both view and invoke functions work
const balance = await contract.get_balance();
const tx = await contract.transfer(recipient, amount);

Reading Contract State​

Direct Method Calls​

// Using contract methods (recommended)
const balance = await myContract.get_balance(address);
console.log('Balance:', balance.toString());

// Using generic call
const result = await myContract.call('get_balance', [address]);

Handling Complex Return Types​

// Struct return value
const { amount, owner } = await myContract.get_token_info(tokenId);

// Array return value
const holders = await myContract.get_all_holders();
for (const holder of holders) {
console.log('Holder:', holder);
}

Writing to Contracts​

Basic Transaction​

// Send a transaction
const { transaction_hash } = await myContract.transfer(recipient, amount);

// Wait for confirmation
await myProvider.waitForTransaction(transaction_hash);

Handling Complex Parameters​

Structs​

// Cairo struct
/*
struct TokenInfo {
amount: felt252,
owner: ContractAddress,
}
*/

// JavaScript object
await myContract.set_token_info({
amount: 1000n,
owner: '0x123...',
});

Arrays and Tuples​

// Arrays
await myContract.set_values([1, 2, 3]);

// Tuples
await myContract.set_coordinate({ x: 10, y: 20 });

Advanced Features​

Using withOptions​

The withOptions method allows you to customize how the next contract interaction is processed. These options only apply to the immediately following operation and don't persist for subsequent calls. For a complete list of available options, see the ContractOptions API reference.

// Example: Multiple options for a transaction
const result = await myContract
.withOptions({
// Block identifier for reading state
blockIdentifier: 'latest',

// Request/Response parsing
parseRequest: true, // Parse and validate input arguments
parseResponse: true, // Parse response into structured data

// Custom response formatting
formatResponse: {
balance: uint256ToBN, // Convert uint256 to BigNumber
tokens: (arr) => arr.map(Number), // Convert array elements to numbers
},

// Transaction details (for writes)
nonce: '0x1',
version: '0x1',

// V1 transaction max fee, soon to be deprecated
maxFee: 1000n,

// V3 transaction resource bounds, for RPC 0.8 and later
resourceBounds: {
l1_gas: { max_amount: '0x186a0', max_price_per_unit: '0x1' },
l2_gas: { max_amount: '0x186a0', max_price_per_unit: '0x1' },
l1_data_gas: { max_amount: '0x186a0', max_price_per_unit: '0x1' },
},
})
.myFunction(arg1, arg2);

Common Use Cases​

  1. Reading Historical State:
const pastBalance = await myContract
.withOptions({ blockIdentifier: '0x123...' })
.get_balance(address);
  1. Custom Response Formatting:
const { tokens, owner } = await myContract
.withOptions({
formatResponse: {
tokens: (arr) => arr.map(BigInt),
owner: (addr) => addr.toLowerCase(),
},
})
.get_token_info();
  1. Raw Data Mode:
const rawResult = await myContract
.withOptions({
parseRequest: false,
parseResponse: false,
})
.my_function();
  1. V3 Transaction with Resource Bounds:
const tx = await myContract
.withOptions({
version: '0x3',
resourceBounds: {
l1_gas: { max_amount: '0x186a0', max_price_per_unit: '0x1' },
l2_gas: { max_amount: '0x186a0', max_price_per_unit: '0x1' },
l1_data_gas: { max_amount: '0x186a0', max_price_per_unit: '0x1' },
},
})
.transfer(recipient, amount);

Fee Estimation​

// Estimate before sending
const { resourceBounds } = await myContract.estimateFee.transfer(recipient, amount);
console.log('Estimated fee:', json.stringify(resourceBounds));

// Use in transaction
const tx = await myContract
.withOptions({
resourceBounds,
})
.transfer(recipient, amount);

Transaction Building​

// Prepare transaction without sending
const tx = myContract.populateTransaction.transfer(recipient, amount);

// Use in multicall
const { transaction_hash } = await myAccount.execute([
tx,
anotherContract.populateTransaction.approve(spender, amount),
]);

Event Handling​

// Listen for events
const receipt = await myProvider.waitForTransaction(tx.transaction_hash);
const events = myContract.parseEvents(receipt);

// Process events
for (const event of events) {
console.log('Event:', {
name: event.name,
data: event.data,
});
}

Type Safety​

For enhanced development experience with TypeScript:

  • ✨ Full type checking
  • 💡 IDE autocompletion
  • 🐛 Compile-time error detection

See our TypeScript Integration Guide for details.

Next Steps​

Best Practices​

  • Store ABIs locally instead of fetching from network
  • Always update ABIs when recompiling contracts
  • Use TypeScript for better type safety (see TypeScript guide)
  • Estimate fees before transactions
  • Handle errors appropriately

Common Issues and Solutions​

IssueSolution
Transaction RevertedCheck parameters and contract state
Insufficient FeeUse estimateFee and add buffer
Nonce Too LowWait for previous transaction to complete
Contract Not FoundVerify address and network