Skip to content

Commit

Permalink
feat: SNIP-9
Browse files Browse the repository at this point in the history
  • Loading branch information
remybar committed Sep 3, 2024
1 parent 2902d76 commit b12229c
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 1 deletion.
77 changes: 76 additions & 1 deletion crates/contracts/src/account_contract.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ pub mod AccountContract {
use core::panic_with_felt252;
use core::starknet::SyscallResultTrait;
use core::starknet::account::{Call};
use core::starknet::secp256_trait::{Signature, signature_from_vrs};
use core::starknet::storage::{
Map, StorageMapReadAccess, StorageMapWriteAccess, StoragePointerReadAccess,
StoragePointerWriteAccess
Expand All @@ -57,7 +58,7 @@ pub mod AccountContract {
use core::starknet::syscalls::{call_contract_syscall, replace_class_syscall};
use core::starknet::{
ContractAddress, EthAddress, ClassHash, VALIDATED, get_caller_address, get_contract_address,
get_tx_info, Store
get_tx_info, Store, get_block_timestamp
};
use core::traits::TryInto;
use openzeppelin::token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait};
Expand All @@ -69,6 +70,8 @@ pub mod AccountContract {
use utils::helpers::{ByteArrayExTrait, ResultExTrait};
use utils::math::OverflowingMul;
use utils::serialization::{deserialize_signature, deserialize_bytes, serialize_bytes};
use utils::snip9::{IOutsideExecution, OutsideExecution};
use utils::utils::load_packed_bytes;

// Add ownable component
component!(path: ownable_component, storage: ownable, event: OwnableEvent);
Expand Down Expand Up @@ -291,6 +294,78 @@ pub mod AccountContract {
}
}

#[abi(embed_v0)]
impl OutsideExecutionImpl of IOutsideExecution<ContractState> {
fn execute_from_outside(
ref self: ContractState, outside_execution: OutsideExecution, signature: Span<felt252>,
) -> Array<Span<felt252>> {
let caller = get_caller_address();
let tx_info = get_tx_info();

if (outside_execution.caller.into() != 'ANY_CALLER') {
assert(caller == outside_execution.caller, 'Invalid caller');
}

let block_timestamp = get_block_timestamp();
assert(block_timestamp > outside_execution.execute_after, 'Too early call');
assert(block_timestamp < outside_execution.execute_before, 'Too late call');

assert(self.Account_bytecode_len.read().is_zero(), 'EOAs cannot have code');
assert(tx_info.version.into() >= 1_u256, 'Deprecated tx version');
assert(signature.len() == 5, 'Invalid signature length');

assert(outside_execution.calls.len() == 1, 'Multicall not supported');

let call = outside_execution.calls.at(0);
assert(*call.to == self.ownable.owner(), 'to is not kakarot core');
assert!(
*call.selector == selector!("eth_send_transaction"),
"selector must be eth_send_transaction"
);

let chain_id: u128 = tx_info.chain_id.try_into().unwrap() % POW_2_32;

let signature = deserialize_signature(signature, chain_id).expect('invalid signature');

let eth_caller_address: felt252 = outside_execution.caller.into();

let tx_metadata = TransactionMetadata {
address: eth_caller_address.try_into().unwrap(),
chain_id,
account_nonce: outside_execution.nonce.into(),
signature
};

let encoded_tx = deserialize_bytes(*outside_execution.calls[0].calldata)
.expect('conversion to Span<u8> failed')
.span();

let validation_result = EthTransactionTrait::validate_eth_tx(tx_metadata, encoded_tx)
.expect('failed to validate eth tx');

assert(validation_result, 'transaction validation failed');

let tx = EthTransactionTrait::decode(encoded_tx).expect('rlp decoding of tx failed');

let is_valid = match tx.try_into_fee_market_transaction() {
Option::Some(tx_fee_infos) => { self.validate_eip1559_tx(@tx, tx_fee_infos) },
Option::None => true
};

let kakarot = IKakarotCoreDispatcher { contract_address: self.ownable.owner() };

let return_data = if is_valid {
let (_, return_data, _) = kakarot.eth_send_transaction(tx);
return_data
} else {
KAKAROT_VALIDATION_FAILED.span()
};
let return_data = serialize_bytes(return_data).span();

array![return_data]
}
}

#[generate_trait]
impl Eip1559TransactionImpl of Eip1559TransactionTrait {
fn validate_eip1559_tx(
Expand Down
1 change: 1 addition & 0 deletions crates/utils/src/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub mod math;
pub mod rlp;
pub mod serialization;
pub mod set;
pub mod snip9;

mod test_data;

Expand Down
23 changes: 23 additions & 0 deletions crates/utils/src/snip9.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use core::starknet::ContractAddress;
use core::starknet::account::Call;

#[derive(Copy, Drop, Serde)]
pub struct OutsideExecution {
pub caller: ContractAddress,
pub nonce: u64,
pub execute_after: u64,
pub execute_before: u64,
pub calls: Span<Call>
}

#[starknet::interface]
pub trait IOutsideExecution<TContractState> {
/// @notice This method allows anyone to submit a transaction on behalf of the account as long
/// as they have the relevant signatures.
///
/// @param `outside_execution` The parameters of the transaction to execute
/// @param `signature` a valid signature on the message encoding of `outside_execution`
fn execute_from_outside(
ref self: TContractState, outside_execution: OutsideExecution, signature: Span<felt252>,
) -> Array<Span<felt252>>;
}

0 comments on commit b12229c

Please sign in to comment.