Skip to main content
Version: 5.24.3

Interact with your contract

Once your provider, contract, and account are connected, you can interact with the contract:

  • you can read the memory of the contract, without fees.
  • you can write to memory, but you have to pay fees.
    • On Mainnet, you have to pay fees with a bridged ETH token.
    • On Testnet, you have to pay with a bridged Goerli ETH token.
    • On devnet, you have to pay with a dummy ETH token.

Your account should be funded enough to pay fees (0.01 ETH should be enough to start).

Here we will interact with a test.cairo contract (Cairo 0) already deployed on Testnet at the address:

This contract contains a storage variable called balance.

  • It can be read with the @view function: get_balance()
  • Balance can be modified with the @external function: increase_balance(amount1: felt, amount2: felt)
import { Provider, Contract, Account, ec, json } from 'starknet';

🔍 Read from contract memory, with meta-class

To read the balance, you need to connect a Provider and a Contract.
You have to call Starknet, with the use of the meta-class method: contract.function_name(params) (here params is not necessary, because there are no parameters for the get_balance function).

//initialize Provider
const provider = new Provider({ sequencer: { network: constants.NetworkName.SN_GOERLI } });
// Connect the deployed Test contract in Testnet
const testAddress = '0x5f7cd1fd465baff2ba9d2d1501ad0a2eb5337d9a885be319366b5205a414fdd';

// read abi of Test contract
const { abi: testAbi } = await provider.getClassAt(testAddress);
if (testAbi === undefined) {
throw new Error('no abi.');
}
const myTestContract = new Contract(testAbi, testAddress, provider);

// Interaction with the contract with call
const bal1 = await myTestContract.get_balance();
console.log('Initial balance =', bal1.res.toString()); // .res because the return value is called 'res' in the Cairo 0 contract.
// With Cairo 1 contract, the result value is in bal1, as bigint.

✍️ Write to contract memory, with meta-class

To increase the balance, you need in addition a connected and funded Account.

You have to invoke Starknet, with the use of the meta-class method: contract.function_name(params)

After the invoke, you have to wait the incorporation of the modification of Balance in the network, with await provider.waitForTransaction(transaction_hash)

Here is an example of how to increase and check the balance:

//initialize Provider
const provider = new Provider({ sequencer: { network: constants.NetworkName.SN_GOERLI } });
// connect your account. To adapt to your own account:
const privateKey0 = process.env.OZ_ACCOUNT_PRIVATE_KEY;
const account0Address = '0x123....789';

const account0 = new Account(provider, account0Address, privateKey0);
// add ,"1" after privateKey0 if this account is not a Cairo 0 contract

// Connect the deployed Test contract in Testnet
const testAddress = '0x5f7cd1fd465baff2ba9d2d1501ad0a2eb5337d9a885be319366b5205a414fdd';

// read abi of Test contract
const { abi: testAbi } = await provider.getClassAt(testAddress);
if (testAbi === undefined) {
throw new Error('no abi.');
}
const myTestContract = new Contract(testAbi, testAddress, provider);

// Connect account with the contract
myTestContract.connect(account0);

// Interactions with the contract with meta-class
const bal1 = await myTestContract.get_balance();
console.log('Initial balance =', bal1.res.toString()); // Cairo 0 contract
// increase_balance needs 2 felts, to add them to the balance.
const myCall = myTestContract.populate('increase_balance', [10, 30]);
const res = await myTestContract.increase_balance(myCall.calldata);
await provider.waitForTransaction(res.transaction_hash);

const bal2 = await myTestContract.get_balance();
console.log('Final balance =', bal2.res.toString());

Contract.populate() is the recommended method to define the parameters to call/invoke the Cairo functions.

Sending sequential transactions

If you intend to send sequential transactions through the contract object, like so:

const tx = await cairo1Contract.array2d_ex(data);
const tx1 = await cairo1Contract.array2d_ex(data);

Be sure to use waitForTransaction between the calls, because you may experience issues with the nonce not incrementing:

const tx = await cairo1Contract.array2d_ex(data);
await provider.waitForTransaction(tx.transaction_hash);
const tx1 = await cairo1Contract.array2d_ex(data);
await provider.waitForTransaction(tx1.transaction_hash);

Write several operations, with Account.execute

In a Starknet transaction, you can include several invoke operations. It will be performed with account.execute.

We will later see this case more in detail in this dedicated guide, but in summary, you use this command with the following parameters:

  • address of the contract to invoke
  • name of the function to invoke
  • and an array of parameters for this function
const result = await account.execute({
contractAddress: myContractAddress,
entrypoint: 'transfer',
calldata: CallData.compile({
recipient: receiverAddress,
amount: cairo.uint256(100000n),
}),
});
await provider.waitForTransaction(result.transaction_hash);

Other existing methods

Some other useful methods to interact with Starknet:

Function name defined in the code

If you want to call a function with its name contained in a variable:

const listFn = ['calc-sum', 'calc-hash', 'calc-proof'];
// fnChoice is a number defined during execution
const res = await myTestContract[listFn[fnChoice]](200, 234567897n, 865423);

Light and fast call

If you want to have a very fast execution, with minimum resource usage:

const specialParameters: Calldata = ['2036735872918048433518', '5130580', '18'];
const getResponse = await myAccount.call('get_bal', specialParameters, { parseRequest: false });

You provide the low-level numbers expected by Starknet, without any parsing or checking. See more details here.