Plugins

How to use the Starknet Plugin

written by
Dalena Dang
Date Last Updated
March 21, 2023
Download Project Files

This is an update to the previous video where we showed you how to install and use the Starknet plugin.

This video will focus on the new changes to the starknet plugin.

Welcome to our video on How to use the Starknet Plugin version 0.5.0. Today I am going to be going over what you can do with the ape-starknet plugin. This will include account management, declaring and deploying contracts, contract interaction, and testing.

This is an update to the previous video where we showed you how to install and use the Starknet plugin. This video will focus on the new changes to the starknet plugin. The best place for you to find the most current up to date information will be through the github repository](https://github.com/ApeWorX/ape-starknet and the README documentation.

The scripts that will be used can be found in the README and the [demo github repo](https://github.com/dtdang/starknet-demo).

Section 1

ACCOUNT MANAGEMENT

Now let’s start with account management. In this section we will go over local accounts as well as testnet and mainnet accounts. We will cover importing, creating, deploying, listing, and deleting accounts through the ape console as well as using accounts to auto-sign messages.

Currently you can access local test accounts out of the box from script or console. There are 10 test accounts that you can access and you can view these test accounts by launching the starknet-devnet process manually and it will show the address, public, and private keys for the test accounts for that session.

starknet dev

Remember that you should not be using the local accounts when testing on testnet or mainnet because it may lead to a loss of funds. You will most likely only be using these local accounts while testing and using testnet or mainnet accounts in your scripts.

Importing Accounts

In order to access a live network like testnet or mainnet, you will need to import or create an account first. You can import any existing deployed account from the Starknet network or if you have an Argent-X or Braavos wallet.

To import an account from an Argent-X wallet, you would run:

 ape starknet accounts import  
  --address  
--network starknet --clash-hash argentx

You will need your private key as well in order to import the wallet. It will also ask you to set a passphrase that you will use to access your account. You can set a passphrase if you would like, however the plugin also accepts empty passphrases. The class hash for Argent-X wallets is already set in the plugin but you can change the import with whichever class hash is appropriate. Setting the network flag to starknet will add all the networks at once.

Once you have successfully imported your account, you can begin to use it in your scripts and testing. To access these accounts from script you can run:  

from ape import accounts		
account = accounts.load(“”)	
print(account.balance

In Starknet the balance of an account is the amount it holds of the fee token.

Creating Accounts


To create an account you can run the create command

ape starknet accounts create 

By default, new accounts will use the Open Zeppelin account contract implementation, however you can change the class using the –class-hash option

Please note that you may also need to change the constructor-calldata using the flag when using a different account contract type.

The create command will create a public and private key combination and store a local keyfile for the account but does not deploy the account automatically. This is because the account needs funding to pay for its deployment.

Deploying Accounts

There are multiple ways we can fund and deploy the account. You can visit the Starknet Faucet or the Starknet L2 bridge to transfer funds to your newly created account before Deploying. Remember that you cannot use an Ethereum account to directly fund a Starknet account but you may use the Starknet Bridge to send L1 Eth to an L2 account. Links to the [Faucet](https://faucet.goerli.starknet.io/) and the [Bridge](https://goerli.starkgate.starknet.io/) can be found in the official [Starknet Guide](https://docs.starknet.io/documentation/getting_started/account_setup/#transferring-goerli-eth-to-the-account)

Otherwise you can use another account with funds to fund the deployment of a newly created account using the --funder tag option

ape starknet accounts deploy  --network testnet --funder 

Listing Accounts

The starknet plugin also allows you to see all your accounts and their deployment addresses using:

ape starknet accounts list

This will show your alias, public key, class hash, and contract address. This will also let you know if the account has been deployed yet on any network.

Deleting an Account

To delete accounts you can run

Ape starknet accounts delete 
--network testnet, testnet2

The delete command differs based on its values of the --network and --address option:

  • To delete all deployments on a given network, use the –network option without the --address
  • To delete all deployments matching an address (regardless of network), use --address without network
  • To delete a deployment on a network with particular address, use both --network and --address option
  • Exclude both options to delete the whole account

Please note that you can also specify multiple networks as like with the import command.

Auto-Sign Message

Using the starknet plugin, you can also auto-sign messages using [keyring](https://pypi.org/project/keyring/). Doing this however is generally bad practice but is sometimes necessary so you should not do this unless you have to. An example would be during testnet automated deployments. To do this, you should use the set_autosign() method available on the keyfile accounts:

import keyring	
from ape import accounts

	# Use keyring package to store secretspassword = keyring.get_password("starknet-testnet-automations", "ci-shared-account")
testnet_account = accounts.load("starknet-testnet-account")
testnet_account.set_autosign(True, passphrase=password
# Won't prompt for signing or unlockingtestnet_account.sign_message([123])

Contract Dependencies

Now let’s briefly go over contracts. If you would like to use dependencies, such as the OpenZepplin Cairo Contracts package, you can add it to the ape-config.yaml.

Dependencies:
Name: OpenZeppelinCairoGithub: 
OpenZeppelin/cairo-contracts
Version: 0.6.0
Contracts_folder: src

Cairo:	
Dependencies:OpenZeppelin
Ciaro@0.6.0

Then in your Cairo contracts, you can import from these dependencies like you see here in the MyToken.cairo contract.

from openzeppelin.token.erc20.library import ERC20

Then when you run ape compile, Ape will download the dependency if needed and include it when compiling.

Section 2

Declaring and Deploying Contracts

In Starknet, you can declare contract types by publishing them to the chain. This allows other contracts to create instances of them using the deploy system call.

To declare a contract using ape-starknet, you can do the following in either script or console.

You can load an account from testnet, mainnet, or load a local account from the starknet-devnet accounts using

	from ape import account
account = accounts.load(“”) # testnet or mainnet accountaccount = accounts.containers[‘starknet’].test_accounts[0] # local account

Then you can declare your contract using

account.declare(project.MyContract)

Once you have declared your contract, you can begin to deploy it. All contracts must be declared before they are deployed and it is recommended that you only declare a contract once However you can deploy a contract as many times as you need to.

Please note that the deploy method in the ape-starknet plugin makes an invoke-function call against the [Starknet public Universal Deployer Contract (UDC)]

This will only work if the contract has been declared previously, you do not need to provide the class hash as Ape will look it up.

	from ape import projec
account.deploy(project.MyContact
Declaration = project.MyContract.deploy(sender=account)

If you want to deploy using project.MyContracy.deploy() you must include a sender as a keyword-argument (kwarg) or else the contract will not be able to deploy properly. This is a change from the previous version because DeployTransactions used to be anonymous but now we make an InvokeTransaction call to the UDC to deploy for us so the deploy transaction now requires a sender.

Another way of deploying a contract is by using a factory contract. In this example, the factory takes the class of the contract it is able to deploy. However, factory contracts can be implemented many ways, depending on the needs of the project.

%lang starknet
from starkware.cairo.common.alloc import allocfrom starkware.starknet.common.syscalls 
import deployfrom starkware.cairo.common.cairo_builtins 
import HashBuiltinfrom starkware.cairo.common.bool import FALSE, TRUE

%lang starkne
from starkware.cairo.common.alloc import alloc
from starkware.starknet.common.syscalls 
import deploy from starkware.cairo.common.cairo_builtins 
import HashBuiltinfrom starkware.cairo.common.bool 
import FALSE, TRUE

@storage_varfunc class_hash() -> (class_hash: felt) {}

@constructor

func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(cls_hash: felt) {

   class_hash.write(value=cls_hash);

   return ();

}


@externalfunc deploy_my_contract{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { let (cls_hash) = class_hash.read();    
let (current_salt) = salt.read();    
let (ctor_calldata) = alloc();    
let (contract_addr) = deploy(        class_hash=cls_hash,        
contract_address_salt=current_salt,        
constructor_calldata_size=0,        
constructor_calldata=ctor_calldata,        
deploy_from_zero=FALSE,    );    
salt.write(value=current_salt + 1);    
return ();}

@external

func deploy_my_contract{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {

   let (cls_hash) = class_hash.read();

   let (current_salt) = salt.read();

   let (ctor_calldata) = alloc();

   let (contract_addr) = deploy(

       class_hash=cls_hash,

       contract_address_salt=current_salt,

       constructor_calldata_size=0,

       constructor_calldata=ctor_calldata,

       deploy_from_zero=FALSE,

   );

   salt.write(value=current_salt + 1);

   return ();

}

# Or you can deploy using a factory contract    
# This contracts accepts a class hash of a declared contract and then deploys it   account.declare(project.MyFactory)    
factory = project.MyFactory.deploy(declaration.class_hash, sender=account)    
factory_deployment = factory.deploy_my_contract(sender=account)    
print(factory_deployment.status)

—-----------------------------Different code block—-----------------------------

   # Or you can deploy using a factory contract

   # This contracts accepts a class hash of a declared contract and then deploys it

   account.declare(project.MyFactory)

   factory = project.MyFactory.deploy(declaration.class_hash, sender=account)

   factory_deployment = factory.deploy_my_contract(sender=account)

   print(factory_deployment.status)

Now let’s run this deploy script in the terminal using ape run deploy –network starknet:local.

Section 3

CONTRACT INTERACTIONS

In this next section, we will go over Contract Interactions using the ape-starknet plugin.

After deploying a contract, you can interact with them. Deploy methods return a contract instance from which you can call methods on

In this instance, we are using a test account and have declared and deployed the contract so that we can see how we can interact with our contract.

We can use a mutable method and include a sender to delegate transactions like how I have done here.

# Interact with deployed contract    
# Use a mutable method and include sender to delegate the transaction    
receipt = contract.increase_balance(account.address, 123, sender=account)

We can also use view methods from a contract

   # Use a view method

   value = contract.get_balance(account)

   # Use a view method    value = contract.get_balance(account)

And we can access return data from a mutable method’s receipt.


# Access return data from mutable method receipt    
result = receipt.return_value
 

As a NOTE: as of now, to pass in arrays as arguments,you have to include the array size beforehand

# Note: To pass in arrays as an argument, you have to include the array size beforehand   	contract.store_sum(3, [1, 2, 3], sender=account)

The Ape Framework uses method argument length to select the correct method to call from your Python code and Cairo uses pointers for arrays. Pointers are memory addresses so arrays always have an arr_len input before it to know how many field elements or felt values to grab at that memory address. The Starknet plugin is smart enough to pass len(array) as the argument for you so the number you input for arr_len does not matter however it is needed as a placeholder for method selection in the Ape framework.

Section 4

TESTING

Now we will go over the testing capabilities of the Starknet plugin.

For testing, you can use starknet-devnet accounts. This is an example of one way you may want to access a devnet account. In our conftest.py, we first set up our account container and first test account.

import pytest
import ape
@pytest.fixturedef devnet_accounts():with ape.networks.starknet.local.use_provider(“starknet”): 
# test connects to starknet local network    		
return ape.accounts.containers["starknet"]

@pytest.fixturedef 
account(devnet_accounts):    
return devnet_accounts.test_accounts[0]

@pytest.fixture

def account(devnet_accounts):

   return devnet_accounts.test_accounts[0]

Then in our file transfer_funds.py we begin to test our accounts including creating and using ephemeral accounts. Any accounts deployed in the testing session are not saved to disk and are ephemeral.

import pytes
@pytest.fixture(scope="session")def ephemeral_account(devnet_accounts):    
new_account = devnet_accounts.create_account("demo-fund")    
# funder = account.load("argent")    
funder = devnet_accounts.test_accounts[2]    
funder.transfer(new_account, "0.02 ETH")    
new_account.deploy_self()    
return devnet_accounts.load("demo-fund")


def test_ephemeral_balance(ephemeral_account):    
balance = ephemeral_account.balance    
print(balance)    
assert balance > 0

def test_ephemeral_balance(ephemeral_account):

   balance = ephemeral_account.balance

   print(balance)

   assert balance > 0

You can also use this script in the fixture ephemeral_account if you need to manually fund and deploy your accounts in testnet or mainnet using an existing imported and created account.

We can also test our contract methods in our file test_contract.py


import pytest
@pytest.fixture(scope="module")def contract(project, account):    
account.declare(project.MyContract)    
contract = project.MyContract.deploy(sender=account)    
contract.initialize(sender=account)    
return contract

def test_increase_balance(contract, account):    
initial_balance = contract.get_balance(account)    
contract.increase_balance(account.address, 100, sender=account)    
assert contract.get_balance(account) == initial_balance + 100

Paying Fees

Starknet fees are currently paid in ETH which is an ERC-20 on the Starknet chain. To check your account balance (in ETH) you can use the balance property on the account.


from ape import account
acct = accounts.load("")
print(acct.balance)

from ape import accounts

acct = accounts.load("")
print(acct.balance)

acct = accounts.load("<ALIAS>")

print(acct.balance)

To pay fees, you can either manually set the max_fee kwarg on the invoke-transaction:

contract.increase_balance(123, max_fee=2900000000000)

NOTE: by not setting the max_fee, it will automatically get set to the value returned from the provider estimate_gas_cost() call. You do NOT need to call estimate_gas_call() explicitly.

Thank you for watching this video on learning how to use Ape-Starknet. If you have any questions, please feel free to join our Discord and ask.

PREVIOUS STARKNET TUTORIAL

SUBMIT YOUR PROJECTGET HELPLEAVE FEEDBACKAPE ACADEMY GITHUB
SHARE TUTORIAL