-
Notifications
You must be signed in to change notification settings - Fork 94
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* test: SNIP-6 implementation * docs: errors recheck * test: SNIP-6 implementation * docs: errors recheck * feat: add simple account example * feat/fix: revisions on #200 * feat:implement SRC5 * feat: implementation with oz * fix: oz impl src5 for account --------- Co-authored-by: Oluwaseun Jeremiah <jeremiah@Jemiah> Co-authored-by: julio4 <[email protected]>
- Loading branch information
1 parent
6f4d055
commit 630092f
Showing
10 changed files
with
212 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
target |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# Code generated by scarb DO NOT EDIT. | ||
version = 1 | ||
|
||
[[package]] | ||
name = "ecdsa_verification" | ||
version = "0.1.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
[package] | ||
name = "simple_account" | ||
version.workspace = true | ||
edition = '2023_11' | ||
|
||
|
||
[dependencies] | ||
starknet.workspace = true | ||
openzeppelin.workspace = true | ||
|
||
[scripts] | ||
test.workspace = true | ||
|
||
[[target.starknet-contract]] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
mod simple_account; | ||
|
||
#[cfg(test)] | ||
mod tests; |
90 changes: 90 additions & 0 deletions
90
listings/advanced-concepts/simple_account/src/simple_account.cairo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
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; | ||
|
||
// 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) | ||
) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
#[cfg(test)] | ||
mod tests { // TODO | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
73 changes: 73 additions & 0 deletions
73
src/advanced-concepts/account_abstraction/account_contract.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
# 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 | ||
/// @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 | ||
/// @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. | ||
|
||
<!-- TODO replace with link to SRC5 example #109 --> | ||
|
||
```rust | ||
/// @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 | ||
{{#rustdoc_include ../../../listings/advanced-concepts/simple_account/src/simple_account.cairo}} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# 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. | ||
|
||
In this section we will how to implement an Account. | ||
<!-- and common customizations that can be done to it. --> |