> ## Documentation Index
> Fetch the complete documentation index at: https://docs.starknet.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Account abstraction

An account is an unique entity that can send transactions, users usually use wallets to manage their accounts.

Historically, in Ethereum, all accounts were Externally Owned Accounts (*EOA*) and were controlled by private keys. This is a simple and secure way to manage accounts, but it has limitations as the account logic is hardcoded in the protocol.

Account Abstraction (*AA*) is the concept behind abstracting parts of the account logic to allow for a more flexible account system.
This replaces EOA with Account Contracts, which are smart contracts that implement the account logic. This opens up a lot of possibilities that can significantly improve the user experience when dealing with accounts.

On Starknet, Account Abstraction is natively supported, and all accounts are Account Contracts.

## Account Contract

A smart contract must follow the Standard Account Interface specification defined in the [SNIP-6](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-6.md).
In practice, this means that the contract must implement the `SRC6` and `SRC5` interfaces to be considered an account contract.

### SNIP-6: SRC6 + SRC5

```rust theme={null}
/// @title Represents a call to a target contract
/// @param to The target contract address
/// @param selector The target function selector
/// @param calldata The serialized function parameters
struct Call {
    to: ContractAddress,
    selector: felt252,
    calldata: Array<felt252>
}
```

The `Call` struct is used to represent a call to a function (`selector`) in a target contract (`to`) with parameters (`calldata`). It is available under the `starknet::account` module.

```rust theme={null}
/// @title SRC-6 Standard Account
trait ISRC6 {
    /// @notice Execute a transaction through the account
    /// @param calls The list of calls to execute
    /// @return The list of each call's serialized return value
    fn __execute__(calls: Array<Call>) -> Array<Span<felt252>>;

    /// @notice Assert whether the transaction is valid to be executed
    /// @param calls The list of calls to execute
    /// @return The string 'VALID' represented as felt when is valid
    fn __validate__(calls: Array<Call>) -> felt252;

    /// @notice Assert whether a given signature for a given hash is valid
    /// @param hash The hash of the data
    /// @param signature The signature to validate
    /// @return The string 'VALID' represented as felt when the signature is valid
    fn is_valid_signature(hash: felt252, signature: Array<felt252>) -> felt252;
}
```

A transaction can be represented as a list of calls `Array<Call>` to other contracts, with atleast one call.

* `__execute__`: Executes a transaction after the validation phase. Returns an array of the serialized return of value (`Span<felt252>`) of each call.

* `__validate__`: Validates a transaction by verifying some predefined rules, such as the signature of the transaction. Returns the `VALID` short string (as a felt252) if the transaction is valid.

* `is_valid_signature`: Verify that a given signature is valid. This is mainly used by applications for authentication purposes.

Both `__execute__` and `__validate__` functions are exclusively called by the Starknet protocol.

```rust theme={null}
/// @title SRC-5 Standard Interface Detection
trait ISRC5 {
    /// @notice Query if a contract implements an interface
    /// @param interface_id The interface identifier, as specified in SRC-5
    /// @return `true` if the contract implements `interface_id`, `false` otherwise
    fn supports_interface(interface_id: felt252) -> bool;
}
```

The interface identifiers of both `SRC5` and `SRC6` must be published with `supports_interface`.

### Minimal account contract Executing Transactions

In this example, we will implement a minimal account contract that can validate and execute transactions.

```rust theme={null}
use starknet::account::Call;

#[starknet::interface]
trait ISRC6<TContractState> {
    fn execute_calls(self: @TContractState, calls: Array<Call>) -> Array<Span<felt252>>;
    fn validate_calls(self: @TContractState, calls: Array<Call>) -> felt252;
    fn is_valid_signature(
        self: @TContractState, hash: felt252, signature: Array<felt252>,
    ) -> felt252;
}

#[starknet::contract]
mod simpleAccount {
    use super::ISRC6;
    use starknet::account::Call;
    use core::num::traits::Zero;
    use core::ecdsa::check_ecdsa_signature;
    use starknet::storage::{StoragePointerWriteAccess, StoragePointerReadAccess};

    // Implement SRC5 with openzeppelin
    use openzeppelin_account::interface;
    use openzeppelin_introspection::src5::SRC5Component;
    component!(path: SRC5Component, storage: src5, event: SRC5Event);

    #[abi(embed_v0)]
    impl SRC5Impl = SRC5Component::SRC5Impl<ContractState>;
    impl SRC5InternalImpl = SRC5Component::InternalImpl<ContractState>;

    #[storage]
    struct Storage {
        #[substorage(v0)]
        src5: SRC5Component::Storage,
        public_key: felt252,
    }

    #[constructor]
    fn constructor(ref self: ContractState, public_key: felt252) {
        self.src5.register_interface(interface::ISRC6_ID);
        self.public_key.write(public_key);
    }

    #[event]
    #[derive(Drop, starknet::Event)]
    enum Event {
        #[flat]
        SRC5Event: SRC5Component::Event,
    }

    #[abi(embed_v0)]
    impl SRC6 of ISRC6<ContractState> {
        fn execute_calls(self: @ContractState, calls: Array<Call>) -> Array<Span<felt252>> {
            assert(starknet::get_caller_address().is_zero(), "Not Starknet Protocol");
            let Call { to, selector, calldata } = calls.at(0);
            let res = starknet::syscalls::call_contract_syscall(*to, *selector, *calldata).unwrap();
            array![res]
        }

        fn validate_calls(self: @ContractState, calls: Array<Call>) -> felt252 {
            assert(starknet::get_caller_address().is_zero(), "Not Starknet Protocol");
            let tx_info = starknet::get_tx_info().unbox();
            let tx_hash = tx_info.transaction_hash;
            let signature = tx_info.signature;
            if self._is_valid_signature(tx_hash, signature) {
                starknet::VALIDATED
            } else {
                0
            }
        }

        fn is_valid_signature(
            self: @ContractState, hash: felt252, signature: Array<felt252>,
        ) -> felt252 {
            if self._is_valid_signature(hash, signature.span()) {
                starknet::VALIDATED
            } else {
                0
            }
        }
    }

    #[generate_trait]
    impl SignatureVerificationImpl of SignatureVerification {
        fn _is_valid_signature(
            self: @ContractState, hash: felt252, signature: Span<felt252>,
        ) -> bool {
            check_ecdsa_signature(
                hash, self.public_key.read(), *signature.at(0_u32), *signature.at(1_u32),
            )
        }
    }
}
```
