From 964f9b89d48808b4adc1928718d5f7f20fd48457 Mon Sep 17 00:00:00 2001 From: Mubarak Muhammad Aminu Date: Wed, 23 Oct 2024 04:31:40 +0100 Subject: [PATCH] Ft account impl (#101) * IStarknetPhoneAccount implementation * remove sample account impl * chore:remove target and update .gitignore --- .gitignore | 3 + contracts/Scarb.lock | 14 +++ contracts/Scarb.toml | 18 ++++ contracts/src/account.cairo | 1 + contracts/src/account/account.cairo | 133 ++++++++++++++++++++++++++++ contracts/src/lib.cairo | 1 + 6 files changed, 170 insertions(+) create mode 100644 contracts/Scarb.lock create mode 100644 contracts/Scarb.toml create mode 100644 contracts/src/account.cairo create mode 100644 contracts/src/account/account.cairo create mode 100644 contracts/src/lib.cairo diff --git a/.gitignore b/.gitignore index f6a762cd..ee8502b5 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,9 @@ # mdBook docs/book/ +target + + **/.gradle/* lightclientservice/app/build diff --git a/contracts/Scarb.lock b/contracts/Scarb.lock new file mode 100644 index 00000000..727eeb1b --- /dev/null +++ b/contracts/Scarb.lock @@ -0,0 +1,14 @@ +# Code generated by scarb DO NOT EDIT. +version = 1 + +[[package]] +name = "contracts" +version = "0.1.0" +dependencies = [ + "snforge_std", +] + +[[package]] +name = "snforge_std" +version = "0.27.0" +source = "git+https://github.com/foundry-rs/starknet-foundry?tag=v0.27.0#2d99b7c00678ef0363881ee0273550c44a9263de" diff --git a/contracts/Scarb.toml b/contracts/Scarb.toml new file mode 100644 index 00000000..17692873 --- /dev/null +++ b/contracts/Scarb.toml @@ -0,0 +1,18 @@ +[package] +name = "contracts" +version = "0.1.0" +edition = "2023_11" + +# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html + +[dependencies] +starknet = "2.7.0" + +[dev-dependencies] +snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry", tag = "v0.27.0" } + +[[target.starknet-contract]] +sierra = true + +[scripts] +test = "snforge test" diff --git a/contracts/src/account.cairo b/contracts/src/account.cairo new file mode 100644 index 00000000..b0edc6c1 --- /dev/null +++ b/contracts/src/account.cairo @@ -0,0 +1 @@ +pub mod account; diff --git a/contracts/src/account/account.cairo b/contracts/src/account/account.cairo new file mode 100644 index 00000000..37cd63f1 --- /dev/null +++ b/contracts/src/account/account.cairo @@ -0,0 +1,133 @@ +use starknet::{account::Call, ContractAddress, ClassHash}; + +#[starknet::interface] +pub trait IStarknetPhoneAccount { + fn get_public_key(self: @TContractState) -> felt252; + fn set_public_key(ref self: TContractState, new_public_key: felt252); + fn is_valid_signature( + self: @TContractState, hash: felt252, signature: Span + ) -> felt252; + fn __validate__(ref self: TContractState, calls: Array) -> felt252; + fn __validate_declare__(self: @TContractState, class_hash: felt252) -> felt252; + fn __validate_deploy__(ref self: TContractState, public_key: felt252) -> felt252; + fn __execute__(ref self: TContractState, calls: Array) -> Array>; +} + +#[starknet::contract(account)] +pub mod StarknetPhoneAccount { + use starknet::storage::StoragePointerWriteAccess; + use starknet::storage::StoragePointerReadAccess; + use starknet::{ + get_tx_info, get_caller_address, get_contract_address, ContractAddress, account::Call, + syscalls::call_contract_syscall, syscalls::replace_class_syscall, ClassHash, + SyscallResultTrait + }; + use core::ecdsa::check_ecdsa_signature; + use core::num::traits::zero::Zero; + + #[storage] + struct Storage { + _public_key: felt252, + } + + #[constructor] + fn constructor(ref self: ContractState, public_key: felt252) { + self._public_key.write(public_key); + } + + #[abi(embed_v0)] + impl IAccountImpl of super::IStarknetPhoneAccount { + fn get_public_key(self: @ContractState) -> felt252 { + self._public_key.read() + } + + fn set_public_key(ref self: ContractState, new_public_key: felt252) { + self.assert_only_self(); + self._public_key.write(new_public_key); + } + + fn is_valid_signature( + self: @ContractState, hash: felt252, signature: Span + ) -> felt252 { + self._is_valid_signature(hash, signature) + } + + fn __validate_deploy__(ref self: ContractState, public_key: felt252) -> felt252 { + self.validate_transaction() + } + + fn __validate_declare__(self: @ContractState, class_hash: felt252) -> felt252 { + self.validate_transaction() + } + + fn __validate__(ref self: ContractState, mut calls: Array) -> felt252 { + self.validate_transaction() + } + + fn __execute__(ref self: ContractState, mut calls: Array) -> Array> { + let caller = get_caller_address(); + assert(caller.is_zero(), 'invalid caller'); + + let tx_info = get_tx_info().unbox(); + assert(tx_info.version != 0, 'invalid tx version'); + + self._execute_calls(calls) + } + } + + #[generate_trait] + impl internalImpl of InternalTrait { + fn assert_only_self(ref self: ContractState) { + let caller = get_caller_address(); + let self = get_contract_address(); + assert(self == caller, 'Account: unathorized'); + } + + fn validate_transaction(self: @ContractState) -> felt252 { + let tx_info = get_tx_info().unbox(); + let tx_hash = tx_info.transaction_hash; + let signature = tx_info.signature; + assert( + self._is_valid_signature(tx_hash, signature) == starknet::VALIDATED, + 'Account: invalid signature' + ); + starknet::VALIDATED + } + + fn _is_valid_signature( + self: @ContractState, hash: felt252, signature: Span + ) -> felt252 { + assert(signature.len() == 2_u32, 'invalid signature'); + let public_key = self._public_key.read(); + + if check_ecdsa_signature( + message_hash: hash, + public_key: public_key, + signature_r: *signature[0_u32], + signature_s: *signature[1_u32], + ) { + return starknet::VALIDATED; + } else { + return 0; + } + } + + fn _execute_calls(ref self: ContractState, mut calls: Array) -> Array> { + let mut result: Array> = ArrayTrait::new(); + let mut calls = calls; + + loop { + match calls.pop_front() { + Option::Some(call) => { + match call_contract_syscall(call.to, call.selector, call.calldata) { + Result::Ok(mut retdata) => { result.append(retdata); }, + Result::Err(_) => { panic(array!['multicall_failed']); } + } + }, + Option::None(_) => { break (); } + }; + }; + result + } + } +} diff --git a/contracts/src/lib.cairo b/contracts/src/lib.cairo new file mode 100644 index 00000000..b0edc6c1 --- /dev/null +++ b/contracts/src/lib.cairo @@ -0,0 +1 @@ +pub mod account;