diff --git a/src/contracts/claim_account.cairo b/src/contracts/claim_account.cairo index aaa3301..e7afc39 100644 --- a/src/contracts/claim_account.cairo +++ b/src/contracts/claim_account.cairo @@ -1,3 +1,38 @@ +use starknet::{ContractAddress, ClassHash, account::Call}; + +#[starknet::interface] +pub trait IAccount { + fn __validate__(ref self: TContractState, calls: Array) -> felt252; + fn __execute__(ref self: TContractState, calls: Array) -> Array>; + fn is_valid_signature(self: @TContractState, hash: felt252, signature: Array) -> felt252; + fn supports_interface(self: @TContractState, interface_id: felt252) -> bool; +} + + +#[starknet::interface] +pub trait IGiftAccount { + /// @notice delegates an action to the account implementation + fn execute_action(ref self: TContractState, selector: felt252, calldata: Array) -> Span; +} + +/// @notice Struct representing the arguments required for constructing a gift account +/// @dev This will be used to determine the address of the gift account +/// @param sender The address of the sender +/// @param gift_token The ERC-20 token address of the gift +/// @param gift_amount The amount of the gift +/// @param fee_token The ERC-20 token address of the fee +/// @param fee_amount The amount of the fee +/// @param claim_pubkey The public key associated with the gift +#[derive(Serde, Drop, Copy)] +pub struct AccountConstructorArguments { + pub sender: ContractAddress, + pub gift_token: ContractAddress, + pub gift_amount: u256, + pub fee_token: ContractAddress, + pub fee_amount: u128, + pub claim_pubkey: felt252 +} + #[starknet::contract(account)] mod ClaimAccount { use core::ecdsa::check_ecdsa_signature; @@ -9,14 +44,15 @@ mod ClaimAccount { use starknet_gifting::contracts::claim_account_impl::{ IClaimAccountImplLibraryDispatcher, IClaimAccountImplDispatcherTrait }; - use starknet_gifting::contracts::interface::{ - IAccount, IGiftAccount, IOutsideExecution, OutsideExecution, ClaimData, AccountConstructorArguments, - IGiftFactory, IGiftFactoryDispatcher, IGiftFactoryDispatcherTrait - }; + use starknet_gifting::contracts::claim_data::{ClaimData}; + use starknet_gifting::contracts::gift_factory::{IGiftFactory, IGiftFactoryDispatcher, IGiftFactoryDispatcherTrait}; + use starknet_gifting::contracts::outside_execution::{IOutsideExecution, OutsideExecution}; + use starknet_gifting::contracts::utils::{ calculate_claim_account_address, full_deserialize, serialize, STRK_ADDRESS, ETH_ADDRESS, TX_V1_ESTIMATE, TX_V1, TX_V3, TX_V3_ESTIMATE }; + use super::{IGiftAccount, IAccount, AccountConstructorArguments}; // https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md const SRC5_INTERFACE_ID: felt252 = 0x3f918d17e5ee77373b56385708f855659a07f75997f365cf87748628532a055; diff --git a/src/contracts/claim_account_impl.cairo b/src/contracts/claim_account_impl.cairo index ab83c07..f71aa72 100644 --- a/src/contracts/claim_account_impl.cairo +++ b/src/contracts/claim_account_impl.cairo @@ -1,6 +1,7 @@ use starknet::{ContractAddress}; -use starknet_gifting::contracts::interface::{ClaimData, OutsideExecution, StarknetSignature}; - +use starknet_gifting::contracts::claim_data::{ClaimData}; +use starknet_gifting::contracts::outside_execution::{OutsideExecution}; +use starknet_gifting::contracts::utils::{StarknetSignature}; #[starknet::interface] pub trait IClaimAccountImpl { @@ -41,10 +42,10 @@ 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_data::{ClaimData}; use starknet_gifting::contracts::claim_hash::{ClaimExternal, IOffChainMessageHashRev1}; - use starknet_gifting::contracts::interface::{ClaimData, OutsideExecution, StarknetSignature}; + use starknet_gifting::contracts::outside_execution::{OutsideExecution}; + use starknet_gifting::contracts::utils::{StarknetSignature}; #[storage] struct Storage {} diff --git a/src/contracts/claim_data.cairo b/src/contracts/claim_data.cairo new file mode 100644 index 0000000..0989795 --- /dev/null +++ b/src/contracts/claim_data.cairo @@ -0,0 +1,24 @@ +use starknet::{ContractAddress, ClassHash}; + +// TODO Align => Rename ClaimData to Claim OR claim to claim_data +// Or even rename to GIFT? so that the user will see gifts in the interface +/// @notice Struct representing the data required for a gift claim +/// @param factory The address of the factory +/// @param class_hash The class hash of the gift account +/// @param sender The address of the sender +/// @param gift_token The ERC-20 token address of the gift +/// @param gift_amount The amount of the gift +/// @param fee_token The ERC-20 token address of the fee +/// @param fee_amount The amount of the fee +/// @param claim_pubkey The public key associated with the gift +#[derive(Serde, Drop, Copy)] +pub struct ClaimData { + pub factory: ContractAddress, + pub class_hash: ClassHash, + pub sender: ContractAddress, + pub gift_token: ContractAddress, + pub gift_amount: u256, + pub fee_token: ContractAddress, + pub fee_amount: u128, + pub claim_pubkey: felt252 +} diff --git a/src/contracts/gift_factory.cairo b/src/contracts/gift_factory.cairo index a04559d..b83dbfb 100644 --- a/src/contracts/gift_factory.cairo +++ b/src/contracts/gift_factory.cairo @@ -1,3 +1,51 @@ +use starknet::{ContractAddress, ClassHash}; + +#[starknet::interface] +pub trait IGiftFactory { + /// @notice Creates a new claim + /// @dev This function can be paused by the owner of the factory and prevent any further deposits + /// @param claim_class_hash The class hash of the claim account (needed in FE to have an optimistic UI) + /// @param gift_token The ERC-20 token address of the gift + /// @param gift_amount The amount of the gift + /// @param fee_token The ERC-20 token address of the fee (can ONLY be ETH or STARK address) used to claim the gift through claim_internal + /// @param fee_amount The amount of the fee + /// @param claim_pubkey The public key associated with the gift + fn deposit( + ref self: TContractState, + claim_class_hash: ClassHash, + gift_token: ContractAddress, + gift_amount: u256, + fee_token: ContractAddress, + fee_amount: u128, + claim_pubkey: felt252 + ); + + /// @notice Retrieve the current class_hash used for creating a gift account + fn get_latest_claim_class_hash(self: @TContractState) -> ClassHash; + + fn get_account_impl_class_hash(self: @TContractState, account_class_hash: ClassHash) -> ClassHash; + + /// @notice Get the address of the claim account contract given all parameters + /// @param class_hash The class hash + /// @param sender The address of the sender + /// @param gift_token The ERC-20 token address of the gift + /// @param gift_amount The amount of the gift + /// @param fee_token The ERC-20 token address of the fee + /// @param fee_amount The amount of the fee + /// @param claim_pubkey The public key associated with the gift + fn get_claim_address( + self: @TContractState, + class_hash: ClassHash, + sender: ContractAddress, + gift_token: ContractAddress, + gift_amount: u256, + fee_token: ContractAddress, + fee_amount: u128, + claim_pubkey: felt252 + ) -> ContractAddress; +} + + #[starknet::contract] mod GiftFactory { use core::ecdsa::check_ecdsa_signature; @@ -10,11 +58,10 @@ mod GiftFactory { ClassHash, ContractAddress, syscalls::deploy_syscall, get_caller_address, get_contract_address, account::Call, get_block_timestamp }; + use starknet_gifting::contracts::claim_account::{IGiftAccount, IGiftAccountDispatcher, AccountConstructorArguments}; use starknet_gifting::contracts::claim_hash::{ClaimExternal, IOffChainMessageHashRev1}; - use starknet_gifting::contracts::interface::{ - IGiftAccountDispatcherTrait, IGiftFactory, ClaimData, AccountConstructorArguments, IGiftAccountDispatcher, - OutsideExecution, StarknetSignature - }; + use starknet_gifting::contracts::gift_factory::IGiftFactory; + use starknet_gifting::contracts::claim_data::{ClaimData}; use starknet_gifting::contracts::timelock_upgrade::{ITimelockUpgradeCallback, TimelockUpgradeComponent}; use starknet_gifting::contracts::utils::{ calculate_claim_account_address, STRK_ADDRESS, ETH_ADDRESS, serialize, full_deserialize diff --git a/src/contracts/interface.cairo b/src/contracts/interface.cairo deleted file mode 100644 index bf06526..0000000 --- a/src/contracts/interface.cairo +++ /dev/null @@ -1,137 +0,0 @@ -use starknet::{ContractAddress, ClassHash, account::Call}; - -#[starknet::interface] -pub trait IAccount { - fn __validate__(ref self: TContractState, calls: Array) -> felt252; - fn __execute__(ref self: TContractState, calls: Array) -> Array>; - fn is_valid_signature(self: @TContractState, hash: felt252, signature: Array) -> felt252; - fn supports_interface(self: @TContractState, interface_id: felt252) -> bool; -} - -#[derive(Serde, Drop, Copy, starknet::Store)] -pub struct StarknetSignature { - pub r: felt252, - pub s: felt252, -} - -#[starknet::interface] -pub trait IGiftFactory { - /// @notice Creates a new claim - /// @dev This function can be paused by the owner of the factory and prevent any further deposits - /// @param claim_class_hash The class hash of the claim account (needed in FE to have an optimistic UI) - /// @param gift_token The ERC-20 token address of the gift - /// @param gift_amount The amount of the gift - /// @param fee_token The ERC-20 token address of the fee (can ONLY be ETH or STARK address) used to claim the gift through claim_internal - /// @param fee_amount The amount of the fee - /// @param claim_pubkey The public key associated with the gift - fn deposit( - ref self: TContractState, - claim_class_hash: ClassHash, - gift_token: ContractAddress, - gift_amount: u256, - fee_token: ContractAddress, - fee_amount: u128, - claim_pubkey: felt252 - ); - - /// @notice Retrieve the current class_hash used for creating a gift account - fn get_latest_claim_class_hash(self: @TContractState) -> ClassHash; - - fn get_account_impl_class_hash(self: @TContractState, account_class_hash: ClassHash) -> ClassHash; - - /// @notice Get the address of the claim account contract given all parameters - /// @param class_hash The class hash - /// @param sender The address of the sender - /// @param gift_token The ERC-20 token address of the gift - /// @param gift_amount The amount of the gift - /// @param fee_token The ERC-20 token address of the fee - /// @param fee_amount The amount of the fee - /// @param claim_pubkey The public key associated with the gift - fn get_claim_address( - self: @TContractState, - class_hash: ClassHash, - sender: ContractAddress, - gift_token: ContractAddress, - gift_amount: u256, - fee_token: ContractAddress, - fee_amount: u128, - claim_pubkey: felt252 - ) -> ContractAddress; -} - - -#[starknet::interface] -pub trait IGiftAccount { - /// @notice delegates an action to the account implementation - fn execute_action(ref self: TContractState, selector: felt252, calldata: Array) -> Span; -} - -/// @notice As defined in SNIP-9 https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-9.md -/// @param caller Only the address specified here will be allowed to call `execute_from_outside` -/// As an exception, to opt-out of this check, the value 'ANY_CALLER' can be used -/// @param nonce It can be any value as long as it's unique. Prevents signature reuse -/// @param execute_after `execute_from_outside` only succeeds if executing after this time -/// @param execute_before `execute_from_outside` only succeeds if executing before this time -/// @param calls The calls that will be executed by the Account -/// Using `Call` here instead of re-declaring `OutsideCall` to avoid the conversion -#[derive(Copy, Drop, Serde)] -pub struct OutsideExecution { - pub caller: ContractAddress, - pub nonce: felt252, - pub execute_after: u64, - pub execute_before: u64, - pub calls: Span -} - -#[starknet::interface] -pub trait IOutsideExecution { - /// @notice Outside execution using SNIP-12 Rev 1 - fn execute_from_outside_v2( - ref self: TContractState, outside_execution: OutsideExecution, signature: Span - ) -> Array>; - - /// Get the status of a given nonce, true if the nonce is available to use - fn is_valid_outside_execution_nonce(self: @TContractState, nonce: felt252) -> bool; -} - - -// TODO Align => Rename ClaimData to Claim OR claim to claim_data -// Or even rename to GIFT? so that the user will see gifts in the interface -/// @notice Struct representing the data required for a gift claim -/// @param factory The address of the factory -/// @param class_hash The class hash of the gift account -/// @param sender The address of the sender -/// @param gift_token The ERC-20 token address of the gift -/// @param gift_amount The amount of the gift -/// @param fee_token The ERC-20 token address of the fee -/// @param fee_amount The amount of the fee -/// @param claim_pubkey The public key associated with the gift -#[derive(Serde, Drop, Copy)] -pub struct ClaimData { - pub factory: ContractAddress, - pub class_hash: ClassHash, - pub sender: ContractAddress, - pub gift_token: ContractAddress, - pub gift_amount: u256, - pub fee_token: ContractAddress, - pub fee_amount: u128, - pub claim_pubkey: felt252 -} - -/// @notice Struct representing the arguments required for constructing a gift account -/// @dev This will be used to determine the address of the gift account -/// @param sender The address of the sender -/// @param gift_token The ERC-20 token address of the gift -/// @param gift_amount The amount of the gift -/// @param fee_token The ERC-20 token address of the fee -/// @param fee_amount The amount of the fee -/// @param claim_pubkey The public key associated with the gift -#[derive(Serde, Drop, Copy)] -pub struct AccountConstructorArguments { - pub sender: ContractAddress, - pub gift_token: ContractAddress, - pub gift_amount: u256, - pub fee_token: ContractAddress, - pub fee_amount: u128, - pub claim_pubkey: felt252 -} diff --git a/src/contracts/outside_execution.cairo b/src/contracts/outside_execution.cairo new file mode 100644 index 0000000..246bb83 --- /dev/null +++ b/src/contracts/outside_execution.cairo @@ -0,0 +1,30 @@ +use starknet::{ContractAddress, ClassHash, account::Call}; + + +/// @notice As defined in SNIP-9 https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-9.md +/// @param caller Only the address specified here will be allowed to call `execute_from_outside` +/// As an exception, to opt-out of this check, the value 'ANY_CALLER' can be used +/// @param nonce It can be any value as long as it's unique. Prevents signature reuse +/// @param execute_after `execute_from_outside` only succeeds if executing after this time +/// @param execute_before `execute_from_outside` only succeeds if executing before this time +/// @param calls The calls that will be executed by the Account +/// Using `Call` here instead of re-declaring `OutsideCall` to avoid the conversion +#[derive(Copy, Drop, Serde)] +pub struct OutsideExecution { + pub caller: ContractAddress, + pub nonce: felt252, + pub execute_after: u64, + pub execute_before: u64, + pub calls: Span +} + +#[starknet::interface] +pub trait IOutsideExecution { + /// @notice Outside execution using SNIP-12 Rev 1 + fn execute_from_outside_v2( + ref self: TContractState, outside_execution: OutsideExecution, signature: Span + ) -> Array>; + + /// Get the status of a given nonce, true if the nonce is available to use + fn is_valid_outside_execution_nonce(self: @TContractState, nonce: felt252) -> bool; +} diff --git a/src/contracts/utils.cairo b/src/contracts/utils.cairo index 54fcae7..a3b9c43 100644 --- a/src/contracts/utils.cairo +++ b/src/contracts/utils.cairo @@ -1,6 +1,7 @@ use openzeppelin::utils::deployments::calculate_contract_address_from_deploy_syscall; use starknet::{ContractAddress, account::Call, contract_address::contract_address_const}; -use starknet_gifting::contracts::interface::{ClaimData, AccountConstructorArguments}; +use starknet_gifting::contracts::claim_account::{AccountConstructorArguments}; +use starknet_gifting::contracts::claim_data::{ClaimData}; pub const TX_V1: felt252 = 1; // INVOKE pub const TX_V1_ESTIMATE: felt252 = consteval_int!(0x100000000000000000000000000000000 + 1); // 2**128 + TX_V1 @@ -15,6 +16,12 @@ pub fn ETH_ADDRESS() -> ContractAddress { contract_address_const::<0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7>() } +#[derive(Serde, Drop, Copy, starknet::Store)] +pub struct StarknetSignature { + pub r: felt252, + pub s: felt252, +} + // Tries to deserialize the given data into. // The data must only contain the returned value and nothing else pub fn full_deserialize, impl EDrop: Drop>(mut data: Span) -> Option { diff --git a/src/lib.cairo b/src/lib.cairo index d43c91b..eaf7a06 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -1,10 +1,11 @@ pub mod contracts { - mod claim_account; - mod claim_account_impl; + pub mod claim_account; + pub mod claim_account_impl; + pub mod claim_data; pub mod claim_hash; - mod gift_factory; - pub mod interface; - mod timelock_upgrade; + pub mod gift_factory; + pub mod outside_execution; + pub mod timelock_upgrade; pub mod utils; } diff --git a/src/mocks/reentrant_erc20.cairo b/src/mocks/reentrant_erc20.cairo index 86c3962..61450b1 100644 --- a/src/mocks/reentrant_erc20.cairo +++ b/src/mocks/reentrant_erc20.cairo @@ -1,5 +1,6 @@ use starknet::{ClassHash, ContractAddress}; -use starknet_gifting::contracts::interface::{StarknetSignature}; +use starknet_gifting::contracts::utils::{StarknetSignature}; + #[derive(Serde, Drop, Copy, starknet::Store, Debug)] struct TestClaimData { @@ -35,10 +36,12 @@ mod ReentrantERC20 { get_caller_address, ContractAddress, get_contract_address, contract_address_const, syscalls::call_contract_syscall }; - use starknet_gifting::contracts::interface::{ - ClaimData, IGiftFactoryDispatcher, IGiftFactoryDispatcherTrait, StarknetSignature - }; + use starknet_gifting::contracts::claim_data::{ClaimData}; + + use starknet_gifting::contracts::gift_factory::{IGiftFactory, IGiftFactoryDispatcher, IGiftFactoryDispatcherTrait}; + use starknet_gifting::contracts::utils::ETH_ADDRESS; + use starknet_gifting::contracts::utils::{StarknetSignature}; use super::IMalicious; use super::TestClaimData; diff --git a/tests/setup.cairo b/tests/setup.cairo index aa08f51..11b13b3 100644 --- a/tests/setup.cairo +++ b/tests/setup.cairo @@ -3,8 +3,8 @@ use openzeppelin::utils::serde::SerializedAppend; use snforge_std::{declare, ContractClassTrait, ContractClass, start_cheat_caller_address, stop_cheat_caller_address}; use starknet::ClassHash; +use starknet_gifting::contracts::gift_factory::{IGiftFactory, IGiftFactoryDispatcher, IGiftFactoryDispatcherTrait}; -use starknet_gifting::contracts::interface::{IGiftFactory, IGiftFactoryDispatcher, IGiftFactoryDispatcherTrait}; use starknet_gifting::contracts::utils::{STRK_ADDRESS, ETH_ADDRESS}; use super::constants::{OWNER, DEPOSITOR, CLAIMER};