Unlike Ethereum where accounts are derived from a private key, all Starknet accounts are contracts. This means there’s no Externally Owned Account (EOA) concept on Starknet.

Instead, the network features native account abstraction and signature validation happens at the contract level.

For a general overview of account abstraction, see Starknet’s documentation. A more detailed discussion on the topic can be found in Starknet Shaman’s forum.

For detailed information on the usage and implementation check the API Reference section.

Standard Account Interface

Accounts in Starknet are smart contracts, and so they can be deployed and interacted with like any other contract, and can be extended to implement any custom logic. However, an account is a special type of contract that is used to validate and execute transactions. For this reason, it must implement a set of entrypoints that the protocol uses for this execution flow. The SNIP-6 proposal defines a standard interface for accounts, supporting this execution flow and interoperability with DApps in the ecosystem.

ISRC6 Interface

/// Represents a call to a target contract function.
struct Call {
    to: ContractAddress,
    selector: felt252,
    calldata: Array<felt252>

/// Standard Account Interface
trait ISRC6 {
    /// Executes a transaction through the account.
    fn __execute__(calls: Array<Call>) -> Array<Span<felt252>>;

    /// Asserts whether the transaction is valid to be executed.
    fn __validate__(calls: Array<Call>) -> felt252;

    /// Asserts whether a given signature for a given hash is valid.
    fn is_valid_signature(hash: felt252, signature: Array<felt252>) -> felt252;

SNIP-6 adds the is_valid_signature method. This method is not used by the protocol, but it’s useful for DApps to verify the validity of signatures, supporting features like Sign In with Starknet.

SNIP-6 also defines that compliant accounts must implement the SRC5 interface following SNIP-5, as a mechanism for detecting whether a contract is an account or not through introspection.

ISRC5 Interface

/// Standard Interface Detection
trait ISRC5 {
    /// Queries if a contract implements a given interface.
    fn supports_interface(interface_id: felt252) -> bool;

SNIP-6 compliant accounts must return true when queried for the ISRC6 interface Id.

Even though these interfaces are not enforced by the protocol, it’s recommended to implement them for enabling interoperability with the ecosystem.

Protocol-level methods

In this section we will describe the methods that the protocol uses for abstracting the accounts. The first two are required for enabling accounts to be used for executing transactions. The rest are optional:

  1. __validate__ verifies the validity of the transaction to be executed. This is usually used to validate signatures, but the entrypoint implementation can be customized to feature any validation mechanism with some limitations.

  2. __execute__ executes the transaction if the validation is successful.

  3. __validate_declare__ optional entrypoint similar to __validate__ but for transactions meant to declare other contracts.

  4. __validate_deploy__ optional entrypoint similar to __validate__ but meant for counterfactual deployments.

Although these entrypoints are available to the protocol for its regular transaction flow, they can also be called like any other method.

Deploying an account

In Starknet there are two ways of deploying smart contracts: using the deploy_syscall and doing counterfactual deployments. The former can be easily done with the Universal Deployer Contract (UDC), a contract that wraps and exposes the deploy_syscall to provide arbitrary deployments through regular contract calls. But if you don’t have an account to invoke it, you will probably want to use the latter.

To do counterfactual deployments, you need to implement another protocol-level entrypoint named __validate_deploy__. Check the counterfactual deployments guide to learn how.