Deploying and interacting with a Starknet smart contract with Remix
This topic previously appeared in the Starknet Book. |
To compile, deploy, and interact with our smart contract, use the Starknet Remix plugin. This tool enables you to start to develop for Starknet without the need for any local installations on your machine.
Setting up your environment in Remix
-
Go to the Remix IDE website with the Starknet plugin activated.
-
Click the File Explorer tab to review the details of the example project.
-
Click the Starknet tab, and click the Settings. Select the latest version of Cairo available in Remix.
-
In the File explorer, open the
Scarb.toml
file to verify the version of your project. Ensure it matches the version specified originally, and correct any discrepancies, if necessary.
Customizing your environment for the Ownable
contract
For this tutorial, a default example project is provided. Modify or remove certain files and directories as needed. |
-
Rename the root directory to
ownable
. In yourScarb.toml
, under the[package]
section, set the name toownable
. -
Under
src/
, delete thebalance.cairo
andforty_two.cairo
files, if they exist. -
Open
lib.cairo
and delete all its contents.
Exploring the ownable
Starknet smart contract
Explore the Example Ownable
contract, crafted in Cairo for Starknet. It includes:
-
An ownership system.
-
A method to transfer ownership.
-
A method to check the current owner.
-
An event notification for ownership changes.
Example: ownable
Cairo contract
use starknet::ContractAddress; (1)
#[starknet::interface]
trait OwnableTrait<T> { (1)
fn transfer_ownership(ref self: T, new_owner: ContractAddress); (2)
fn get_owner(self: @T) -> ContractAddress; (2)
}
#[starknet::contract]
mod Ownable {
use super::ContractAddress;
use starknet::get_caller_address;
#[event]
#[derive(Drop, starknet::Event)]
enum Event {
OwnershipTransferred: OwnershipTransferred, (3)
}
#[derive(Drop, starknet::Event)]
struct OwnershipTransferred { (3)
#[key]
prev_owner: ContractAddress,
#[key]
new_owner: ContractAddress,
}
#[storage]
struct Storage { (4)
owner: ContractAddress,
}
#[constructor]
fn constructor(ref self: ContractState, init_owner: ContractAddress) { (5)
self.owner.write(init_owner);
}
#[abi(embed_v0)]
impl OwnableImpl of super::OwnableTrait<ContractState> {
fn transfer_ownership(ref self: ContractState, new_owner: ContractAddress) {
self.only_owner();
let prev_owner = self.owner.read();
self.owner.write(new_owner);
self.emit(Event::OwnershipTransferred(OwnershipTransferred {
prev_owner: prev_owner,
new_owner: new_owner,
}));
}
fn get_owner(self: @ContractState) -> ContractAddress {
self.owner.read()
}
}
#[generate_trait]
impl PrivateMethods of PrivateMethodsTrait {
fn only_owner(self: @ContractState) { (6)
let caller = get_caller_address();
assert(caller == self.owner.read(), 'Caller is not the owner');
}
}
}
Contract component breakdown
1 | Dependencies and Interface:starknet::ContractAddress : Represents a Starknet contract address.
OwnableTrait : Defines essential functions for transferring and retrieving ownership. |
2 | External Functions: Contains functions for transferring ownership and fetching information about the current owner. |
3 | Events: OwnershipTransferred indicates changes in ownership, providing details about the previous and new owners. |
4 | Storage: Stores the contract’s state, including the address of the current owner. |
5 | Constructor: Sets up the contract by assigning an initial owner. |
6 | Private Methods: only_owner validates if the caller is the current owner. |
Compiling the contract
To compile using Remix:
-
Click the File Explorer tab, open the
lib.cairo
file and insert the code from Example:ownable
Cairo contract. -
Click the Starknet tab, then click Home.
-
Under (1) Compile, click Compile lib.cairo.
-
Grant the necessary permissions when prompted. Select Remember my choice for a smoother compilation process in the future.
The compilation process creates an artifacts
directory containing the compiled contract in two formats: a Sierra file, in JSON format, and a Casm file. For Starknet deployment, Remix uses the Sierra file.
Deploying your contract on the development network (devnet)
Deploying a smart contract in Starknet requires two high-level steps:
-
Declare the class of your contract, that is, send your contract’s code to the network.
When you declare the contract class, you establish an initial owner by calling the class’s
constructor
function. -
Deploy an instance of the contract class.
This tutorial uses a development network (devnet) to deploy your smart contract. A devnet is a Starknet instance that you run as a local node. A devnet enables much quicker development than is possible using testnet, as well as providing privacy prior to launching on testnet.
-
Select the network by clicking the Starknet tab, and then clicking the Remote Devnet menu.
-
Under Devnet account selection, open the menu to view a list of accounts specific to the designated devnet.
-
Select a devnet account from the list and note its address for later use.
-
Click the Declare
lib.cairo
button.Remix’s terminal provides various logs with important details such as:
-
transaction_hash
: This unique hash identifies the transaction and can be used to track its status. -
class_hash
: Similar to an identifier, the class hash contains the definition of the smart contract.
Remix terminal output after declaring theownable
contract------------------------ Declaring contract: lib.cairo ------------------------ { "transaction_hash": "0x36dabf43f4962c97cf67ba132fb520091f268e7e33477d77d01747eeb0d7b43", "class_hash": "0x540779cd109ad20f46cb36d8de1ce30c75469862b4dc75f2f29d1b4d1454f60" } ---------------------- End Declaring contract: lib.cairo ----------------------
-
After Remix declares the contract class, the Declare button says Declared lib.cairo.
Now you’re ready to deploy an instance of the contract class.
-
Paste the Devnet account address you used into the
init_owner
variable. -
Click Deploy.
After deployment, Remix’s terminal displays various logs, including a transaction receipt, containing important details, such as:
-
transaction_hash
: This unique hash identifies the transaction and can be used to track its status. -
contract_address
: The address of the deployed contract. You can use this address to interact with your contract. -
data
: Contains theinit_owner
address provided to the constructor.
Ownable
class in lib.cairo
------------------------ Deploying contract: lib.cairo ------------------------
{
"transaction_hash": "0x624f5b9f57e53f6b5b62e588f0f949442172b3ad5d04f0827928b4d12c2fa58",
"contract_address": [
"0x699952dc736661d0ed573cd2b0956c80a1602169e034fdaa3515bfbc36d6410"
]
...
"data": [
"0x6b0ee6f418e47408cf56c6f98261c1c5693276943be12db9597b933d363df",
...
]
...
}
---------------------- End Deploying contract: lib.cairo ----------------------
Interacting with the contract
Now that the contract is operational on the development network, you can start interacting with it on the Starknet tab, under (3) Interact.
Identifying the owner of the contract instance
-
Under Read you should see the
get_owner()
function. Click the Call button. The function doesn’t require any arguments so the calldata field remains empty. This function reads data, so its invocation is referred to as a call.
The terminal displays the output, showing the owner’s address, which you provided during the contract’s deployment within the calldata for the constructor:
------------------- Calling get_owner ------------------------
{
"resp": {
"result": [
"0x6b0ee6f418e47408cf56c6f98261c1c5693276943be12db9597b933d363df"
]
},
"contract": "lib.cairo",
"function": "get_owner"
}
------------------- End calling get_owner --------------------
This call doesn’t consume gas because the function doesn’t modify the contract’s state.
Transferring ownership of the contract instance
-
Under (3) Interact, select Write, where functions that alter the contract’s state are listed.
-
Select the
transfer_ownership()
function, which requires providing the new owner address as input. -
Fill in the
new_owner
field with any Devnet address other than the one you used to deploy the contract.Under Devnet account selection, open the menu, select a Devnet account from the list, and copy its address.
-
Click the Call button. The terminal displays the transaction hash indicating the change in the contract’s state. Because this interaction is an
INVOKE
transaction, and it modifies the contract’s state. AnINVOKE
transaction requires the signature of the account executing the function.For
INVOKE
transactions, the terminal logs include afinality_status
parameter indicating the outcome. A status ofACCEPTED_ON_L2
indicates approval by the Sequencer, the entity responsible for receiving and processing transactions, indicating inclusion in an upcoming block. Conversely, aREJECTED
status indicates that the Sequencer did not approve the transaction, preventing its inclusion in the next block. Typically, transactions of this nature are approved, resulting in a modification of the contract’s state.
---------- Invoke transfer_ownership transaction receipt ----------------
{
"resp": {
"transaction_hash": "0x5495d56633745aa3b97bdb89c255d522e98fd2cb481974efe898560839aa472"
},
"contract": "lib.cairo",
"function": "get_owner"
}
----------End Invoke transfer_ownership transaction receipt -------------
Deployment on Starknet testnet
After testing your smart contract on a development network, the next step is deploying it onto the Starknet testnet. The Starknet testnet is a public platform accessible to all, providing an excellent environment for testing smart contracts and encouraging collaboration among developers.
Setting up a smart wallet and a Starknet account on testnet
Before deploying your smart contract on Starknet, it’s crucial to address transaction costs. While deploying on the Starknet testnet is free, having an operational smart wallet account is essential. You can set up a smart wallet and a Starknet account using either of the following platforms:
Both options offer robust Starknet wallets with advanced security measures and enhanced accessibility features enabled by the capabilities of the Cairo VM.
-
Install the recommended browser extension corresponding to your chosen wallet.
-
Follow the instructions provided by your wallet provider to deploy your account on testnet.
-
Use the Starknet Faucet to fund your account.
-
Execute the deployment of your account onto the network, typically completed within approximately 10 seconds.
Once the setup is complete, you are primed to deploy your smart contracts onto the Starknet testnet.
Deployment and Interaction
-
Proceed as per the aforementioned deployment steps.
-
Within the Environment selection tab, Select Wallet.
-
Select your Starknet account and proceed with the deployment and interaction processes for your contract.
You can monitor transaction hashes and addresses by using various Starknet block explorers such as:
These block explorers offer a graphical depiction of transactions and modifications to the contract state. Noteworthy is the visibility provided when altering contract ownership through the transfer_ownership()
function, as the emitted event by the contract becomes observable within the block explorer. This mechanism serves as a potent means to monitor contractual events.