Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Execute action fallback safeguard #42

Merged
merged 12 commits into from
Jun 26, 2024
15 changes: 12 additions & 3 deletions src/contracts/claim_account.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,21 @@ mod ClaimAccount {

#[abi(embed_v0)]
impl GiftAccountImpl of IGiftAccount<ContractState> {
fn execute_action(ref self: ContractState, selector: felt252, calldata: Array<felt252>) -> Span<felt252> {
fn execute_action(ref self: ContractState, calldata: Array<felt252>) -> Span<felt252> {
let mut calldata_span = calldata.span();
let _selector = calldata_span.pop_front(); // Skip the selector
let claim: ClaimData = Serde::deserialize(ref calldata_span).expect('gift-acc/invalid-claim');
let implementation_class_hash = get_validated_impl(claim);
// TODO consider delegating to a fixed selector to we can have a whitelist of selectors in the implementation
library_call_syscall(implementation_class_hash, selector, calldata.span()).unwrap()
library_call_syscall(implementation_class_hash, selector!("execute_action"), calldata.span()).unwrap()
}
}

#[generate_trait]
impl ArrayExt<T, +Drop<T>, +Copy<T>> of ArrayExtTrait<T> {
fn append_all(ref self: Array<T>, mut value: Span<T>) {
while let Option::Some(item) = value.pop_front() {
self.append(*item);
};
}
}

Expand Down
81 changes: 56 additions & 25 deletions src/contracts/claim_account_impl.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ use starknet_gifting::contracts::interface::{ClaimData, OutsideExecution, Starkn
#[starknet::interface]
pub trait IClaimAccountImpl<TContractState> {
fn claim_internal(ref self: TContractState, claim: ClaimData, receiver: ContractAddress) -> Array<Span<felt252>>;

fn execute_action(ref self: TContractState, calldata: Array<felt252>) -> Span<felt252>;

fn is_valid_account_signature(
self: @TContractState, claim: ClaimData, hash: felt252, remaining_signature: Span<felt252>
) -> felt252;

fn execute_from_outside_v2(
ref self: TContractState, claim: ClaimData, outside_execution: OutsideExecution, signature: Span<felt252>
) -> Array<Span<felt252>>;
}

#[starknet::interface]
pub trait IExecutableAction<TContractState> {
fn claim_external(
ref self: TContractState,
claim: ClaimData,
Expand All @@ -23,14 +37,6 @@ pub trait IClaimAccountImpl<TContractState> {
/// @param claim The claim data
/// @param receiver The address of the receiver
fn get_dust(ref self: TContractState, claim: ClaimData, receiver: ContractAddress);

fn is_valid_account_signature(
self: @TContractState, claim: ClaimData, hash: felt252, remaining_signature: Span<felt252>
) -> felt252;

fn execute_from_outside_v2(
ref self: TContractState, claim: ClaimData, outside_execution: OutsideExecution, signature: Span<felt252>
) -> Array<Span<felt252>>;
}

#[starknet::contract]
Expand All @@ -41,10 +47,9 @@ mod ClaimAccountImpl {
use openzeppelin::access::ownable::interface::{IOwnable, IOwnableDispatcherTrait, IOwnableDispatcher};
use openzeppelin::token::erc20::interface::{IERC20, IERC20DispatcherTrait, IERC20Dispatcher};
use starknet::{ClassHash, ContractAddress, get_caller_address, get_contract_address,};


use starknet_gifting::contracts::claim_hash::{ClaimExternal, IOffChainMessageHashRev1};
use starknet_gifting::contracts::interface::{ClaimData, OutsideExecution, StarknetSignature};
use starknet_gifting::contracts::utils::full_deserialize;

#[storage]
struct Storage {}
Expand Down Expand Up @@ -87,14 +92,54 @@ mod ClaimAccountImpl {
}

#[abi(embed_v0)]
impl Impl of super::IClaimAccountImpl<ContractState> {
impl ClaimAccountImpl of super::IClaimAccountImpl<ContractState> {
fn claim_internal(
ref self: ContractState, claim: ClaimData, receiver: ContractAddress
) -> Array<Span<felt252>> {
self.proceed_with_claim(claim, receiver, Zero::zero());
array![]
}

fn execute_action(ref self: ContractState, mut calldata: Array<felt252>) -> Span<felt252> {
let selector = calldata.pop_front().unwrap();
gaetbout marked this conversation as resolved.
Show resolved Hide resolved
let mut leftovers = calldata.span();
if selector == selector!("claim_external") {
let (
claimData, receiver, dust_receiver, signature
): (ClaimData, ContractAddress, ContractAddress, StarknetSignature) =
full_deserialize(
leftovers
)
.unwrap();
self.claim_external(claimData, receiver, dust_receiver, signature);
} else if selector == selector!("cancel") {
let claimData: ClaimData = full_deserialize(leftovers).unwrap();
self.cancel(claimData);
} else if selector == selector!("get_dust") {
let (claimData, receiver): (ClaimData, ContractAddress) = full_deserialize(leftovers).unwrap();
self.get_dust(claimData, receiver);
} else {
panic_with_felt252('gift/invalid-selector');
}
array![].span()
sgc-code marked this conversation as resolved.
Show resolved Hide resolved
}

fn is_valid_account_signature(
self: @ContractState, claim: ClaimData, hash: felt252, mut remaining_signature: Span<felt252>
) -> felt252 {
0 // Accounts don't support offchain signatures now, but it could
}

fn execute_from_outside_v2(
ref self: ContractState, claim: ClaimData, outside_execution: OutsideExecution, signature: Span<felt252>
) -> Array<Span<felt252>> {
panic_with_felt252('not-allowed-yet');
array![]
}
}

// Never embed this trait.
impl ExecutableActionImpl of super::IExecutableAction<ContractState> {
fn claim_external(
ref self: ContractState,
claim: ClaimData,
Expand Down Expand Up @@ -142,21 +187,7 @@ mod ClaimAccountImpl {
self.transfer_from_account(claim.fee_token, fee_balance, claim.sender);
}
}

fn is_valid_account_signature(
self: @ContractState, claim: ClaimData, hash: felt252, mut remaining_signature: Span<felt252>
) -> felt252 {
0 // Accounts don't support offchain signatures now, but it could
}

fn execute_from_outside_v2(
ref self: ContractState, claim: ClaimData, outside_execution: OutsideExecution, signature: Span<felt252>
) -> Array<Span<felt252>> {
panic_with_felt252('not-allowed-yet');
array![]
}
}

#[generate_trait]
impl Private of PrivateTrait {
fn proceed_with_claim(
Expand Down
2 changes: 1 addition & 1 deletion src/contracts/interface.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ pub trait IGiftFactory<TContractState> {
#[starknet::interface]
pub trait IGiftAccount<TContractState> {
/// @notice delegates an action to the account implementation
fn execute_action(ref self: TContractState, selector: felt252, calldata: Array<felt252>) -> Span<felt252>;
fn execute_action(ref self: TContractState, calldata: Array<felt252>) -> Span<felt252>;
}

/// @notice As defined in SNIP-9 https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-9.md
Expand Down
Loading