diff --git a/src/accountV3/accountV3.cairo b/src/accountV3/accountV3.cairo index 24c39ee..9e05f20 100644 --- a/src/accountV3/accountV3.cairo +++ b/src/accountV3/accountV3.cairo @@ -2,4 +2,200 @@ // ACCOUNT V3 // ************************************************************************* +#[starknet::contract] +pub mod AccountV3 { + use starknet::{ContractAddress, get_caller_address, get_tx_info, ClassHash, account::Call}; + use token_bound_accounts::components::account::account::AccountComponent; + use token_bound_accounts::components::upgradeable::upgradeable::UpgradeableComponent; + use token_bound_accounts::components::lockable::lockable::LockableComponent; + use token_bound_accounts::components::signatory::signatory::SignatoryComponent; + use token_bound_accounts::components::permissionable::permissionable::PermissionableComponent; + use token_bound_accounts::interfaces::{ + IUpgradeable::IUpgradeable, IExecutable::IExecutable, ILockable::ILockable, + ISignatory::ISignatory, IPermissionable::IPermissionable, IAccountV3::IAccountV3 + }; + component!(path: AccountComponent, storage: account, event: AccountEvent); + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent); + component!(path: LockableComponent, storage: lockable, event: LockableEvent); + component!(path: SignatoryComponent, storage: signatory, event: SignatoryEvent); + component!(path: PermissionableComponent, storage: permissionable, event: PermissionableEvent); + + // Account + #[abi(embed_v0)] + impl AccountImpl = AccountComponent::AccountInternalImpl; + + impl AccountInternalImpl = AccountComponent::AccountPrivateImpl; + impl UpgradeableInternalImpl = UpgradeableComponent::UpgradeablePrivateImpl; + impl LockableImpl = LockableComponent::LockablePrivateImpl; + impl SignerImpl = SignatoryComponent::SignatoryPrivateImpl; + impl PermissionableImpl = PermissionableComponent::PermissionablePrivateImpl; + + // ************************************************************************* + // STORAGE + // ************************************************************************* + #[storage] + struct Storage { + #[substorage(v0)] + account: AccountComponent::Storage, + #[substorage(v0)] + upgradeable: UpgradeableComponent::Storage, + #[substorage(v0)] + lockable: LockableComponent::Storage, + #[substorage(v0)] + signatory: SignatoryComponent::Storage, + #[substorage(v0)] + permissionable: PermissionableComponent::Storage, + } + + // ************************************************************************* + // EVENTS + // ************************************************************************* + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + AccountEvent: AccountComponent::Event, + #[flat] + UpgradeableEvent: UpgradeableComponent::Event, + #[flat] + LockableEvent: LockableComponent::Event, + #[flat] + SignatoryEvent: SignatoryComponent::Event, + #[flat] + PermissionableEvent: PermissionableComponent::Event + } + + // ************************************************************************* + // CONSTRUCTOR + // ************************************************************************* + #[constructor] + fn constructor( + ref self: ContractState, + token_contract: ContractAddress, + token_id: u256, + registry: ContractAddress, + implementation_hash: felt252, + salt: felt252 + ) { + self.account.initializer(token_contract, token_id, registry, implementation_hash, salt); + } + + // ************************************************************************* + // ACCOUNT V3 IMPL + // ************************************************************************* + #[abi(embed_v0)] + impl AccountV3 of IAccountV3 { + fn on_erc721_received( + self: @ContractState, + _token_contract: ContractAddress, + _token_id: u256, + calldata: Span + ) -> ByteArray { + let (token_contract_adddres, token_id, chain_id) = self.account.token(); + let tx_info = get_tx_info().unbox(); + let _chain_id = tx_info.chain_id; + assert(_token_contract == token_contract_adddres, 'Account Recieved it own token'); + assert(_token_id == token_id, 'Account Recieved it own token'); + assert(_chain_id == chain_id, 'Account Recieved it own token'); + return "erjgtky;lhgehtikep"; + } + fn get_context(self: @ContractState) -> (ContractAddress, felt252, felt252) { + self.account._context() + } + } + + // ************************************************************************* + // SIGNATORY IMPL + // ************************************************************************* + #[abi(embed_v0)] + impl Signatory of ISignatory { + fn is_valid_signer(self: @ContractState, signer: ContractAddress) -> bool { + self.signatory._permissioned_signer_validation(signer) + } + + fn is_valid_signature( + self: @ContractState, hash: felt252, signature: Span + ) -> felt252 { + self.signatory._is_valid_signature(hash, signature) + } + } + + // ************************************************************************* + // EXECUTABLE IMPL + // ************************************************************************* + #[abi(embed_v0)] + impl Executable of IExecutable { + fn execute(ref self: ContractState, mut calls: Array) -> Array> { + // validate signer + let caller = get_caller_address(); + assert(self.is_valid_signer(caller), 'Account: unauthorized'); + + // cannot make this call when the account is lock + let (is_locked, _) = self.lockable.is_locked(); + assert(is_locked != true, 'Account: locked'); + + // execute calls + self.account._execute(calls) + } + } + + // ************************************************************************* + // UPGRADEABLE IMPL + // ************************************************************************* + #[abi(embed_v0)] + impl Upgradeable of IUpgradeable { + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) { + // validate signer + let caller = get_caller_address(); + assert(self.is_valid_signer(caller), 'Account: unauthorized'); + + // cannot make this call when the account is lock + let (is_locked, _) = self.lockable.is_locked(); + assert(is_locked != true, 'Account: locked'); + + // upgrade account + self.upgradeable._upgrade(new_class_hash); + } + } + + // ************************************************************************* + // LOCKABLE IMPL + // ************************************************************************* + #[abi(embed_v0)] + impl Lockable of ILockable { + fn lock(ref self: ContractState, lock_until: u64) { + // validate signer + let caller = get_caller_address(); + assert(self.is_valid_signer(caller), 'Account: unauthorized'); + + // lock account + self.lockable.lock(lock_until); + } + + fn is_locked(self: @ContractState) -> (bool, u64) { + self.lockable.is_locked() + } + } + + // ************************************************************************* + // PERMISSIONABLE IMPL + // ************************************************************************* + #[abi(embed_v0)] + impl Permissionable of IPermissionable { + fn set_permission( + ref self: ContractState, + permissioned_addresses: Array, + permissions: Array + ) { + // set permissions + self.permissionable.set_permission(permissioned_addresses, permissions) + } + + fn has_permission( + self: @ContractState, owner: ContractAddress, permissioned_address: ContractAddress + ) -> bool { + self.permissionable.has_permission(owner, permissioned_address) + } + } +} diff --git a/src/interfaces.cairo b/src/interfaces.cairo index 1e3f2b4..41405f7 100644 --- a/src/interfaces.cairo +++ b/src/interfaces.cairo @@ -7,3 +7,4 @@ pub mod IExecutable; pub mod ILockable; pub mod ISignatory; pub mod IPermissionable; +pub mod IAccountV3; diff --git a/src/interfaces/IAccountV3.cairo b/src/interfaces/IAccountV3.cairo new file mode 100644 index 0000000..3fd56da --- /dev/null +++ b/src/interfaces/IAccountV3.cairo @@ -0,0 +1,20 @@ +use starknet::{ContractAddress, ClassHash,}; + + +#[starknet::interface] +pub trait IAccountV3 { + // fn on_erc721_received( + // self: @TContractState, + // token_contract: ContractAddress, + // token_id: u256, + // calldata: Span + // ) -> (felt252, ByteArray); + fn on_erc721_received( + self: @TContractState, + _token_contract: ContractAddress, + _token_id: u256, + calldata: Span + ) -> ByteArray; + fn get_context(self: @TContractState) -> (ContractAddress, felt252, felt252); +} +