Skip to main content
Version: Next

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