Deploying Account Contracts

After building our account contract, we'll now deploy it using Starkli on the testnet and interact with other contracts.

Ensure you've installed starkli and scarb. Review the Basic Installation subchapter in Chapter 2 if you haven't.

Account Contract Configuration Files

Starkli requires two key configuration files:

  • keystore.json: A secure file that holds the private key.
  • account.json: An open file with the account's public details like public key, class hash, and address.

Optionally, Starkli can use:

  • envars.sh: A script to set environment variables for Starkli commands.

For multiple wallets, keep a clean directory structure. Each wallet should have its own folder with the three files inside. Group these under a ~/.starkli-wallets directory.

Here's a suggested structure:

tree ~/.starkli-wallets

.starkli-wallets
├── wallet-a
│   ├── account.json
│   ├── envars.sh
│   └── keystore.json
└── wallet-b
    ├── account.json
    ├── envars.sh
    └── keystore.json

This setup promotes better organization.

We'll make a custom folder in .starkli-wallets for our contract wallet:

mkdir ~/.starkli-wallets/custom

Next, we use Starkli to create keystore.json and account.json, then write envars.sh by hand.

Creating the Keystore File with Starkli

Starkli simplifies creating a keystore.json file. This encrypted file holds your private key and sits in the custom directory. You can create this with one command:

starkli signer keystore new ~/.starkli-wallets/custom/keystore.json

When you run this, you'll enter a password to secure the file. The resulting keystore.json is essential for setting up the envars.sh file, which stores environment variables.

Create envars.sh like this:

touch ~/.starkli-wallets/custom/envars.sh

Open the file and insert:

#!/bin/bash
export STARKNET_KEYSTORE=~/.starkli-wallets/custom/keystore.json

Activate the variable by sourcing the file:

source ~/.starkli-wallets/custom/envars.sh

Now, your environment is ready for the next step: creating the account.json file.

Generating the Account Configuration File

Our account contract's signature validation mirrors that in Open Zeppelin's default account contract, using a single signer and a STARK-compatible elliptic curve. Despite building our contract independently, we'll use Starkli's command for Open Zeppelin accounts to create our configuration:

starkli account oz init ~/.starkli-wallets/custom/account.json

After entering your keystore password, account.json is created. This file includes a class hash for OpenZeppelin's contract, not ours. Since the class hash influences the deployment address, the shown address won't match our contract.

Here's what account.json looks like:

{
  "version": 1,
  "variant": {
    "type": "open_zeppelin",
    "version": 1,
    "public_key": "0x1445385497364c73fabf223c55b7b323586b61c42942c99715d842c6f0a781c",
    "legacy": false
  },
  "deployment": {
    "status": "undeployed",
    "class_hash": "0x4c6d6cf894f8bc96bb9c525e6853e5483177841f7388f74a46cfda6f028c755",
    "salt": "0x36cb2427f99a75b7d4c4ceeca1e412cd94b1fc396e09fec8adca14f8dc33374"
  }
}

To deploy our unique contract, we must compile our project to obtain the correct class hash and update account.json accordingly.

Finding the Class Hash

Previously, we set up a aa directory for our account contract's Cairo code. If you don't have it, clone the repository:

git clone git@github.com:starknet-edu/aa-workshop.git aa

To compile the contract with Scarb, enter the project directory:

cd aa
scarb build

The compiled contract lies in target/dev. Use Starkli to get the class hash:

starkli class-hash target/dev/aa_Account.sierra.json

Next, edit account.json to insert the correct class hash:

code ~/.starkli-wallets/custom/account.json

Ensure the class_hash is updated:

{
  "version": 1,
  "variant": {
    "type": "open_zeppelin",
    "version": 1,
    "public_key": "0x1445385497364c73fabf223c55b7b323586b61c42942c99715d842c6f0a781c",
    "legacy": false
  },
  "deployment": {
    "status": "undeployed",
    "class_hash": "0x03480253c19b447b1d7e7a6422acf80b73866522de03126fa55796a712d9f092",
    "salt": "0x36cb2427f99a75b7d4c4ceeca1e412cd94b1fc396e09fec8adca14f8dc33374"
  }
}

To point to the updated account.json, modify envars.sh:

code ~/.starkli-wallets/custom/envars.sh

Add the account path:

#!/bin/bash
export STARKNET_KEYSTORE=~/.starkli-wallets/custom/keystore.json
export STARKNET_ACCOUNT=~/.starkli-wallets/custom/account.json

Source envars.sh to apply the changes:

source ~/.starkli-wallets/custom/envars.sh

Now, we're ready to declare the contract on the testnet.

Establishing an RPC Provider

For transactions on Starknet, an RPC provider is essential. This guide uses Infura but a personal node is a viable alternative.

Steps for Infura:

  • Create an account.
  • Start a new project for Starknet Goerli.
  • Obtain the RPC URL.
  • Add this URL to envars.sh:
aa $ code ~/.starkli-wallets/custom/envars.sh

The file should now include:

#!/bin/bash
export STARKNET_KEYSTORE=~/.starkli-wallets/custom/keystore.json
export STARKNET_ACCOUNT=~/.starkli-wallets/custom/account.json
export STARKNET_RPC=https://starknet-goerli.infura.io/v3/your-api-key

Replace your-api-key with the actual API key provided by Infura.

Declaring the Account Contract

You'll need a funded account to pay gas fees. Configure Starkli with a Braavos or Argent X wallet as the deployer. Instructions are available here.

After setting up, your Starkli wallet structure will be:

tree .

.
├── custom
│   ├── account.json
│   ├── envars.sh
│   └── keystore.json
└── deployer
    ├── account.json
    ├── envars.sh
    └── keystore.json

Source the deployer's environment file in the aa directory to use it:

source ~/.starkli-wallets/deployer/envars.sh

Declare the contract with the deployer covering gas:

starkli declare target/dev/aa_Account.sierra.json

After reaching "Accepted on L2," status (less than a minute) switch back to the account's environment:

source ~/.starkli-wallets/custom/envars.sh

Deploy the account with Starkli:

starkli account deploy ~/.starkli-wallets/custom/account.json

Starkli will wait for you to fund the address displayed with at least the estimated fee from Starknet's faucet.

Once funded, press ENTER to deploy:

...
Deployment transaction confirmed

Your account contract is now live on the Starknet testnet.

Using the Account Contract

To test our account contract, we can send 100 gwei to the wallet 0x070a...52d1 by calling the transfer function of the WETH smart contract on Starknet's testnet.

Invoke the transfer with Starkli (more details on Starkli's in Chapter 2):

starkli invoke eth transfer 0x070a012... u256:100

A successful invoke confirms that our account contract has authenticated the signature, executed the transfer, and managed the gas fees.

Here's a summary of all the steps from declaration to interaction:

# Quick Guide: Declare, Deploy, and Interact with a Custom Account Contract

# [1] Set up environment variables in envars.sh
export STARKNET_KEYSTORE=~/.starkli-wallets/custom/keystore.json
export STARKNET_ACCOUNT=~/.starkli-wallets/custom/account.json
export STARKNET_RPC=https://starknet-goerli.infura.io/v3/your-api-key

# [2] Generate keystore.json
starkli signer keystore new ~/.starkli-wallets/custom/keystore.json

# [3] Initialize account.json
starkli account oz init ~/.starkli-wallets/custom/account.json

# [4] Build the contract with Scarb
scarb build

# [5] Get the class hash
starkli class-hash target/dev/aa_Account.sierra.json

# [6] Update account.json with the real class hash
code ~/.starkli-wallets/custom/account.json

# [7] Set deployer wallet environment
source ~/.starkli-wallets/deployer/envars.sh

# [8] Declare the contract using the deployer
starkli declare target/dev/aa_Account.sierra.json

# [9] Switch to the custom wallet
source ~/.starkli-wallets/custom/envars.sh

# [10] Deploy the contract
starkli account deploy ~/.starkli-wallets/custom/account.json

# [11] Test the contract by transferring ETH
starkli invoke eth transfer 0x070a012... u256:100

# [bonus] Recommended directory structure
.
├── account.json
├── envars.sh
└── keystore.json

Summary

We've successfully deployed and used our custom account contract on Starknet with Starkli. Here's what we accomplished:

  • Set environment variables in envars.sh.
  • Created keystore.json to securely store the private key.
  • Initialized account.json as the account descriptor file.
  • Used Braavos smart wallet to set up the deployer environment.
  • Declared and deployed our account contract to the Starknet testnet.
  • Conducted a transfer to another wallet.

We matched the Open Zeppelin's contract in terms of signature methods for the constructor and __declare_deploy__ functions, which allowed us to use Starkli's Open Zeppelin preset. Should there be a need for signature modification, Starknet JS SDK would be the tool of choice.