diff --git a/.gitignore b/.gitignore index 31b07586..d4eae8ce 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ tests/fixtures/TestDapp.json tests/fixtures/TestDapp.casm dump package-lock.json + +starknet-contract-verifier +voyager-verify \ No newline at end of file diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 00000000..967b4ca2 --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +scarb 0.5.1 \ No newline at end of file diff --git a/Scarb.lock b/Scarb.lock new file mode 100644 index 00000000..b0cb1be7 --- /dev/null +++ b/Scarb.lock @@ -0,0 +1,6 @@ +# Code generated by scarb DO NOT EDIT. +version = 1 + +[[package]] +name = "argent" +version = "0.1.0" diff --git a/Scarb.toml b/Scarb.toml new file mode 100644 index 00000000..5b0ff061 --- /dev/null +++ b/Scarb.toml @@ -0,0 +1,21 @@ +[package] +name = "argent" +version = "0.1.0" + +# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest + +[[target.starknet-contract]] +sierra = true +casm = true +allowed-libfuncs-list.name = "audited" + +[dependencies] +starknet = "2.0.1" + +[tool.fmt] +max-line-length = 120 +sort-module-level-items = true + + +[tool.voyager] +ArgentMultisig = { path = "multisig/argent_multisig.cairo" } diff --git a/contracts/account/README.md b/contracts/account/README.md deleted file mode 100644 index 5dd8d61e..00000000 --- a/contracts/account/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# Argent Account - -## High-Level Specification - -The Argent account is a custom multisig (1-of-1, 2-of-2 or 2-of-3) tailored for individuals. - -The primary key called the `owner` is typically stored on the user's device. A second key called the `guardian` acts both as a co-validator for typical operations of the wallet, and as the trusted actor that can recover the wallet in case the `owner` key is lost or compromised. In a typical setting the `guardian` key is managed by an off-chain service to enable fraud monitoring (e.g. trusted contacts, daily limits, etc) and recovery. - -The user can always opt-out of the guardian service and manage the guardian key himself. Alternatively he/she can add a second `guardian_backup` key to the account that has the same role as the `guardian` and can be used as the ultimate censorship resistance guarantee. The account can only have a `guardian_backup` when the `guardian` is set. - -By default the account can execute a sequence of operations such as calling external contracts in a multicall. A multicall will fail if one of the inner call fails. Whenever a function of the account must be called (`change_owner`, `trigger_escape_guardian`, `upgrade`, etc), it should be the only call performed in this multicall. - -In addition to the main `__execute__` entry point used by the Starknet protocol, the account can also be called by an external party via the `execute_from_outside` method to e.g. enable sponsored transactions. The calling party must provide a valid signature (`owner` and/or `guardian`) for the target execution. - -Normal operations of the wallet (calling external contracts via `__execute__` or `execute_from_outside`, `change_owner`, `change_guardian`, `change_guardian_backup`, `cancel_escape`, `upgrade`) require the approval of the `owner` and a `guardian` to be executed. - -Each party alone can trigger the `escape` mode (a.k.a. recovery) on the wallet if the other party is not cooperating or lost. An escape takes 7 days before being active, after which the non-cooperating party can be replaced. The escape expires 7 days after being active. - -The wallet is asymmetric in favor of the `owner` who can override an escape triggered by a `guardian`. - -A triggered escape can always be cancelled with the approval of the `owner` and a `guardian`. - -We assume that the `owner` key is backed up such that the probability of the `owner` key being lost should be close to zero. - -Under this model we can build a simple yet highly secure non-custodial wallet. - -To enable that model to evolve the account can be upgraded. Upgrading the wallet to a new implementation requires the approval of both the `owner` and a `guardian`. At the end of the upgrade, a call to `execute_after_upgrade` is made on the new implementation of the account to perform some maintenance if needed (e.g. migrate state). - -| Action | Owner | Guardian | Comments | -| ----------------------- | ------ | -------- | ----------------------------------------- | -| Multicall | X | X | | -| Change Owner | X | X | | -| Change Guardian | X | X | | -| Change Guardian Backup | X | X | | -| Trigger Escape Guardian | X | | Can override an escape owner in progress | -| Trigger Escape Owner | | X | Fail if escape guardian in progress | -| Escape Guardian | X | | After security period | -| Escape Owner | | X | After security period | -| Cancel Escape | X | X | | -| Upgrade | X | X | | diff --git a/contracts/account/cairo_project.toml b/contracts/account/cairo_project.toml deleted file mode 100644 index e4a06a89..00000000 --- a/contracts/account/cairo_project.toml +++ /dev/null @@ -1,3 +0,0 @@ -[crate_roots] -account = "src" -lib = "../lib/src" \ No newline at end of file diff --git a/contracts/account/src/argent_account.cairo b/contracts/account/src/argent_account.cairo deleted file mode 100644 index abffa5c0..00000000 --- a/contracts/account/src/argent_account.cairo +++ /dev/null @@ -1,843 +0,0 @@ -#[starknet::contract] -mod ArgentAccount { - use array::{ArrayTrait, SpanTrait}; - use box::BoxTrait; - use ecdsa::check_ecdsa_signature; - use hash::{TupleSize4LegacyHash, LegacyHashFelt252}; - use option::{OptionTrait, OptionTraitImpl}; - use serde::Serde; - use traits::Into; - use starknet::{ - ClassHash, class_hash_const, ContractAddress, get_block_timestamp, get_caller_address, - get_execution_info, get_contract_address, get_tx_info, VALIDATED, replace_class_syscall, - account::Call - }; - - use account::{Escape, EscapeStatus, IArgentAccount, IDeprecatedArgentAccount}; - use lib::{ - IAccount, assert_correct_tx_version, assert_no_self_call, assert_caller_is_null, - assert_only_self, execute_multicall, Version, IErc165LibraryDispatcher, - IErc165DispatcherTrait, OutsideExecution, hash_outside_execution_message, - assert_correct_declare_version, ERC165_IERC165_INTERFACE_ID, - ERC165_IERC165_INTERFACE_ID_OLD, ERC165_ACCOUNT_INTERFACE_ID, - ERC165_ACCOUNT_INTERFACE_ID_OLD_1, ERC165_ACCOUNT_INTERFACE_ID_OLD_2, IErc165, - IOutsideExecution, ERC165_OUTSIDE_EXECUTION_INTERFACE_ID, IUpgradeable, - IUpgradeableLibraryDispatcher, IUpgradeableDispatcherTrait, - }; - - const NAME: felt252 = 'ArgentAccount'; - const VERSION_MAJOR: u8 = 0; - const VERSION_MINOR: u8 = 3; - const VERSION_PATCH: u8 = 0; - const VERSION_COMPAT: felt252 = '0.3.0'; - - /// Time it takes for the escape to become ready after being triggered - const ESCAPE_SECURITY_PERIOD: u64 = 604800; // 7 * 24 * 60 * 60; // 7 days - /// The escape will be ready and can be completed for this duration - const ESCAPE_EXPIRY_PERIOD: u64 = 604800; // 7 * 24 * 60 * 60; // 7 days - const ESCAPE_TYPE_GUARDIAN: felt252 = 1; - const ESCAPE_TYPE_OWNER: felt252 = 2; - - const TRIGGER_ESCAPE_GUARDIAN_SELECTOR: felt252 = - 73865429733192804476769961144708816295126306469589518371407068321865763651; // starknet_keccak('trigger_escape_guardian') - const TRIGGER_ESCAPE_OWNER_SELECTOR: felt252 = - 1099763735485822105046709698985960101896351570185083824040512300972207240555; // starknet_keccak('trigger_escape_owner') - const ESCAPE_GUARDIAN_SELECTOR: felt252 = - 1662889347576632967292303062205906116436469425870979472602094601074614456040; // starknet_keccak('escape_guardian') - const ESCAPE_OWNER_SELECTOR: felt252 = - 1621457541430776841129472853859989177600163870003012244140335395142204209277; // starknet_keccak'(escape_owner') - const EXECUTE_AFTER_UPGRADE_SELECTOR: felt252 = - 738349667340360233096752603318170676063569407717437256101137432051386874767; // starknet_keccak('execute_after_upgrade') - const CHANGE_OWNER_SELECTOR: felt252 = - 658036363289841962501247229249022783727527757834043681434485756469236076608; // starknet_keccak('change_owner') - - /// Limit escape attempts by only one party - const MAX_ESCAPE_ATTEMPTS: u32 = 5; - /// Limits fee in escapes - const MAX_ESCAPE_MAX_FEE: u128 = 50000000000000000; // 0.05 ETH - - #[storage] - struct Storage { - _implementation: ClassHash, // This is deprecated and used to migrate cairo 0 accounts only - _signer: felt252, /// Current account owner - _guardian: felt252, /// Current account guardian - _guardian_backup: felt252, /// Current account backup guardian - _escape: Escape, /// The ongoing escape, if any - /// Keeps track of used nonces for outside transactions (`execute_from_outside`) - outside_nonces: LegacyMap, - /// Keeps track of how many escaping tx the guardian has submitted. Used to limit the number of transactions the account will pay for - /// It resets when an escape is completed or canceled - guardian_escape_attempts: u32, - /// Keeps track of how many escaping tx the owner has submitted. Used to limit the number of transactions the account will pay for - /// It resets when an escape is completed or canceled - owner_escape_attempts: u32 - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - AccountCreated: AccountCreated, - TransactionExecuted: TransactionExecuted, - EscapeOwnerTriggered: EscapeOwnerTriggered, - EscapeGuardianTriggered: EscapeGuardianTriggered, - OwnerEscaped: OwnerEscaped, - GuardianEscaped: GuardianEscaped, - EscapeCanceled: EscapeCanceled, - OwnerChanged: OwnerChanged, - GuardianChanged: GuardianChanged, - GuardianBackupChanged: GuardianBackupChanged, - AccountUpgraded: AccountUpgraded, - OwnerAdded: OwnerAdded, - OwnerRemoved: OwnerRemoved, - } - - /// @notice Emitted exactly once when the account is initialized - /// @param account The account address - /// @param owner The owner address - /// @param guardian The guardian address - #[derive(Drop, starknet::Event)] - struct AccountCreated { - #[key] - owner: felt252, - guardian: felt252 - } - - /// @notice Emitted when the account executes a transaction - /// @param hash The transaction hash - /// @param response The data returned by the methods called - #[derive(Drop, starknet::Event)] - struct TransactionExecuted { - #[key] - hash: felt252, - response: Span> - } - - /// @notice Owner escape was triggered by the guardian - /// @param ready_at when the escape can be completed - /// @param new_owner new owner address to be set after the security period - #[derive(Drop, starknet::Event)] - struct EscapeOwnerTriggered { - ready_at: u64, - new_owner: felt252 - } - - /// @notice Guardian escape was triggered by the owner - /// @param ready_at when the escape can be completed - /// @param new_guardian address of the new guardian to be set after the security period. O if the guardian will be removed - #[derive(Drop, starknet::Event)] - struct EscapeGuardianTriggered { - ready_at: u64, - new_guardian: felt252 - } - - /// @notice Owner escape was completed and there is a new account owner - /// @param new_owner new owner address - #[derive(Drop, starknet::Event)] - struct OwnerEscaped { - new_owner: felt252 - } - - /// @notice Guardian escape was completed and there is a new account guardian - /// @param new_guardian address of the new guardian or 0 if it was removed - #[derive(Drop, starknet::Event)] - struct GuardianEscaped { - new_guardian: felt252 - } - - /// An ongoing escape was canceled - #[derive(Drop, starknet::Event)] - struct EscapeCanceled {} - - /// @notice The account owner was changed - /// @param new_owner new owner address - #[derive(Drop, starknet::Event)] - struct OwnerChanged { - new_owner: felt252 - } - - /// @notice The account guardian was changed or removed - /// @param new_guardian address of the new guardian or 0 if it was removed - #[derive(Drop, starknet::Event)] - struct GuardianChanged { - new_guardian: felt252 - } - - /// @notice The account backup guardian was changed or removed - /// @param new_guardian_backup address of the backup guardian or 0 if it was removed - #[derive(Drop, starknet::Event)] - struct GuardianBackupChanged { - new_guardian_backup: felt252 - } - - /// @notice Emitted when the implementation of the account changes - /// @param new_implementation The new implementation - #[derive(Drop, starknet::Event)] - struct AccountUpgraded { - new_implementation: ClassHash - } - - /// This event is part of an account discoverability standard, SNIP not yet created - /// Emitted when an account owner is added, including when the account is created. - /// Should also be emitted with the current owners when upgrading an account from Cairo 0 - #[derive(Drop, starknet::Event)] - struct OwnerAdded { - #[key] - new_owner_guid: felt252, - } - - /// This event is part of an account discoverability standard, SNIP not yet created - /// Emitted when an account owner is removed - #[derive(Drop, starknet::Event)] - struct OwnerRemoved { - #[key] - removed_owner_guid: felt252, - } - - #[constructor] - fn constructor(ref self: ContractState, owner: felt252, guardian: felt252) { - assert(owner != 0, 'argent/null-owner'); - - self._signer.write(owner); - self._guardian.write(guardian); - self._guardian_backup.write(0); - self.emit(AccountCreated { owner, guardian }); - self.emit(OwnerAdded { new_owner_guid: owner }); - } - - #[external(v0)] - impl Account of IAccount { - fn __validate__(ref self: ContractState, calls: Array) -> felt252 { - assert_caller_is_null(); - let tx_info = get_tx_info().unbox(); - self - .assert_valid_calls_and_signature( - calls.span(), - tx_info.transaction_hash, - tx_info.signature, - is_from_outside: false - ); - VALIDATED - } - - fn __execute__(ref self: ContractState, calls: Array) -> Array> { - assert_caller_is_null(); - let tx_info = get_tx_info().unbox(); - assert_correct_tx_version(tx_info.version); - - let retdata = execute_multicall(calls.span()); - - let hash = tx_info.transaction_hash; - let response = retdata.span(); - self.emit(TransactionExecuted { hash, response }); - retdata - } - - fn is_valid_signature( - self: @ContractState, hash: felt252, signature: Array - ) -> felt252 { - if self.is_valid_span_signature(hash, signature.span()) { - VALIDATED - } else { - 0 - } - } - } - - #[external(v0)] - impl ExecuteFromOutsideImpl of IOutsideExecution { - fn execute_from_outside( - ref self: ContractState, outside_execution: OutsideExecution, signature: Array - ) -> Array> { - // Checks - if outside_execution.caller.into() != 'ANY_CALLER' { - assert(get_caller_address() == outside_execution.caller, 'argent/invalid-caller'); - } - - let block_timestamp = get_block_timestamp(); - assert( - outside_execution.execute_after < block_timestamp - && block_timestamp < outside_execution.execute_before, - 'argent/invalid-timestamp' - ); - let nonce = outside_execution.nonce; - assert(!self.outside_nonces.read(nonce), 'argent/duplicated-outside-nonce'); - - let outside_tx_hash = hash_outside_execution_message(@outside_execution); - - let calls = outside_execution.calls; - - self - .assert_valid_calls_and_signature( - calls, outside_tx_hash, signature.span(), is_from_outside: true - ); - - // Effects - self.outside_nonces.write(nonce, true); - - // Interactions - let retdata = execute_multicall(calls); - - self.emit(TransactionExecuted { hash: outside_tx_hash, response: retdata.span() }); - retdata - } - - fn get_outside_execution_message_hash( - self: @ContractState, outside_execution: OutsideExecution - ) -> felt252 { - hash_outside_execution_message(@outside_execution) - } - - fn is_valid_outside_execution_nonce(self: @ContractState, nonce: felt252) -> bool { - !self.outside_nonces.read(nonce) - } - } - - #[external(v0)] - impl UpgradeableImpl of IUpgradeable { - /// Must be called by the account and authorised by the owner and a guardian (if guardian is set). - fn upgrade( - ref self: ContractState, new_implementation: ClassHash, calldata: Array - ) -> Array { - assert_only_self(); - - let supports_interface = IErc165LibraryDispatcher { - class_hash: new_implementation - }.supports_interface(ERC165_ACCOUNT_INTERFACE_ID); - assert(supports_interface, 'argent/invalid-implementation'); - - replace_class_syscall(new_implementation).unwrap_syscall(); - self.emit(AccountUpgraded { new_implementation }); - - IUpgradeableLibraryDispatcher { - class_hash: new_implementation - }.execute_after_upgrade(calldata) - } - - fn execute_after_upgrade(ref self: ContractState, data: Array) -> Array { - assert_only_self(); - - // Check basic invariants - assert(self._signer.read() != 0, 'argent/null-owner'); - if self._guardian.read() == 0 { - assert(self._guardian_backup.read() == 0, 'argent/backup-should-be-null'); - } - - let implementation = self._implementation.read(); - if implementation != class_hash_const::<0>() { - replace_class_syscall(implementation).unwrap_syscall(); - self._implementation.write(class_hash_const::<0>()); - // Technically the owner is not added here, but we emit the event since it wasn't emitted in previous versions - self.emit(OwnerAdded { new_owner_guid: self._signer.read() }); - } - - if data.is_empty() { - return ArrayTrait::new(); - } - - let mut data_span = data.span(); - let calls: Array = Serde::deserialize(ref data_span) - .expect('argent/invalid-calls'); - assert(data_span.is_empty(), 'argent/invalid-calls'); - - assert_no_self_call(calls.span(), get_contract_address()); - - let multicall_return = execute_multicall(calls.span()); - let mut output = ArrayTrait::new(); - multicall_return.serialize(ref output); - output - } - } - - #[external(v0)] - impl ArgentAccountImpl of IArgentAccount { - fn __validate_declare__(self: @ContractState, class_hash: felt252) -> felt252 { - let tx_info = get_tx_info().unbox(); - assert_correct_declare_version(tx_info.version); - self.assert_valid_span_signature(tx_info.transaction_hash, tx_info.signature); - VALIDATED - } - - fn __validate_deploy__( - self: @ContractState, - class_hash: felt252, - contract_address_salt: felt252, - owner: felt252, - guardian: felt252 - ) -> felt252 { - let tx_info = get_tx_info().unbox(); - assert_correct_tx_version(tx_info.version); - self.assert_valid_span_signature(tx_info.transaction_hash, tx_info.signature); - VALIDATED - } - - fn change_owner( - ref self: ContractState, new_owner: felt252, signature_r: felt252, signature_s: felt252 - ) { - assert_only_self(); - self.assert_valid_new_owner(new_owner, signature_r, signature_s); - - self.reset_escape(); - self.reset_escape_attempts(); - - let old_owner = self._signer.read(); - - self._signer.write(new_owner); - self.emit(OwnerChanged { new_owner }); - self.emit(OwnerRemoved { removed_owner_guid: old_owner }); - self.emit(OwnerAdded { new_owner_guid: new_owner }); - } - - fn change_guardian(ref self: ContractState, new_guardian: felt252) { - assert_only_self(); - // There cannot be a guardian_backup when there is no guardian - if new_guardian == 0 { - assert(self._guardian_backup.read() == 0, 'argent/backup-should-be-null'); - } - - self.reset_escape(); - self.reset_escape_attempts(); - - self._guardian.write(new_guardian); - self.emit(GuardianChanged { new_guardian }); - } - - fn change_guardian_backup(ref self: ContractState, new_guardian_backup: felt252) { - assert_only_self(); - self.assert_guardian_set(); - - self.reset_escape(); - self.reset_escape_attempts(); - - self._guardian_backup.write(new_guardian_backup); - self.emit(GuardianBackupChanged { new_guardian_backup }); - } - - fn trigger_escape_owner(ref self: ContractState, new_owner: felt252) { - assert_only_self(); - - // no escape if there is a guardian escape triggered by the owner in progress - let current_escape = self._escape.read(); - if current_escape.escape_type == ESCAPE_TYPE_GUARDIAN { - assert( - get_escape_status(current_escape.ready_at) == EscapeStatus::Expired(()), - 'argent/cannot-override-escape' - ); - } - - self.reset_escape(); - let ready_at = get_block_timestamp() + ESCAPE_SECURITY_PERIOD; - let escape = Escape { ready_at, escape_type: ESCAPE_TYPE_OWNER, new_signer: new_owner }; - self._escape.write(escape); - self.emit(EscapeOwnerTriggered { ready_at, new_owner }); - } - - fn trigger_escape_guardian(ref self: ContractState, new_guardian: felt252) { - assert_only_self(); - - self.reset_escape(); - - let ready_at = get_block_timestamp() + ESCAPE_SECURITY_PERIOD; - let escape = Escape { - ready_at, escape_type: ESCAPE_TYPE_GUARDIAN, new_signer: new_guardian - }; - self._escape.write(escape); - self.emit(EscapeGuardianTriggered { ready_at, new_guardian }); - } - - fn escape_owner(ref self: ContractState) { - assert_only_self(); - - let current_escape = self._escape.read(); - - let current_escape_status = get_escape_status(current_escape.ready_at); - assert(current_escape_status == EscapeStatus::Ready(()), 'argent/invalid-escape'); - - self.reset_escape_attempts(); - - // update owner - let old_owner = self._signer.read(); - self._signer.write(current_escape.new_signer); - self.emit(OwnerEscaped { new_owner: current_escape.new_signer }); - self.emit(OwnerRemoved { removed_owner_guid: old_owner }); - self.emit(OwnerAdded { new_owner_guid: current_escape.new_signer }); - - // clear escape - self._escape.write(Escape { ready_at: 0, escape_type: 0, new_signer: 0 }); - } - - fn escape_guardian(ref self: ContractState) { - assert_only_self(); - - let current_escape = self._escape.read(); - assert( - get_escape_status(current_escape.ready_at) == EscapeStatus::Ready(()), - 'argent/invalid-escape' - ); - - self.reset_escape_attempts(); - - //update guardian - self._guardian.write(current_escape.new_signer); - self.emit(GuardianEscaped { new_guardian: current_escape.new_signer }); - // clear escape - self._escape.write(Escape { ready_at: 0, escape_type: 0, new_signer: 0 }); - } - - fn cancel_escape(ref self: ContractState) { - assert_only_self(); - let current_escape = self._escape.read(); - let current_escape_status = get_escape_status(current_escape.ready_at); - assert(current_escape_status != EscapeStatus::None(()), 'argent/invalid-escape'); - self.reset_escape(); - self.reset_escape_attempts(); - } - - fn get_owner(self: @ContractState) -> felt252 { - self._signer.read() - } - - fn get_guardian(self: @ContractState) -> felt252 { - self._guardian.read() - } - - fn get_guardian_backup(self: @ContractState) -> felt252 { - self._guardian_backup.read() - } - - fn get_escape(self: @ContractState) -> Escape { - self._escape.read() - } - - /// Semantic version of this contract - fn get_version(self: @ContractState) -> Version { - Version { major: VERSION_MAJOR, minor: VERSION_MINOR, patch: VERSION_PATCH } - } - - fn get_name(self: @ContractState) -> felt252 { - NAME - } - - fn get_guardian_escape_attempts(self: @ContractState) -> u32 { - self.guardian_escape_attempts.read() - } - - fn get_owner_escape_attempts(self: @ContractState) -> u32 { - self.owner_escape_attempts.read() - } - - /// Current escape if any, and its status - fn get_escape_and_status(self: @ContractState) -> (Escape, EscapeStatus) { - let current_escape = self._escape.read(); - (current_escape, get_escape_status(current_escape.ready_at)) - } - } - - #[external(v0)] - impl Erc165Impl of IErc165 { - fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { - if interface_id == ERC165_IERC165_INTERFACE_ID { - true - } else if interface_id == ERC165_ACCOUNT_INTERFACE_ID { - true - } else if interface_id == ERC165_OUTSIDE_EXECUTION_INTERFACE_ID { - true - } else if interface_id == ERC165_IERC165_INTERFACE_ID_OLD { - true - } else if interface_id == ERC165_ACCOUNT_INTERFACE_ID_OLD_1 { - true - } else if interface_id == ERC165_ACCOUNT_INTERFACE_ID_OLD_2 { - true - } else { - false - } - } - } - - #[external(v0)] - impl OldArgentAccountImpl< - impl ArgentAccount: IArgentAccount, - impl Account: IAccount, - impl Erc165: IErc165, - > of IDeprecatedArgentAccount { - fn getVersion(self: @ContractState) -> felt252 { - VERSION_COMPAT - } - - fn getName(self: @ContractState) -> felt252 { - ArgentAccount::get_name(self) - } - - fn supportsInterface(self: @ContractState, interface_id: felt252) -> felt252 { - if Erc165::supports_interface(self, interface_id) { - 1 - } else { - 0 - } - } - - fn isValidSignature( - self: @ContractState, hash: felt252, signatures: Array - ) -> felt252 { - assert( - Account::is_valid_signature(self, hash, signatures) == VALIDATED, - 'argent/invalid-signature' - ); - 1 - } - } - - #[generate_trait] - impl Private of PrivateTrait { - fn assert_valid_calls_and_signature( - ref self: ContractState, - calls: Span, - execution_hash: felt252, - signature: Span, - is_from_outside: bool - ) { - let execution_info = get_execution_info().unbox(); - let account_address = execution_info.contract_address; - let tx_info = execution_info.tx_info.unbox(); - assert_correct_tx_version(tx_info.version); - - if calls.len() == 1 { - let call = calls.at(0); - if *call.to == account_address { - let selector = *call.selector; - - if selector == TRIGGER_ESCAPE_OWNER_SELECTOR { - if !is_from_outside { - let current_attempts = self.guardian_escape_attempts.read(); - assert_valid_escape_parameters(current_attempts); - self.guardian_escape_attempts.write(current_attempts + 1); - } - - let mut calldata: Span = call.calldata.span(); - let new_owner: felt252 = Serde::deserialize(ref calldata) - .expect('argent/invalid-calldata'); - assert(calldata.is_empty(), 'argent/invalid-calldata'); - assert(new_owner != 0, 'argent/null-owner'); - self.assert_guardian_set(); - - let is_valid = self.is_valid_guardian_signature(execution_hash, signature); - assert(is_valid, 'argent/invalid-guardian-sig'); - return; // valid - } - if selector == ESCAPE_OWNER_SELECTOR { - if !is_from_outside { - let current_attempts = self.guardian_escape_attempts.read(); - assert_valid_escape_parameters(current_attempts); - self.guardian_escape_attempts.write(current_attempts + 1); - } - - assert(call.calldata.is_empty(), 'argent/invalid-calldata'); - self.assert_guardian_set(); - let current_escape = self._escape.read(); - assert( - current_escape.escape_type == ESCAPE_TYPE_OWNER, 'argent/invalid-escape' - ); - // needed if user started escape in old cairo version and - // upgraded half way through, then tries to finish the escape in new version - assert(current_escape.new_signer != 0, 'argent/null-owner'); - - let is_valid = self.is_valid_guardian_signature(execution_hash, signature); - assert(is_valid, 'argent/invalid-guardian-sig'); - return; // valid - } - if selector == TRIGGER_ESCAPE_GUARDIAN_SELECTOR { - if !is_from_outside { - let current_attempts = self.owner_escape_attempts.read(); - assert_valid_escape_parameters(current_attempts); - self.owner_escape_attempts.write(current_attempts + 1); - } - let mut calldata: Span = call.calldata.span(); - let new_guardian: felt252 = Serde::deserialize(ref calldata) - .expect('argent/invalid-calldata'); - assert(calldata.is_empty(), 'argent/invalid-calldata'); - - if new_guardian == 0 { - assert( - self._guardian_backup.read() == 0, 'argent/backup-should-be-null' - ); - } - self.assert_guardian_set(); - let is_valid = self.is_valid_owner_signature(execution_hash, signature); - assert(is_valid, 'argent/invalid-owner-sig'); - return; // valid - } - if selector == ESCAPE_GUARDIAN_SELECTOR { - if !is_from_outside { - let current_attempts = self.owner_escape_attempts.read(); - assert_valid_escape_parameters(current_attempts); - self.owner_escape_attempts.write(current_attempts + 1); - } - assert(call.calldata.is_empty(), 'argent/invalid-calldata'); - self.assert_guardian_set(); - let current_escape = self._escape.read(); - - assert( - current_escape.escape_type == ESCAPE_TYPE_GUARDIAN, - 'argent/invalid-escape' - ); - - // needed if user started escape in old cairo version and - // upgraded half way through, then tries to finish the escape in new version - if current_escape.new_signer == 0 { - assert( - self._guardian_backup.read() == 0, 'argent/backup-should-be-null' - ); - } - let is_valid = self.is_valid_owner_signature(execution_hash, signature); - assert(is_valid, 'argent/invalid-owner-sig'); - return; // valid - } - assert(selector != EXECUTE_AFTER_UPGRADE_SELECTOR, 'argent/forbidden-call'); - } - } else { - // make sure no call is to the account - assert_no_self_call(calls, account_address); - } - - self.assert_valid_span_signature(execution_hash, signature); - } - - fn is_valid_span_signature( - self: @ContractState, hash: felt252, signatures: Span - ) -> bool { - let (owner_signature, guardian_signature) = split_signatures(signatures); - let is_valid = self.is_valid_owner_signature(hash, owner_signature); - if !is_valid { - return false; - } - if self._guardian.read() == 0 { - guardian_signature.is_empty() - } else { - self.is_valid_guardian_signature(hash, guardian_signature) - } - } - - fn assert_valid_span_signature( - self: @ContractState, hash: felt252, signatures: Span - ) { - let (owner_signature, guardian_signature) = split_signatures(signatures); - let is_valid = self.is_valid_owner_signature(hash, owner_signature); - assert(is_valid, 'argent/invalid-owner-sig'); - - if self._guardian.read() == 0 { - assert(guardian_signature.is_empty(), 'argent/invalid-guardian-sig'); - } else { - assert( - self.is_valid_guardian_signature(hash, guardian_signature), - 'argent/invalid-guardian-sig' - ); - } - } - - fn is_valid_owner_signature( - self: @ContractState, hash: felt252, signature: Span - ) -> bool { - if signature.len() != 2 { - return false; - } - let signature_r = *signature[0]; - let signature_s = *signature[1]; - check_ecdsa_signature(hash, self._signer.read(), signature_r, signature_s) - } - - fn is_valid_guardian_signature( - self: @ContractState, hash: felt252, signature: Span - ) -> bool { - if signature.len() != 2 { - return false; - } - let signature_r = *signature[0]; - let signature_s = *signature[1]; - let is_valid = check_ecdsa_signature( - hash, self._guardian.read(), signature_r, signature_s - ); - if is_valid { - true - } else { - check_ecdsa_signature(hash, self._guardian_backup.read(), signature_r, signature_s) - } - } - - /// The signature is the result of signing the message hash with the new owner private key - /// The message hash is the result of hashing the array: - /// [change_owner selector, chainid, contract address, old_owner] - /// as specified here: https://docs.starknet.io/documentation/architecture_and_concepts/Hashing/hash-functions/#array_hashing - fn assert_valid_new_owner( - self: @ContractState, new_owner: felt252, signature_r: felt252, signature_s: felt252 - ) { - assert(new_owner != 0, 'argent/null-owner'); - let chain_id = get_tx_info().unbox().chain_id; - let mut message_hash = TupleSize4LegacyHash::hash( - 0, (CHANGE_OWNER_SELECTOR, chain_id, get_contract_address(), self._signer.read()) - ); - // We now need to hash message_hash with the size of the array: (change_owner selector, chainid, contract address, old_owner) - // https://github.com/starkware-libs/cairo-lang/blob/b614d1867c64f3fb2cf4a4879348cfcf87c3a5a7/src/starkware/cairo/common/hash_state.py#L6 - message_hash = LegacyHashFelt252::hash(message_hash, 4); - let is_valid = check_ecdsa_signature(message_hash, new_owner, signature_r, signature_s); - assert(is_valid, 'argent/invalid-owner-sig'); - } - - #[inline(always)] - fn reset_escape(ref self: ContractState) { - let current_escape_status = get_escape_status(self._escape.read().ready_at); - if current_escape_status == EscapeStatus::None(()) { - return; - } - self._escape.write(Escape { ready_at: 0, escape_type: 0, new_signer: 0 }); - if current_escape_status != EscapeStatus::Expired(()) { - self.emit(EscapeCanceled {}); - } - } - - #[inline(always)] - fn assert_guardian_set(self: @ContractState) { - assert(self._guardian.read() != 0, 'argent/guardian-required'); - } - - #[inline(always)] - fn reset_escape_attempts(ref self: ContractState) { - self.owner_escape_attempts.write(0); - self.guardian_escape_attempts.write(0); - } - } - - fn assert_valid_escape_parameters(attempts: u32) { - let tx_info = get_tx_info().unbox(); - assert(tx_info.max_fee <= MAX_ESCAPE_MAX_FEE, 'argent/max-fee-too-high'); - assert(attempts < MAX_ESCAPE_ATTEMPTS, 'argent/max-escape-attempts'); - } - - fn split_signatures(full_signature: Span) -> (Span, Span) { - if full_signature.len() == 2 { - return (full_signature, ArrayTrait::new().span()); - } - assert(full_signature.len() == 4, 'argent/invalid-signature-length'); - let mut owner_signature = ArrayTrait::new(); - owner_signature.append(*full_signature[0]); - owner_signature.append(*full_signature[1]); - let mut guardian_signature = ArrayTrait::new(); - guardian_signature.append(*full_signature[2]); - guardian_signature.append(*full_signature[3]); - (owner_signature.span(), guardian_signature.span()) - } - - fn get_escape_status(escape_ready_at: u64) -> EscapeStatus { - if escape_ready_at == 0 { - return EscapeStatus::None(()); - } - - let block_timestamp = get_block_timestamp(); - if block_timestamp < escape_ready_at { - return EscapeStatus::NotReady(()); - } - if escape_ready_at + ESCAPE_EXPIRY_PERIOD <= block_timestamp { - return EscapeStatus::Expired(()); - } - - EscapeStatus::Ready(()) - } -} diff --git a/contracts/account/src/escape.cairo b/contracts/account/src/escape.cairo deleted file mode 100644 index f5540fb4..00000000 --- a/contracts/account/src/escape.cairo +++ /dev/null @@ -1,21 +0,0 @@ -#[derive(Drop, Copy, Serde, PartialEq)] -enum EscapeStatus { - /// No escape triggered, or it was canceled - None: (), - /// Escape was triggered and it's waiting for the `escapeSecurityPeriod` - NotReady: (), - /// The security period has elapsed and the escape is ready to be completed - Ready: (), - /// No confirmation happened for `escapeExpiryPeriod` since it became `Ready`. The escape cannot be completed now, only canceled - Expired: () -} - -#[derive(Drop, Copy, Serde, storage_access::StorageAccess)] -struct Escape { - // timestamp for activation of escape mode, 0 otherwise - ready_at: u64, - // None, Guardian, Owner - escape_type: felt252, - // new owner or new guardian address - new_signer: felt252, -} diff --git a/contracts/account/src/interface.cairo b/contracts/account/src/interface.cairo deleted file mode 100644 index d3a048c7..00000000 --- a/contracts/account/src/interface.cairo +++ /dev/null @@ -1,96 +0,0 @@ -use lib::Version; -use account::{Escape, EscapeStatus}; - -#[starknet::interface] -trait IArgentAccount { - fn __validate_declare__(self: @TContractState, class_hash: felt252) -> felt252; - fn __validate_deploy__( - self: @TContractState, - class_hash: felt252, - contract_address_salt: felt252, - owner: felt252, - guardian: felt252 - ) -> felt252; - // External - - /// @notice Changes the owner - /// Must be called by the account and authorised by the owner and a guardian (if guardian is set). - /// @param new_owner New owner address - /// @param signature_r Signature R from the new owner - /// @param signature_S Signature S from the new owner - /// Signature is required to prevent changing to an address which is not in control of the user - /// Signature is the Signed Message of this hash: - /// hash = pedersen(0, (change_owner selector, chainid, contract address, old_owner)) - fn change_owner( - ref self: TContractState, new_owner: felt252, signature_r: felt252, signature_s: felt252 - ); - - /// @notice Changes the guardian - /// Must be called by the account and authorised by the owner and a guardian (if guardian is set). - /// @param new_guardian The address of the new guardian, or 0 to disable the guardian - /// @dev can only be set to 0 if there is no guardian backup set - fn change_guardian(ref self: TContractState, new_guardian: felt252); - - /// @notice Changes the backup guardian - /// Must be called by the account and authorised by the owner and a guardian (if guardian is set). - /// @param new_guardian_backup The address of the new backup guardian, or 0 to disable the backup guardian - fn change_guardian_backup(ref self: TContractState, new_guardian_backup: felt252); - - /// @notice Triggers the escape of the owner when it is lost or compromised. - /// Must be called by the account and authorised by just a guardian. - /// Cannot override an ongoing escape of the guardian. - /// @param new_owner The new account owner if the escape completes - /// @dev This method assumes that there is a guardian, and that `_newOwner` is not 0. - /// This must be guaranteed before calling this method, usually when validating the transaction. - fn trigger_escape_owner(ref self: TContractState, new_owner: felt252); - - /// @notice Triggers the escape of the guardian when it is lost or compromised. - /// Must be called by the account and authorised by the owner alone. - /// Can override an ongoing escape of the owner. - /// @param new_guardian The new account guardian if the escape completes - /// @dev This method assumes that there is a guardian, and that `new_guardian` can only be 0 - /// if there is no guardian backup. - /// This must be guaranteed before calling this method, usually when validating the transaction - fn trigger_escape_guardian(ref self: TContractState, new_guardian: felt252); - - /// @notice Completes the escape and changes the owner after the security period - /// Must be called by the account and authorised by just a guardian - /// @dev This method assumes that there is a guardian, and that the there is an escape for the owner. - /// This must be guaranteed before calling this method, usually when validating the transaction. - fn escape_owner(ref self: TContractState); - - /// @notice Completes the escape and changes the guardian after the security period - /// Must be called by the account and authorised by just the owner - /// @dev This method assumes that there is a guardian, and that the there is an escape for the guardian. - /// This must be guaranteed before calling this method. Usually when validating the transaction. - fn escape_guardian(ref self: TContractState); - - /// @notice Cancels an ongoing escape if any. - /// Must be called by the account and authorised by the owner and a guardian (if guardian is set). - fn cancel_escape(ref self: TContractState); - - // Views - fn get_owner(self: @TContractState) -> felt252; - fn get_guardian(self: @TContractState) -> felt252; - fn get_guardian_backup(self: @TContractState) -> felt252; - fn get_escape(self: @TContractState) -> Escape; - fn get_version(self: @TContractState) -> Version; - fn get_name(self: @TContractState) -> felt252; - fn get_guardian_escape_attempts(self: @TContractState) -> u32; - fn get_owner_escape_attempts(self: @TContractState) -> u32; - - /// Current escape if any, and its status - fn get_escape_and_status(self: @TContractState) -> (Escape, EscapeStatus); -} - -/// Deprecated methods for compatibility reasons -#[starknet::interface] -trait IDeprecatedArgentAccount { - fn getVersion(self: @TContractState) -> felt252; - fn getName(self: @TContractState) -> felt252; - fn supportsInterface(self: @TContractState, interface_id: felt252) -> felt252; - /// For compatibility reasons this method returns 1 when the signature is valid, and panics otherwise - fn isValidSignature( - self: @TContractState, hash: felt252, signatures: Array - ) -> felt252; -} diff --git a/contracts/account/src/lib.cairo b/contracts/account/src/lib.cairo deleted file mode 100644 index d65a0cec..00000000 --- a/contracts/account/src/lib.cairo +++ /dev/null @@ -1,12 +0,0 @@ -mod interface; -use interface::IArgentAccount; -use interface::IDeprecatedArgentAccount; - -mod argent_account; -use argent_account::ArgentAccount; - -mod escape; -use escape::{Escape, EscapeStatus}; - -#[cfg(test)] -mod tests; diff --git a/contracts/account/src/tests.cairo b/contracts/account/src/tests.cairo deleted file mode 100644 index 121a9bf0..00000000 --- a/contracts/account/src/tests.cairo +++ /dev/null @@ -1,94 +0,0 @@ -mod test_argent_account; -mod test_argent_account_signatures; - -use array::ArrayTrait; -use option::OptionTrait; -use result::ResultTrait; -use traits::TryInto; - -use starknet::{ - contract_address_const, Felt252TryIntoClassHash, deploy_syscall, account::Call, - testing::set_contract_address -}; - -use account::{Escape, EscapeStatus, ArgentAccount}; -use lib::Version; - -#[starknet::interface] -trait ITestArgentAccount { - // IAccount - fn __validate_declare__(self: @TContractState, class_hash: felt252) -> felt252; - 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; - - // IArgentAccount - fn __validate_deploy__( - self: @TContractState, - class_hash: felt252, - contract_address_salt: felt252, - owner: felt252, - guardian: felt252 - ) -> felt252; - // External - fn change_owner( - ref self: TContractState, new_owner: felt252, signature_r: felt252, signature_s: felt252 - ); - fn change_guardian(ref self: TContractState, new_guardian: felt252); - fn change_guardian_backup(ref self: TContractState, new_guardian_backup: felt252); - fn trigger_escape_owner(ref self: TContractState, new_owner: felt252); - fn trigger_escape_guardian(ref self: TContractState, new_guardian: felt252); - fn escape_owner(ref self: TContractState); - fn escape_guardian(ref self: TContractState); - fn cancel_escape(ref self: TContractState); - // Views - fn get_owner(self: @TContractState) -> felt252; - fn get_guardian(self: @TContractState) -> felt252; - fn get_guardian_backup(self: @TContractState) -> felt252; - fn get_escape(self: @TContractState) -> Escape; - fn get_version(self: @TContractState) -> Version; - fn get_name(self: @TContractState) -> felt252; - fn get_guardian_escape_attempts(self: @TContractState) -> u32; - fn get_owner_escape_attempts(self: @TContractState) -> u32; - fn get_escape_and_status(self: @TContractState) -> (Escape, EscapeStatus); - - // IErc165 - fn supports_interface(self: @TContractState, interface_id: felt252) -> bool; - - // IDeprecatedArgentAccount - fn getVersion(self: @TContractState) -> felt252; - fn getName(self: @TContractState) -> felt252; - fn supportsInterface(self: @TContractState, interface_id: felt252) -> felt252; - fn isValidSignature( - self: @TContractState, hash: felt252, signatures: Array - ) -> felt252; -} - -const owner_pubkey: felt252 = 0x1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca; -const guardian_pubkey: felt252 = 0x759ca09377679ecd535a81e83039658bf40959283187c654c5416f439403cf5; -const wrong_owner_pubkey: felt252 = - 0x743829e0a179f8afe223fc8112dfc8d024ab6b235fd42283c4f5970259ce7b7; -const wrong_guardian_pubkey: felt252 = - 0x6eeee2b0c71d681692559735e08a2c3ba04e7347c0c18d4d49b83bb89771591; - -fn initialize_account() -> ITestArgentAccountDispatcher { - initialize_account_with(owner_pubkey, guardian_pubkey) -} - -fn initialize_account_without_guardian() -> ITestArgentAccountDispatcher { - initialize_account_with(owner_pubkey, 0) -} - -fn initialize_account_with(owner: felt252, guardian: felt252) -> ITestArgentAccountDispatcher { - let mut calldata = ArrayTrait::new(); - calldata.append(owner); - calldata.append(guardian); - let class_hash = ArgentAccount::TEST_CLASS_HASH.try_into().unwrap(); - let (contract_address, _) = deploy_syscall(class_hash, 0, calldata.span(), true).unwrap(); - - // This will set the caller for subsequent calls (avoid 'argent/only-self') - set_contract_address(contract_address_const::<1>()); - ITestArgentAccountDispatcher { contract_address } -} diff --git a/contracts/account/src/tests/test_argent_account.cairo b/contracts/account/src/tests/test_argent_account.cairo deleted file mode 100644 index 87e2ca07..00000000 --- a/contracts/account/src/tests/test_argent_account.cairo +++ /dev/null @@ -1,289 +0,0 @@ -use array::ArrayTrait; -use option::OptionTrait; -use result::ResultTrait; -use traits::TryInto; -use zeroable::Zeroable; - -use starknet::{ - contract_address_const, deploy_syscall, testing::{set_version, set_contract_address} -}; - -use account::{ - ArgentAccount, - tests::{ - ITestArgentAccountDispatcherTrait, owner_pubkey, wrong_owner_pubkey, - initialize_account_with, initialize_account, initialize_account_without_guardian, - } -}; - -const new_owner_pubkey: felt252 = 0xa7da05a4d664859ccd6e567b935cdfbfe3018c7771cb980892ef38878ae9bc; -const new_owner_r: felt252 = 0x3e242301b001c97a5be2b3a165fae7abf72027cb8b1ca4713580d52d9ff008e; -const new_owner_s: felt252 = 0x758f108a8beed1dec98d054740287611882d7633bb1b94c73728aaff777bf6c; - -const wrong_owner_r: felt252 = 0x4be5db0599a2e5943f207da3f9bf2dd091acf055b71a1643e9c35fcd7e2c0df; -const wrong_owner_s: felt252 = 0x2e44d5bad55a0d692e02529e7060f352fde85fae8d5946f28c34a10a29bc83b; - -#[test] -#[available_gas(2000000)] -fn initialize() { - let account = initialize_account_with(1, 2); - assert(account.get_owner() == 1, 'value should be 1'); - assert(account.get_guardian() == 2, 'value should be 2'); - assert(account.get_guardian_backup() == 0, 'value should be 0'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('argent/invalid-tx-version', 'ENTRYPOINT_FAILED'))] -fn check_transaction_version_on_execute() { - let account = initialize_account(); - set_contract_address(contract_address_const::<0>()); - set_version(32); - account.__execute__(ArrayTrait::new()); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('argent/invalid-tx-version', 'ENTRYPOINT_FAILED'))] -fn check_transaction_version_on_validate() { - let account = initialize_account(); - set_contract_address(contract_address_const::<0>()); - set_version(32); - account.__validate__(ArrayTrait::new()); -} - -#[test] -#[available_gas(2000000)] -fn initialize_with_null_owner() { - let mut calldata = ArrayTrait::new(); - calldata.append(0); - calldata.append(12); - let class_hash = ArgentAccount::TEST_CLASS_HASH.try_into().unwrap(); - let mut err = deploy_syscall(class_hash, 0, calldata.span(), true).unwrap_err(); - assert(@err.pop_front().unwrap() == @'argent/null-owner', 'Should be argent/null-owner'); -} -#[test] -#[available_gas(2000000)] -fn initialized_no_guardian_no_backup() { - let account = initialize_account_with(1, 0); - assert(account.get_owner() == 1, 'value should be 1'); - assert(account.get_guardian() == 0, 'guardian should be zero'); - assert(account.get_guardian_backup() == 0, 'guardian backup should be zero'); -} - -#[test] -#[available_gas(2000000)] -fn erc165_unsupported_interfaces() { - let account = initialize_account(); - assert(!account.supports_interface(0), 'Should not support 0'); - assert(!account.supports_interface(0xffffffff), 'Should not support 0xffffffff'); -} - -#[test] -#[available_gas(2000000)] -fn erc165_supported_interfaces() { - let account = initialize_account(); - assert( - account - .supports_interface(0x3f918d17e5ee77373b56385708f855659a07f75997f365cf87748628532a055), - 'IERC165' - ); - assert(account.supports_interface(0x01ffc9a7), 'IERC165_OLD'); - assert( - account - .supports_interface(0x2ceccef7f994940b3962a6c67e0ba4fcd37df7d131417c604f91e03caecc1cd), - 'IACCOUNT' - ); - assert(account.supports_interface(0xa66bd575), 'IACCOUNT_OLD_1'); - assert(account.supports_interface(0x3943f10f), 'IACCOUNT_OLD_2'); - - assert( - account - .supports_interface(0x68cfd18b92d1907b8ba3cc324900277f5a3622099431ea85dd8089255e4181), - 'OUTSIDE_EXECUTION' - ); -} - -#[test] -#[available_gas(2000000)] -fn change_owner() { - let account = initialize_account(); - assert(account.get_owner() == owner_pubkey, 'value should be 1'); - - set_contract_address(contract_address_const::<1>()); - account.change_owner(new_owner_pubkey, new_owner_r, new_owner_s); - assert(account.get_owner() == new_owner_pubkey, 'value should be new owner pub'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('argent/only-self', 'ENTRYPOINT_FAILED'))] -fn change_owner_only_self() { - let account = initialize_account(); - set_contract_address(contract_address_const::<42>()); - account.change_owner(new_owner_pubkey, new_owner_r, new_owner_s); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('argent/null-owner', 'ENTRYPOINT_FAILED'))] -fn change_owner_to_zero() { - let account = initialize_account(); - account.change_owner(0, new_owner_r, new_owner_s); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('argent/invalid-owner-sig', 'ENTRYPOINT_FAILED'))] -fn change_owner_invalid_message() { - let account = initialize_account(); - account.change_owner(new_owner_pubkey, wrong_owner_r, wrong_owner_s); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('argent/invalid-owner-sig', 'ENTRYPOINT_FAILED'))] -fn change_owner_wrong_pub_key() { - let account = initialize_account(); - account.change_owner(wrong_owner_pubkey, new_owner_r, new_owner_s); -} - -#[test] -#[available_gas(2000000)] -fn change_guardian() { - let account = initialize_account(); - account.change_guardian(22); - assert(account.get_guardian() == 22, 'value should be 22'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('argent/only-self', 'ENTRYPOINT_FAILED'))] -fn change_guardian_only_self() { - let account = initialize_account(); - set_contract_address(contract_address_const::<42>()); - account.change_guardian(22); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('argent/backup-should-be-null', 'ENTRYPOINT_FAILED'))] -fn change_guardian_to_zero() { - let account = initialize_account(); - account.change_guardian_backup(42); - account.change_guardian(0); -} - -#[test] -#[available_gas(2000000)] -fn change_guardian_to_zero_without_guardian_backup() { - let account = initialize_account(); - account.change_guardian(0); - assert(account.get_guardian().is_zero(), 'value should be 0'); -} - -#[test] -#[available_gas(2000000)] -fn change_guardian_backup() { - let account = initialize_account(); - account.change_guardian_backup(33); - assert(account.get_guardian_backup() == 33, 'value should be 33'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('argent/only-self', 'ENTRYPOINT_FAILED'))] -fn change_guardian_backup_only_self() { - let account = initialize_account(); - set_contract_address(contract_address_const::<42>()); - account.change_guardian_backup(22); -} - -#[test] -#[available_gas(2000000)] -fn change_guardian_backup_to_zero() { - let account = initialize_account(); - account.change_guardian_backup(0); - assert(account.get_guardian_backup().is_zero(), 'value should be 0'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('argent/guardian-required', 'ENTRYPOINT_FAILED'))] -fn change_invalid_guardian_backup() { - let account = initialize_account_without_guardian(); - account.change_guardian_backup(33); -} - -#[test] -#[available_gas(2000000)] -fn get_version() { - let version = initialize_account().get_version(); - assert(version.major == 0, 'Version major = 0'); - assert(version.minor == 3, 'Version minor = 3'); - assert(version.patch == 0, 'Version patch = 0'); -} - -#[test] -#[available_gas(2000000)] -fn getVersion() { - assert(initialize_account().getVersion() == '0.3.0', 'Version should be 0.3.0'); -} - -#[test] -#[available_gas(2000000)] -fn get_name() { - assert(initialize_account().get_name() == 'ArgentAccount', 'Name should be ArgentAccount'); -} - -#[test] -#[available_gas(2000000)] -fn getName() { - assert(initialize_account().getName() == 'ArgentAccount', 'Name should be ArgentAccount'); -} -#[test] -#[available_gas(2000000)] -fn unsuported_supportsInterface() { - let account = initialize_account(); - assert(account.supportsInterface(0) == 0, 'value should be false'); - assert(account.supportsInterface(0xffffffff) == 0, 'Should not support 0xffffffff'); -} - -#[test] -#[available_gas(2000000)] -fn supportsInterface() { - let account = initialize_account(); - assert(account.supportsInterface(0x01ffc9a7) == 1, 'ERC165_IERC165_INTERFACE_ID'); - assert(account.supportsInterface(0xa66bd575) == 1, 'ERC165_ACCOUNT_INTERFACE_ID'); - assert(account.supportsInterface(0x3943f10f) == 1, 'ERC165_OLD_ACCOUNT_INTERFACE_ID'); -} - -#[test] -#[available_gas(2000000)] -fn test_selectors() { - // Double check to ensure it IS and STAYS correct - assert( - ArgentAccount::TRIGGER_ESCAPE_GUARDIAN_SELECTOR == 73865429733192804476769961144708816295126306469589518371407068321865763651, - 'trigger_escape_guardian' - ); - assert( - ArgentAccount::TRIGGER_ESCAPE_OWNER_SELECTOR == 1099763735485822105046709698985960101896351570185083824040512300972207240555, - 'trigger_escape_owner' - ); - assert( - ArgentAccount::ESCAPE_GUARDIAN_SELECTOR == 1662889347576632967292303062205906116436469425870979472602094601074614456040, - 'escape_guardian' - ); - assert( - ArgentAccount::ESCAPE_OWNER_SELECTOR == 1621457541430776841129472853859989177600163870003012244140335395142204209277, - 'escape_owner' - ); - assert( - ArgentAccount::EXECUTE_AFTER_UPGRADE_SELECTOR == 738349667340360233096752603318170676063569407717437256101137432051386874767, - 'execute_after_upgrade' - ); - assert( - ArgentAccount::CHANGE_OWNER_SELECTOR == 658036363289841962501247229249022783727527757834043681434485756469236076608, - 'change_owner' - ); -} diff --git a/contracts/account/src/tests/test_argent_account_signatures.cairo b/contracts/account/src/tests/test_argent_account_signatures.cairo deleted file mode 100644 index 80a87c58..00000000 --- a/contracts/account/src/tests/test_argent_account_signatures.cairo +++ /dev/null @@ -1,229 +0,0 @@ -use array::{ArrayTrait, SpanTrait}; - -use account::{ - ArgentAccount, - tests::{ - ITestArgentAccountDispatcher, ITestArgentAccountDispatcherTrait, owner_pubkey, - initialize_account, initialize_account_without_guardian, initialize_account_with - } -}; -use starknet::VALIDATED; - -const message_hash: felt252 = 0x2d6479c0758efbb5aa07d35ed5454d728637fceab7ba544d3ea95403a5630a8; - -const owner_r: felt252 = 0x6ff7b413a8457ef90f326b5280600a4473fef49b5b1dcdfcd7f42ca7aa59c69; -const owner_s: felt252 = 0x23a9747ed71abc5cb956c0df44ee8638b65b3e9407deade65de62247b8fd77; - -const guardian_r: felt252 = 0x1734f5510c8b862984461d2221411d12a706140bae629feac0aad35f4d91a19; -const guardian_s: felt252 = 0x75c904c1969e5b2bf2e9fedb32d6180f06288d81a6a2164d876ea4be2ae7520; - -const guardian_backup_pubkey: felt252 = - 0x411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20; -const guardian_backup_r: felt252 = - 0x1e03a158a4142532f903caa32697a74fcf5c05b762bb866cec28670d0a53f9a; -const guardian_backup_s: felt252 = - 0x74be76fe620a42899bc34afce7b31a058408b23c250805054fca4de4e0121ca; - -const wrong_owner_r: felt252 = 0x4be5db0599a2e5943f207da3f9bf2dd091acf055b71a1643e9c35fcd7e2c0df; -const wrong_owner_s: felt252 = 0x2e44d5bad55a0d692e02529e7060f352fde85fae8d5946f28c34a10a29bc83b; - -const wrong_guardian_r: felt252 = 0x5e5375b33d31fea164fb58c97ae0f9354863af5274f47a261b268b072285539; -const wrong_guardian_s: felt252 = 0x649c2cc2696a1f257534f03d913f869daae675467ed2f994b94059341e68929; - -fn double_signature(r1: felt252, s1: felt252, r2: felt252, s2: felt252) -> Array { - let mut signatures = ArrayTrait::new(); - signatures.append(r1); - signatures.append(s1); - signatures.append(r2); - signatures.append(s2); - signatures -} - -fn single_signature(r: felt252, s: felt252) -> Array { - let mut signatures = ArrayTrait::new(); - signatures.append(r); - signatures.append(s); - signatures -} - -#[test] -#[available_gas(2000000)] -fn valid_no_guardian() { - let signatures = single_signature(owner_r, owner_s); - assert( - initialize_account_without_guardian() - .is_valid_signature(message_hash, signatures) == VALIDATED, - 'invalid signature' - ); -} - -#[test] -#[available_gas(2000000)] -fn valid_with_guardian() { - let signatures = double_signature(owner_r, owner_s, guardian_r, guardian_s); - assert( - initialize_account().is_valid_signature(message_hash, signatures) == VALIDATED, - 'invalid signature' - ); -} - -#[test] -#[available_gas(2000000)] -fn valid_with_guardian_backup() { - let account = initialize_account_with(owner_pubkey, 1); - account.change_guardian_backup(guardian_backup_pubkey); - let signatures = double_signature(owner_r, owner_s, guardian_backup_r, guardian_backup_s); - assert(account.is_valid_signature(message_hash, signatures) == VALIDATED, 'invalid signature'); -} - -#[test] -#[available_gas(2000000)] -fn invalid_hash_1() { - let account = initialize_account_without_guardian(); - let signatures = single_signature(owner_r, owner_s); - assert(account.is_valid_signature(0, signatures) == 0, 'invalid signature'); -} - -#[test] -#[available_gas(2000000)] -fn invalid_hash_2() { - let account = initialize_account_without_guardian(); - let signatures = single_signature(owner_r, owner_s); - assert(account.is_valid_signature(123, signatures) == 0, 'invalid signature'); -} - -#[test] -#[available_gas(2000000)] -fn invalid_owner_without_guardian() { - let account = initialize_account_without_guardian(); - let signatures = single_signature(0, 0); - assert(account.is_valid_signature(message_hash, signatures) == 0, 'invalid signature 1'); - let signatures = single_signature(wrong_owner_r, wrong_owner_s); - assert(account.is_valid_signature(message_hash, signatures) == 0, 'invalid signature 2'); - let signatures = single_signature(guardian_r, guardian_s); - assert(account.is_valid_signature(message_hash, signatures) == 0, 'invalid signature 3'); -} - -#[test] -#[available_gas(2000000)] -fn invalid_owner_with_guardian() { - let account = initialize_account(); - let signatures = double_signature(0, 0, guardian_r, guardian_s); - assert(account.is_valid_signature(message_hash, signatures) == 0, 'invalid signature 1'); - let signatures = double_signature(42, 99, guardian_r, guardian_s); - assert(account.is_valid_signature(message_hash, signatures) == 0, 'invalid signature 2'); - let signatures = double_signature(wrong_owner_r, wrong_owner_s, guardian_r, guardian_s); - assert(account.is_valid_signature(message_hash, signatures) == 0, 'invalid signature 3'); - let signatures = double_signature(guardian_r, guardian_s, guardian_r, guardian_s); - assert(account.is_valid_signature(message_hash, signatures) == 0, 'invalid signature 4'); -} - -#[test] -#[available_gas(2000000)] -fn valid_owner_with_invalid_guardian() { - let account = initialize_account(); - let signatures = double_signature(owner_r, owner_s, 0, 0); - assert(account.is_valid_signature(message_hash, signatures) == 0, 'invalid signature 1'); - let signatures = double_signature(owner_r, owner_s, 42, 69); - assert(account.is_valid_signature(message_hash, signatures) == 0, 'invalid signature 2'); - let signatures = double_signature(owner_r, owner_s, wrong_guardian_r, wrong_guardian_s); - assert(account.is_valid_signature(message_hash, signatures) == 0, 'invalid signature 3'); - let signatures = double_signature(owner_r, owner_s, owner_r, owner_s); - assert(account.is_valid_signature(message_hash, signatures) == 0, 'invalid signature 4'); -} - -#[test] -#[available_gas(2000000)] -fn invalid_owner_with_invalid_guardian() { - let account = initialize_account(); - let signatures = double_signature(0, 0, 0, 0); - assert(account.is_valid_signature(message_hash, signatures) == 0, 'invalid signature 1'); - let signatures = double_signature(42, 99, 534, 123); - assert(account.is_valid_signature(message_hash, signatures) == 0, 'invalid signature 2'); - let signatures = double_signature(wrong_owner_r, wrong_owner_s, 0, 0); - assert(account.is_valid_signature(message_hash, signatures) == 0, 'invalid signature 3'); - let signatures = double_signature(0, 0, wrong_guardian_r, wrong_guardian_s); - assert(account.is_valid_signature(message_hash, signatures) == 0, 'invalid signature 4'); - let signatures = double_signature( - wrong_owner_r, wrong_owner_s, wrong_guardian_r, wrong_guardian_s - ); - assert(account.is_valid_signature(message_hash, signatures) == 0, 'invalid signature 5'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('argent/invalid-signature-length', 'ENTRYPOINT_FAILED'))] -fn invalid_empty_signature_without_guardian() { - let account = initialize_account_without_guardian(); - let signatures = ArrayTrait::new(); - account.is_valid_signature(message_hash, signatures); -} - -#[test] -#[available_gas(2000000)] -fn invalid_signature_length_without_guardian() { - let account = initialize_account_without_guardian(); - let signatures = double_signature(owner_r, owner_s, guardian_r, guardian_s); - assert(account.is_valid_signature(message_hash, signatures) == 0, 'invalid signature'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('argent/invalid-signature-length', 'ENTRYPOINT_FAILED'))] -fn invalid_empty_signature_with_guardian() { - let account = initialize_account(); - let signatures = ArrayTrait::new(); - account.is_valid_signature(message_hash, signatures); -} - -#[test] -#[available_gas(2000000)] -fn invalid_signature_length_with_guardian() { - let account = initialize_account(); - let signatures = single_signature(owner_r, owner_s); - assert(account.is_valid_signature(message_hash, signatures) == 0, 'invalid signature'); - let signatures = single_signature(guardian_r, guardian_s); - assert(account.is_valid_signature(message_hash, signatures) == 0, 'invalid signature'); -} - -#[test] -#[available_gas(2000000)] -fn split_signatures() { - let mut arr = ArrayTrait::new(); - arr.append(21); - arr.append(42); - let (full, empty) = ArgentAccount::split_signatures(arr.span()); - assert(full.len() == 2, 'Len should be 2'); - assert(empty.len() == 0, 'Len should be 0'); - assert(*full[0] == 21, 'Idx 0 should be 21'); - assert(*full[1] == 42, 'Idx 1 should be 42'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('argent/invalid-signature-length', ))] -fn split_signatures_wrong_lenght() { - let mut arr = ArrayTrait::new(); - arr.append(21); - arr.append(42); - arr.append(45); - ArgentAccount::split_signatures(arr.span()); -} - -#[test] -#[available_gas(2000000)] -fn split_signatures_length_4() { - let mut arr = ArrayTrait::new(); - arr.append(21); - arr.append(42); - arr.append(23); - arr.append(69); - let (owner, guardian) = ArgentAccount::split_signatures(arr.span()); - assert(owner.len() == 2, 'Len owner should be 2'); - assert(guardian.len() == 2, 'Len guardian should be 0'); - assert(*owner[0] == 21, 'Idx 0 should be 21'); - assert(*owner[1] == 42, 'Idx 1 should be 42'); - assert(*guardian[0] == 23, 'Idx 0 should be 23'); - assert(*guardian[1] == 69, 'Idx 1 should be 69'); -} - diff --git a/contracts/cairo_project.toml b/contracts/cairo_project.toml deleted file mode 100644 index 9cce3c80..00000000 --- a/contracts/cairo_project.toml +++ /dev/null @@ -1,5 +0,0 @@ -[crate_roots] -account = "account/src" -lib = "lib/src" -multicall = "multicall/src" -multisig = "multisig/src" \ No newline at end of file diff --git a/contracts/lib/src/lib.cairo b/contracts/lib/src/lib.cairo deleted file mode 100644 index 3bea9210..00000000 --- a/contracts/lib/src/lib.cairo +++ /dev/null @@ -1,41 +0,0 @@ -mod asserts; -use asserts::{ - assert_only_self, assert_no_self_call, assert_caller_is_null, assert_correct_tx_version, - assert_correct_declare_version -}; - -mod account; -use account::{ - IAccount, ERC165_ACCOUNT_INTERFACE_ID, ERC165_ACCOUNT_INTERFACE_ID_OLD_1, - ERC165_ACCOUNT_INTERFACE_ID_OLD_2 -}; - -mod outside_execution; -use outside_execution::{ - OutsideExecution, hash_outside_execution_message, IOutsideExecution, - ERC165_OUTSIDE_EXECUTION_INTERFACE_ID -}; - -mod test_dapp; -use test_dapp::TestDapp; - -mod array_ext; -use array_ext::ArrayExtTrait; - -mod calls; -use calls::execute_multicall; - -mod version; -use version::Version; - -mod erc165; -use erc165::{ - IErc165, IErc165LibraryDispatcher, IErc165DispatcherTrait, ERC165_IERC165_INTERFACE_ID, - ERC165_IERC165_INTERFACE_ID_OLD -}; - -mod upgrade; -use upgrade::{IUpgradeable, IUpgradeableLibraryDispatcher, IUpgradeableDispatcherTrait}; - -#[cfg(test)] -mod tests; diff --git a/contracts/lib/src/test_dapp.cairo b/contracts/lib/src/test_dapp.cairo deleted file mode 100644 index e0b83ba8..00000000 --- a/contracts/lib/src/test_dapp.cairo +++ /dev/null @@ -1,53 +0,0 @@ -use starknet::ContractAddress; - -#[starknet::interface] -trait ITestDapp { - fn set_number(ref self: TContractState, number: felt252); - fn set_number_double(ref self: TContractState, number: felt252); - fn set_number_times3(ref self: TContractState, number: felt252); - fn increase_number(ref self: TContractState, number: felt252) -> felt252; - fn throw_error(ref self: TContractState, number: felt252); - - fn get_number(self: @TContractState, user: ContractAddress) -> felt252; -} - -#[starknet::contract] -mod TestDapp { - use starknet::{get_caller_address, ContractAddress}; - - #[storage] - struct Storage { - stored_number: LegacyMap, - } - - #[external(v0)] - impl TestDappImpl of super::ITestDapp { - fn set_number(ref self: ContractState, number: felt252) { - self.stored_number.write(get_caller_address(), number); - } - - fn set_number_double(ref self: ContractState, number: felt252) { - self.stored_number.write(get_caller_address(), number * 2); - } - - fn set_number_times3(ref self: ContractState, number: felt252) { - self.stored_number.write(get_caller_address(), number * 3); - } - - fn increase_number(ref self: ContractState, number: felt252) -> felt252 { - let user = get_caller_address(); - let val = self.stored_number.read(user); - let new_number = val + number; - self.stored_number.write(user, new_number); - new_number - } - - fn throw_error(ref self: ContractState, number: felt252) { - assert(0 == 1, 'test dapp reverted') - } - - fn get_number(self: @ContractState, user: ContractAddress) -> felt252 { - self.stored_number.read(user) - } - } -} diff --git a/contracts/lib/src/tests.cairo b/contracts/lib/src/tests.cairo deleted file mode 100644 index dd91b298..00000000 --- a/contracts/lib/src/tests.cairo +++ /dev/null @@ -1 +0,0 @@ -mod test_asserts; diff --git a/contracts/lib/src/tests/test_asserts.cairo b/contracts/lib/src/tests/test_asserts.cairo deleted file mode 100644 index a19c1f30..00000000 --- a/contracts/lib/src/tests/test_asserts.cairo +++ /dev/null @@ -1,104 +0,0 @@ -use array::ArrayTrait; -use starknet::{ - contract_address_const, testing::{set_caller_address, set_contract_address}, account::Call -}; - -use lib::asserts; - -#[test] -#[available_gas(2000000)] -fn test_assert_only_self() { - set_caller_address(contract_address_const::<42>()); - set_contract_address(contract_address_const::<42>()); - asserts::assert_only_self(); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('argent/only-self', ))] -fn test_assert_only_self_panic() { - set_caller_address(contract_address_const::<42>()); - set_contract_address(contract_address_const::<69>()); - asserts::assert_only_self(); -} - -#[test] -fn assert_correct_tx_version() { - asserts::assert_correct_tx_version(1); -} - -#[test] -fn assert_correct_tx_version_query_version() { - asserts::assert_correct_tx_version(340282366920938463463374607431768211457); -} - -#[test] -#[should_panic(expected: ('argent/invalid-tx-version', ))] -fn assert_correct_tx_version_invalid_tx() { - asserts::assert_correct_tx_version(4); -} - -#[test] -#[available_gas(2000000)] -fn test_no_self_call_empty() { - let self = contract_address_const::<42>(); - set_caller_address(self); - let calls = ArrayTrait::new(); - asserts::assert_no_self_call(calls.span(), self); -} - -#[test] -#[available_gas(2000000)] -fn test_no_self_call_1() { - let self = contract_address_const::<42>(); - set_caller_address(self); - let mut calls = ArrayTrait::new(); - let call1 = Call { - to: contract_address_const::<1>(), selector: 100, calldata: ArrayTrait::new() - }; - calls.append(call1); - asserts::assert_no_self_call(calls.span(), self); -} - -#[test] -#[available_gas(2000000)] -fn test_no_self_call_2() { - let self = contract_address_const::<42>(); - set_caller_address(self); - let mut calls = ArrayTrait::new(); - let call1 = Call { - to: contract_address_const::<2>(), selector: 100, calldata: ArrayTrait::new() - }; - let call2 = Call { - to: contract_address_const::<3>(), selector: 200, calldata: ArrayTrait::new() - }; - calls.append(call1); - calls.append(call2); - asserts::assert_no_self_call(calls.span(), self); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('argent/no-multicall-to-self', ))] -fn test_no_self_call_invalid() { - let self = contract_address_const::<42>(); - set_caller_address(self); - let mut calls = ArrayTrait::new(); - calls.append(Call { to: self, selector: 100, calldata: ArrayTrait::new() }); - asserts::assert_no_self_call(calls.span(), self); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('argent/no-multicall-to-self', ))] -fn test_no_self_call_invalid_2() { - let self = contract_address_const::<42>(); - set_caller_address(self); - let mut calls = ArrayTrait::new(); - let call1 = Call { - to: contract_address_const::<1>(), selector: 100, calldata: ArrayTrait::new() - }; - calls.append(call1); - calls.append(Call { to: self, selector: 200, calldata: ArrayTrait::new() }); - asserts::assert_no_self_call(calls.span(), self); -} diff --git a/contracts/multicall/README.md b/contracts/multicall/README.md deleted file mode 100644 index 33d8866b..00000000 --- a/contracts/multicall/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Multicall - -The Multicall contract aggregates results from multiple contract view function calls. - -This reduces the number of separate JSON RPC requests that need to be sent while also providing the guarantee that all values returned are from the same block. \ No newline at end of file diff --git a/contracts/multicall/cairo_project.toml b/contracts/multicall/cairo_project.toml deleted file mode 100644 index 96c09f30..00000000 --- a/contracts/multicall/cairo_project.toml +++ /dev/null @@ -1,3 +0,0 @@ -[crate_roots] -multicall = "src" -lib = "../lib/src" diff --git a/contracts/multicall/src/lib.cairo b/contracts/multicall/src/lib.cairo deleted file mode 100644 index 93a6c2dd..00000000 --- a/contracts/multicall/src/lib.cairo +++ /dev/null @@ -1,5 +0,0 @@ -mod multicall; -use multicall::Multicall; - -#[cfg(test)] -mod tests; diff --git a/contracts/multicall/src/multicall.cairo b/contracts/multicall/src/multicall.cairo deleted file mode 100644 index 9fe91c66..00000000 --- a/contracts/multicall/src/multicall.cairo +++ /dev/null @@ -1,24 +0,0 @@ -use starknet::account::Call; - -#[starknet::interface] -trait IMulticall { - fn aggregate(self: @TContractState, calls: Array) -> (u64, Array>); -} - -#[starknet::contract] -mod Multicall { - use array::ArrayTrait; - use starknet::{info::get_block_number, account::Call}; - - use lib::execute_multicall; - - #[storage] - struct Storage {} - - #[external(v0)] - impl MulticallImpl of super::IMulticall { - fn aggregate(self: @ContractState, calls: Array) -> (u64, Array>) { - (get_block_number(), execute_multicall(calls.span())) - } - } -} diff --git a/contracts/multicall/src/tests.cairo b/contracts/multicall/src/tests.cairo deleted file mode 100644 index 8b6e1728..00000000 --- a/contracts/multicall/src/tests.cairo +++ /dev/null @@ -1 +0,0 @@ -mod test_multicall; diff --git a/contracts/multicall/src/tests/test_multicall.cairo b/contracts/multicall/src/tests/test_multicall.cairo deleted file mode 100644 index 2aff69bf..00000000 --- a/contracts/multicall/src/tests/test_multicall.cairo +++ /dev/null @@ -1,60 +0,0 @@ -use array::ArrayTrait; -use traits::TryInto; -use option::OptionTrait; -use result::ResultTrait; -use starknet::{contract_address_const, deploy_syscall, account::Call}; - -use lib::{execute_multicall, TestDapp}; - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('argent/multicall-failed', 0, 'CONTRACT_NOT_DEPLOYED'))] -fn execute_multicall_simple() { - let call = Call { - to: contract_address_const::<42>(), selector: 43, calldata: ArrayTrait::new() - }; - - let mut arr = ArrayTrait::new(); - arr.append(call); - execute_multicall(arr.span()); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('argent/multicall-failed', 2, 'test dapp reverted', 'ENTRYPOINT_FAILED'))] -fn execute_multicall_at_one() { - let calldataDeploy = ArrayTrait::new(); - let class_hash = TestDapp::TEST_CLASS_HASH.try_into().unwrap(); - let (address0, _) = deploy_syscall(class_hash, 0, calldataDeploy.span(), false).unwrap(); - - let mut calldata1 = ArrayTrait::new(); - calldata1.append(12); - let call1 = Call { - to: address0, - selector: 1257997212343903061729138261393903607425919870525153789348007715635666768741, // set_number(number) - calldata: calldata1 - }; - - let mut calldata2 = ArrayTrait::new(); - calldata2.append(12); - let call2 = Call { - to: address0, - selector: 966438596990474552217413352546537164754794065595593730315125915414067970214, // increase_number(number) - calldata: calldata2 - }; - - let mut calldata3 = ArrayTrait::new(); - calldata3.append(12); - let call3 = Call { - to: address0, - selector: 1378405772398747753825744346429351463310669626437442629621279049660910933566, // throw_error(number) - calldata: calldata3 - }; - - let mut arr = ArrayTrait::new(); - arr.append(call1); - arr.append(call2); - arr.append(call3); - execute_multicall(arr.span()); -} - diff --git a/contracts/multisig/README.md b/contracts/multisig/README.md deleted file mode 100644 index 5336e36a..00000000 --- a/contracts/multisig/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# Argent Multisig - -## High-Level Specification - -The Argent Multisig account is a typical n-of-m multisig. It requires multiple signatures from different parties to authorize any operation from the account. - -The account is controlled by multiple owners (or `signers`). The number of owners that need to approve an operation is called the `threshold`. - -This account leverages account abstraction, so the account can pay for its own transaction fees. - -A valid account signature is a list of `threshold` individual owner signatures. This account signature can be used to validate a Starknet transaction or an off-chain message through the `is_valid_signature` method. - -Any operation that changes the security parameters of the account, like adding/removing/changing owners, upgrading, or changing the threshold will also require the approval (signature) of `threshold` owners. - -By default the account can execute a sequence of operations such as calling external contracts in a multicall. A multicall will fail if one of the inner call fails. Whenever a function of the account must be called (`add_signers`, `remove_signers`, `upgrade`, etc), it should be the only call performed in this multicall. - -In addition to the main `__execute__` entry point used by the Starknet protocol, the account can also be called by an external party via the `execute_from_outside` method to e.g. enable sponsored transactions. The calling party must provide a valid account signature for the target execution. - -## Signature format - -The account signature is a list of owner signatures. The list must contain exactly `threshold` signatures and every owner can only sign once. Moreover, to simplify processing, the signatures need to be ordered by the owner public key, in ascending order. - -## Self-deployment - -The account can pay the transaction fee for its own deployment. In this scenario, the multisig only requires the signature of one of the owners. -This allows for better UX. - -**For extra safety, it's recommended to deploy the account before depositing large amounts in the account**. - -## Upgrade - -To enable the model to evolve, the account implements an `upgrade` method that replaces the implementation. Calling this method, as any other method, requires the approval from `threshold` owners. diff --git a/contracts/multisig/cairo_project.toml b/contracts/multisig/cairo_project.toml deleted file mode 100644 index 66009a8c..00000000 --- a/contracts/multisig/cairo_project.toml +++ /dev/null @@ -1,3 +0,0 @@ -[crate_roots] -multisig = "src" -lib = "../lib/src" diff --git a/contracts/multisig/src/lib.cairo b/contracts/multisig/src/lib.cairo deleted file mode 100644 index f872aac9..00000000 --- a/contracts/multisig/src/lib.cairo +++ /dev/null @@ -1,16 +0,0 @@ -mod interface; -use interface::IArgentMultisig; -use interface::IDeprecatedArgentMultisig; - -mod argent_multisig; -use argent_multisig::ArgentMultisig; - -// Structures - -mod signer_signature; -use signer_signature::SignerSignature; -use signer_signature::deserialize_array_signer_signature; - -#[cfg(test)] -mod tests; - diff --git a/contracts/multisig/src/tests.cairo b/contracts/multisig/src/tests.cairo deleted file mode 100644 index 3f727163..00000000 --- a/contracts/multisig/src/tests.cairo +++ /dev/null @@ -1,117 +0,0 @@ -mod test_multisig_account; -mod test_multisig_remove_signers; -mod test_multisig_replace_signers; -mod test_multisig_signing; - -use multisig::ArgentMultisig; - -const signer_pubkey_1: felt252 = 0x1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca; -const signer_pubkey_2: felt252 = 0x759ca09377679ecd535a81e83039658bf40959283187c654c5416f439403cf5; -const signer_pubkey_3: felt252 = 0x411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20; - -use array::{ArrayTrait, SpanTrait}; -use traits::{TryInto, Into}; -use option::OptionTrait; -use result::ResultTrait; -use starknet::{ - contract_address_const, syscalls::deploy_syscall, account::Call, testing::set_contract_address -}; -use lib::Version; - -#[starknet::interface] -trait ITestArgentMultisig { - // IAccount - fn __validate_declare__(self: @TContractState, class_hash: felt252) -> felt252; - 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; - // IArgentMultisig - fn __validate_deploy__( - self: @TContractState, - class_hash: felt252, - contract_address_salt: felt252, - threshold: usize, - signers: Array - ) -> felt252; - // External - fn change_threshold(ref self: TContractState, new_threshold: usize); - fn add_signers(ref self: TContractState, new_threshold: usize, signers_to_add: Array); - fn remove_signers( - ref self: TContractState, new_threshold: usize, signers_to_remove: Array - ); - fn replace_signer(ref self: TContractState, signer_to_remove: felt252, signer_to_add: felt252); - // Views - fn get_name(self: @TContractState) -> felt252; - fn get_version(self: @TContractState) -> Version; - fn get_threshold(self: @TContractState) -> usize; - fn get_signers(self: @TContractState) -> Array; - fn is_signer(self: @TContractState, signer: felt252) -> bool; - fn assert_valid_signer_signature( - self: @TContractState, - hash: felt252, - signer: felt252, - signature_r: felt252, - signature_s: felt252 - ); - fn is_valid_signer_signature( - self: @TContractState, - hash: felt252, - signer: felt252, - signature_r: felt252, - signature_s: felt252 - ) -> bool; - - // IErc165 - fn supports_interface(self: @TContractState, interface_id: felt252) -> bool; - - // IDeprecatedArgentMultisig - fn getVersion(self: @TContractState) -> felt252; - fn getName(self: @TContractState) -> felt252; - fn supportsInterface(self: @TContractState, interface_id: felt252) -> felt252; - fn isValidSignature( - self: @TContractState, hash: felt252, signatures: Array - ) -> felt252; -} - -fn initialize_multisig() -> ITestArgentMultisigDispatcher { - let threshold = 1; - let mut signers_array = ArrayTrait::new(); - signers_array.append(signer_pubkey_1); - signers_array.append(signer_pubkey_2); - signers_array.append(signer_pubkey_3); - initialize_multisig_with(threshold, signers_array.span()) -} - -fn initialize_multisig_with_one_signer() -> ITestArgentMultisigDispatcher { - let threshold = 1; - let mut signers_array = ArrayTrait::new(); - signers_array.append(signer_pubkey_1); - initialize_multisig_with(threshold, signers_array.span()) -} - -fn initialize_multisig_with( - threshold: usize, mut signers: Span -) -> ITestArgentMultisigDispatcher { - let mut calldata = ArrayTrait::new(); - calldata.append(threshold.into()); - calldata.append(signers.len().into()); - loop { - match signers.pop_front() { - Option::Some(signer) => { - calldata.append(*signer) - }, - Option::None(()) => { - break; - }, - }; - }; - - let class_hash = ArgentMultisig::TEST_CLASS_HASH.try_into().unwrap(); - let (contract_address, _) = deploy_syscall(class_hash, 0, calldata.span(), true).unwrap(); - - // This will set the caller for subsequent calls (avoid 'argent/only-self') - set_contract_address(contract_address_const::<1>()); - ITestArgentMultisigDispatcher { contract_address } -} diff --git a/contracts/multisig/src/tests/test_multisig_account.cairo b/contracts/multisig/src/tests/test_multisig_account.cairo deleted file mode 100644 index 2503a7fe..00000000 --- a/contracts/multisig/src/tests/test_multisig_account.cairo +++ /dev/null @@ -1,119 +0,0 @@ -use array::ArrayTrait; -use option::OptionTrait; -use result::ResultTrait; -use traits::TryInto; - -use starknet::deploy_syscall; - -use multisig::{ - ArgentMultisig, - tests::{ - initialize_multisig, signer_pubkey_1, signer_pubkey_2, ITestArgentMultisigDispatcherTrait, - initialize_multisig_with, initialize_multisig_with_one_signer - } -}; - -#[test] -#[available_gas(20000000)] -fn valid_initialize() { - let multisig = initialize_multisig_with_one_signer(); - assert(multisig.get_threshold() == 1, 'threshold not set'); - // test if is signer correctly returns true - assert(multisig.is_signer(signer_pubkey_1), 'is signer cant find signer'); - - // test signers list - let signers = multisig.get_signers(); - assert(signers.len() == 1, 'invalid signers length'); - assert(*signers[0] == signer_pubkey_1, 'invalid signers result'); -} - -#[test] -#[available_gas(20000000)] -fn valid_initialize_two_signers() { - let threshold = 1; - let mut signers_array = ArrayTrait::new(); - signers_array.append(signer_pubkey_1); - signers_array.append(signer_pubkey_2); - let multisig = initialize_multisig_with(threshold, signers_array.span()); - // test if is signer correctly returns true - assert(multisig.is_signer(signer_pubkey_1), 'is signer cant find signer 1'); - assert(multisig.is_signer(signer_pubkey_2), 'is signer cant find signer 2'); - - // test signers list - let signers = multisig.get_signers(); - assert(signers.len() == 2, 'invalid signers length'); - assert(*signers[0] == signer_pubkey_1, 'invalid signers result'); - assert(*signers[1] == signer_pubkey_2, 'invalid signers result'); -} - -#[test] -#[available_gas(20000000)] -fn invalid_threshold() { - let threshold = 3; - let mut calldata = ArrayTrait::new(); - calldata.append(threshold); - calldata.append(1); - calldata.append(signer_pubkey_1); - - let class_hash = ArgentMultisig::TEST_CLASS_HASH.try_into().unwrap(); - let mut err = deploy_syscall(class_hash, 0, calldata.span(), true).unwrap_err(); - assert(@err.pop_front().unwrap() == @'argent/bad-threshold', 'Should be argent/bad-threshold'); -} - -#[test] -#[available_gas(20000000)] -fn change_threshold() { - let threshold = 1; - let mut signers_array = ArrayTrait::new(); - signers_array.append(1); - signers_array.append(2); - let multisig = initialize_multisig_with(threshold, signers_array.span()); - - multisig.change_threshold(2); - assert(multisig.get_threshold() == 2, 'new threshold not set'); -} - -#[test] -#[available_gas(20000000)] -fn add_signers() { - // init - let multisig = initialize_multisig_with_one_signer(); - - // add signer - let mut new_signers = ArrayTrait::new(); - new_signers.append(signer_pubkey_2); - multisig.add_signers(2, new_signers); - - // check - let signers = multisig.get_signers(); - assert(signers.len() == 2, 'invalid signers length'); - assert(multisig.get_threshold() == 2, 'new threshold not set'); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('argent/already-a-signer', 'ENTRYPOINT_FAILED'))] -fn add_signer_already_in_list() { - // init - let multisig = initialize_multisig_with_one_signer(); - - // add signer - let mut new_signers = ArrayTrait::new(); - new_signers.append(signer_pubkey_1); - multisig.add_signers(2, new_signers); -} - -#[test] -#[available_gas(20000000)] -fn get_name() { - assert(initialize_multisig().get_name() == 'ArgentMultisig', 'Name should be ArgentMultisig'); -} - -#[test] -#[available_gas(20000000)] -fn get_version() { - let version = initialize_multisig().get_version(); - assert(version.major == 0, 'Version major'); - assert(version.minor == 1, 'Version minor'); - assert(version.patch == 0, 'Version patch'); -} diff --git a/contracts/multisig/src/tests/test_multisig_remove_signers.cairo b/contracts/multisig/src/tests/test_multisig_remove_signers.cairo deleted file mode 100644 index e4ab1e5a..00000000 --- a/contracts/multisig/src/tests/test_multisig_remove_signers.cairo +++ /dev/null @@ -1,218 +0,0 @@ -use array::ArrayTrait; - -use multisig::tests::{ - initialize_multisig, signer_pubkey_1, signer_pubkey_2, signer_pubkey_3, - ITestArgentMultisigDispatcherTrait -}; - -#[test] -#[available_gas(20000000)] -fn remove_signers_first() { - // init - let multisig = initialize_multisig(); - - // remove signer - let mut signer_to_remove = ArrayTrait::new(); - signer_to_remove.append(signer_pubkey_1); - multisig.remove_signers(1, signer_to_remove); - - // check - let signers = multisig.get_signers(); - assert(signers.len() == 2, 'invalid signers length'); - assert(multisig.get_threshold() == 1, 'new threshold not set'); - assert(!multisig.is_signer(signer_pubkey_1), 'signer 1 was not removed'); - assert(multisig.is_signer(signer_pubkey_2), 'signer 2 was removed'); - assert(multisig.is_signer(signer_pubkey_3), 'signer 3 was removed'); -} -#[test] -#[available_gas(20000000)] -fn remove_signers_center() { - // init - let multisig = initialize_multisig(); - - // remove signer - let mut signer_to_remove = ArrayTrait::new(); - signer_to_remove.append(signer_pubkey_2); - multisig.remove_signers(1, signer_to_remove); - - // check - let signers = multisig.get_signers(); - assert(signers.len() == 2, 'invalid signers length'); - assert(multisig.get_threshold() == 1, 'new threshold not set'); - assert(!multisig.is_signer(signer_pubkey_2), 'signer 2 was not removed'); - assert(multisig.is_signer(signer_pubkey_1), 'signer 1 was removed'); - assert(multisig.is_signer(signer_pubkey_3), 'signer 3 was removed'); -} - -#[test] -#[available_gas(20000000)] -fn remove_signers_last() { - // init - let multisig = initialize_multisig(); - - // remove signer - let mut signer_to_remove = ArrayTrait::new(); - signer_to_remove.append(signer_pubkey_3); - multisig.remove_signers(1, signer_to_remove); - - // check - let signers = multisig.get_signers(); - assert(signers.len() == 2, 'invalid signers length'); - assert(multisig.get_threshold() == 1, 'new threshold not set'); - assert(!multisig.is_signer(signer_pubkey_3), 'signer 3 was not removed'); - assert(multisig.is_signer(signer_pubkey_1), 'signer 1 was removed'); - assert(multisig.is_signer(signer_pubkey_2), 'signer 2 was removed'); -} - -#[test] -#[available_gas(20000000)] -fn remove_1_and_2() { - // init - let multisig = initialize_multisig(); - - // remove signer - let mut signer_to_remove = ArrayTrait::new(); - signer_to_remove.append(signer_pubkey_1); - signer_to_remove.append(signer_pubkey_2); - multisig.remove_signers(1, signer_to_remove); - - // check - let signers = multisig.get_signers(); - assert(signers.len() == 1, 'invalid signers length'); - assert(multisig.get_threshold() == 1, 'new threshold not set'); - assert(!multisig.is_signer(signer_pubkey_1), 'signer 1 was not removed'); - assert(!multisig.is_signer(signer_pubkey_2), 'signer 2 was not removed'); - assert(multisig.is_signer(signer_pubkey_3), 'signer 3 was removed'); -} - -#[test] -#[available_gas(20000000)] -fn remove_1_and_3() { - // init - let multisig = initialize_multisig(); - - // remove signer - let mut signer_to_remove = ArrayTrait::new(); - signer_to_remove.append(signer_pubkey_1); - signer_to_remove.append(signer_pubkey_3); - multisig.remove_signers(1, signer_to_remove); - - // check - let signers = multisig.get_signers(); - assert(signers.len() == 1, 'invalid signers length'); - assert(multisig.get_threshold() == 1, 'new threshold not set'); - assert(!multisig.is_signer(signer_pubkey_1), 'signer 1 was not removed'); - assert(!multisig.is_signer(signer_pubkey_3), 'signer 3 was not removed'); - assert(multisig.is_signer(signer_pubkey_2), 'signer 2 was removed'); -} - -#[test] -#[available_gas(20000000)] -fn remove_2_and_3() { - // init - let multisig = initialize_multisig(); - - // remove signer - let mut signer_to_remove = ArrayTrait::new(); - signer_to_remove.append(signer_pubkey_2); - signer_to_remove.append(signer_pubkey_3); - multisig.remove_signers(1, signer_to_remove); - - // check - let signers = multisig.get_signers(); - assert(signers.len() == 1, 'invalid signers length'); - assert(multisig.get_threshold() == 1, 'new threshold not set'); - assert(!multisig.is_signer(signer_pubkey_2), 'signer 2 was not removed'); - assert(!multisig.is_signer(signer_pubkey_3), 'signer 3 was not removed'); - assert(multisig.is_signer(signer_pubkey_1), 'signer 1 was removed'); -} - -#[test] -#[available_gas(20000000)] -fn remove_2_and_1() { - // init - let multisig = initialize_multisig(); - - // remove signer - let mut signer_to_remove = ArrayTrait::new(); - signer_to_remove.append(signer_pubkey_2); - signer_to_remove.append(signer_pubkey_1); - multisig.remove_signers(1, signer_to_remove); - - // check - let signers = multisig.get_signers(); - assert(signers.len() == 1, 'invalid signers length'); - assert(multisig.get_threshold() == 1, 'new threshold not set'); - assert(!multisig.is_signer(signer_pubkey_2), 'signer 2 was not removed'); - assert(!multisig.is_signer(signer_pubkey_1), 'signer 1 was not removed'); - assert(multisig.is_signer(signer_pubkey_3), 'signer 3 was removed'); -} - -#[test] -#[available_gas(20000000)] -fn remove_3_and_1() { - // init - let multisig = initialize_multisig(); - - // remove signer - let mut signer_to_remove = ArrayTrait::new(); - signer_to_remove.append(signer_pubkey_3); - signer_to_remove.append(signer_pubkey_1); - multisig.remove_signers(1, signer_to_remove); - - // check - let signers = multisig.get_signers(); - assert(signers.len() == 1, 'invalid signers length'); - assert(multisig.get_threshold() == 1, 'new threshold not set'); - assert(!multisig.is_signer(signer_pubkey_3), 'signer 3 was not removed'); - assert(!multisig.is_signer(signer_pubkey_1), 'signer 1 was not removed'); - assert(multisig.is_signer(signer_pubkey_2), 'signer 2 was removed'); -} - -#[test] -#[available_gas(20000000)] -fn remove_3_and_2() { - // init - let multisig = initialize_multisig(); - - // remove signer - let mut signer_to_remove = ArrayTrait::new(); - signer_to_remove.append(signer_pubkey_3); - signer_to_remove.append(signer_pubkey_2); - multisig.remove_signers(1, signer_to_remove); - - // check - let signers = multisig.get_signers(); - assert(signers.len() == 1, 'invalid signers length'); - assert(multisig.get_threshold() == 1, 'new threshold not set'); - assert(!multisig.is_signer(signer_pubkey_3), 'signer 3 was not removed'); - assert(!multisig.is_signer(signer_pubkey_2), 'signer 2 was not removed'); - assert(multisig.is_signer(signer_pubkey_1), 'signer 1 was removed'); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('argent/not-a-signer', 'ENTRYPOINT_FAILED'))] -fn remove_invalid_signers() { - // init - let multisig = initialize_multisig(); - - // remove signer - let mut signer_to_remove = ArrayTrait::new(); - signer_to_remove.append(10); - multisig.remove_signers(1, signer_to_remove); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('argent/bad-threshold', 'ENTRYPOINT_FAILED'))] -fn remove_signers_invalid_threshold() { - // init - let multisig = initialize_multisig(); - - // remove signer - let mut signer_to_remove = ArrayTrait::new(); - signer_to_remove.append(signer_pubkey_1); - signer_to_remove.append(signer_pubkey_2); - multisig.remove_signers(2, signer_to_remove); -} diff --git a/contracts/multisig/src/tests/test_multisig_replace_signers.cairo b/contracts/multisig/src/tests/test_multisig_replace_signers.cairo deleted file mode 100644 index 21dc4961..00000000 --- a/contracts/multisig/src/tests/test_multisig_replace_signers.cairo +++ /dev/null @@ -1,109 +0,0 @@ -use array::ArrayTrait; -use traits::Into; - -use multisig::tests::{ - initialize_multisig, signer_pubkey_1, signer_pubkey_2, signer_pubkey_3, - ITestArgentMultisigDispatcherTrait, initialize_multisig_with_one_signer -}; - -#[test] -#[available_gas(20000000)] -fn replace_signer_1() { - // init - let multisig = initialize_multisig_with_one_signer(); - - // replace signer - let signer_to_add = signer_pubkey_2; - multisig.replace_signer(signer_pubkey_1, signer_to_add); - - // check - let signers = multisig.get_signers(); - assert(signers.len() == 1, 'signer list changed size'); - assert(multisig.get_threshold() == 1, 'threshold changed'); - assert(!multisig.is_signer(signer_pubkey_1), 'signer 1 was not removed'); - assert(multisig.is_signer(signer_to_add), 'new was not added'); -} - -#[test] -#[available_gas(20000000)] -fn replace_signer_start() { - // init - let multisig = initialize_multisig(); - - // replace signer - let signer_to_add = 5; - multisig.replace_signer(signer_pubkey_1, signer_to_add); - - // check - let signers = multisig.get_signers(); - assert(signers.len() == 3, 'signer list changed size'); - assert(multisig.get_threshold() == 1, 'threshold changed'); - assert(!multisig.is_signer(signer_pubkey_1), 'signer 1 was not removed'); - assert(multisig.is_signer(signer_to_add), 'new was not added'); - assert(multisig.is_signer(signer_pubkey_2), 'signer 2 was removed'); - assert(multisig.is_signer(signer_pubkey_3), 'signer 3 was removed'); -} - -#[test] -#[available_gas(20000000)] -fn replace_signer_middle() { - // init - let multisig = initialize_multisig(); - - // replace signer - let signer_to_add = 5; - multisig.replace_signer(signer_pubkey_2, signer_to_add); - - // check - let signers = multisig.get_signers(); - assert(signers.len() == 3, 'signer list changed size'); - assert(multisig.get_threshold() == 1, 'threshold changed'); - assert(!multisig.is_signer(signer_pubkey_2), 'signer 2 was not removed'); - assert(multisig.is_signer(signer_to_add), 'new was not added'); - assert(multisig.is_signer(signer_pubkey_1), 'signer 1 was removed'); - assert(multisig.is_signer(signer_pubkey_3), 'signer 3 was removed'); -} - -#[test] -#[available_gas(20000000)] -fn replace_signer_end() { - // init - let multisig = initialize_multisig(); - - // replace signer - let signer_to_add = 5; - multisig.replace_signer(signer_pubkey_3, signer_to_add); - - // check - let signers = multisig.get_signers(); - assert(signers.len() == 3, 'signer list changed size'); - assert(multisig.get_threshold() == 1, 'threshold changed'); - assert(!multisig.is_signer(signer_pubkey_3), 'signer 3 was not removed'); - assert(multisig.is_signer(signer_to_add), 'new was not added'); - assert(multisig.is_signer(signer_pubkey_1), 'signer 1 was removed'); - assert(multisig.is_signer(signer_pubkey_2), 'signer 2 was removed'); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('argent/not-a-signer', 'ENTRYPOINT_FAILED'))] -fn replace_invalid_signer() { - // init - let multisig = initialize_multisig(); - - // replace signer - let signer_to_add = 5; - let not_a_signer = 10; - multisig.replace_signer(not_a_signer, signer_to_add); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('argent/already-a-signer', 'ENTRYPOINT_FAILED'))] -fn replace_already_signer() { - // init - let multisig = initialize_multisig(); - - // replace signer - multisig.replace_signer(signer_pubkey_3, signer_pubkey_1); -} diff --git a/contracts/multisig/src/tests/test_multisig_signing.cairo b/contracts/multisig/src/tests/test_multisig_signing.cairo deleted file mode 100644 index 8cca8d40..00000000 --- a/contracts/multisig/src/tests/test_multisig_signing.cairo +++ /dev/null @@ -1,136 +0,0 @@ -use array::ArrayTrait; -use traits::Into; - -use multisig::tests::{ - ITestArgentMultisigDispatcherTrait, initialize_multisig_with, - initialize_multisig_with_one_signer, signer_pubkey_1, signer_pubkey_2 -}; -use starknet::VALIDATED; - -const message_hash: felt252 = 424242; - -const signer_1_signature_r: felt252 = - 780418022109335103732757207432889561210689172704851180349474175235986529895; -const signer_1_signature_s: felt252 = - 117732574052293722698213953663617651411051623743664517986289794046851647347; - -const signer_2_signature_r: felt252 = - 2543572729543774155040746789716602521360190010191061121815852574984983703153; -const signer_2_signature_s: felt252 = - 3047778680024311010844701802416003052323696285920266547201663937333620527443; - -#[test] -#[available_gas(20000000)] -fn test_signature() { - let multisig = initialize_multisig_with_one_signer(); - - let mut signature = ArrayTrait::::new(); - signature.append(signer_pubkey_1); - signature.append(signer_1_signature_r); - signature.append(signer_1_signature_s); - assert(multisig.is_valid_signature(message_hash, signature) == VALIDATED, 'bad signature'); -} - -#[test] -#[available_gas(20000000)] -fn test_double_signature() { - // init - let threshold = 2; - let mut signers_array = ArrayTrait::new(); - signers_array.append(signer_pubkey_1); - signers_array.append(signer_pubkey_2); - let multisig = initialize_multisig_with(threshold, signers_array.span()); - - let mut signature = ArrayTrait::::new(); - signature.append(signer_pubkey_1); - signature.append(signer_1_signature_r); - signature.append(signer_1_signature_s); - signature.append(signer_pubkey_2); - signature.append(signer_2_signature_r); - signature.append(signer_2_signature_s); - assert(multisig.is_valid_signature(message_hash, signature) == VALIDATED, 'bad signature'); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('argent/signatures-not-sorted', 'ENTRYPOINT_FAILED'))] -fn test_double_signature_order() { - let threshold = 2; - let mut signers_array = ArrayTrait::new(); - signers_array.append(signer_pubkey_2); - signers_array.append(signer_pubkey_1); - let multisig = initialize_multisig_with(threshold, signers_array.span()); - - let mut signature = ArrayTrait::::new(); - signature.append(signer_pubkey_2); - signature.append(signer_2_signature_r); - signature.append(signer_2_signature_s); - signature.append(signer_pubkey_1); - signature.append(signer_1_signature_r); - signature.append(signer_1_signature_s); - multisig.is_valid_signature(message_hash, signature); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('argent/signatures-not-sorted', 'ENTRYPOINT_FAILED'))] -fn test_same_owner_twice() { - let threshold = 2; - let mut signers_array = ArrayTrait::new(); - signers_array.append(signer_pubkey_1); - signers_array.append(signer_pubkey_2); - let multisig = initialize_multisig_with(threshold, signers_array.span()); - - let mut signature = ArrayTrait::::new(); - signature.append(signer_pubkey_1); - signature.append(signer_1_signature_r); - signature.append(signer_1_signature_s); - signature.append(signer_pubkey_1); - signature.append(signer_1_signature_r); - signature.append(signer_1_signature_s); - multisig.is_valid_signature(message_hash, signature); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('argent/invalid-signature-length', 'ENTRYPOINT_FAILED'))] -fn test_missing_owner_signature() { - let threshold = 2; - let mut signers_array = ArrayTrait::new(); - signers_array.append(signer_pubkey_1); - signers_array.append(signer_pubkey_2); - let multisig = initialize_multisig_with(threshold, signers_array.span()); - - let mut signature = ArrayTrait::::new(); - signature.append(signer_pubkey_1); - signature.append(signer_1_signature_r); - signature.append(signer_1_signature_s); - multisig.is_valid_signature(message_hash, signature); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('argent/invalid-signature-length', 'ENTRYPOINT_FAILED'))] -fn test_short_signature() { - let multisig = initialize_multisig_with_one_signer(); - - let mut signature = ArrayTrait::::new(); - signature.append(signer_pubkey_1); - signature.append(signer_1_signature_r); - signature.append(signer_1_signature_s); - signature.append(signer_pubkey_1); - signature.append(signer_1_signature_r); - signature.append(signer_1_signature_s); - multisig.is_valid_signature(message_hash, signature); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('argent/invalid-signature-length', 'ENTRYPOINT_FAILED'))] -fn test_long_signature() { - let multisig = initialize_multisig_with_one_signer(); - - let mut signature = ArrayTrait::::new(); - signature.append(42); - multisig.is_valid_signature(message_hash, signature); -} diff --git a/scripts/calculate-class-hash.py b/scripts/calculate-class-hash.py deleted file mode 100644 index 12f9cc2b..00000000 --- a/scripts/calculate-class-hash.py +++ /dev/null @@ -1,22 +0,0 @@ -import os -import json -from starkware.starknet.core.os.class_hash import compute_class_hash -from starkware.starknet.services.api.contract_class import ContractClass - -path_to_json = './artifacts/' -json_files = [pos_json for pos_json in os.listdir( - path_to_json) if pos_json.endswith('.json')] - - -def print_class_hash(class_location): - location = path_to_json + class_location - with open(location) as f: - class_data = json.load(f) - contract_class = ContractClass.load(class_data) - contract_hash = compute_class_hash( - contract_class=contract_class, - ) - print("{} class hash: {}".format(class_location, hex(contract_hash))) - - -list(map(print_class_hash, json_files)) diff --git a/scripts/calculate-signatures.py b/scripts/calculate-signatures.py deleted file mode 100755 index 8061c89c..00000000 --- a/scripts/calculate-signatures.py +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/env python -import sys -sys.path.append('test') -from utils.utilities import str_to_felt -from utils.Signer import Signer -from starkware.cairo.common.hash_state import compute_hash_on_elements - -owner = Signer(1) -guardian = Signer(2) -guardian_backup = Signer(3) - -new_owner = Signer(4) -new_guardian = Signer(5) -new_guardian_backup = Signer(6) - -wrong_owner = Signer(7) -wrong_guardian = Signer(8) - -ESCAPE_SECURITY_PERIOD = 24*7*60*60 - -VERSION = str_to_felt('0.2.4') -NAME = str_to_felt('ArgentAccount') - -IACCOUNT_ID = 0xa66bd575 -IACCOUNT_ID_OLD = 0x3943f10f - -ESCAPE_TYPE_GUARDIAN = 1 -ESCAPE_TYPE_OWNER = 2 - - -def calculate_sig_account(): - hash = 1283225199545181604979924458180358646374088657288769423115053097913173815464 - invalid_hash = 1283225199545181604979924458180358646374088657288769423115053097913173811111 - - owner_r, owner_s = owner.sign(hash) - guardian_r, guardian_s = guardian.sign(hash) - guardian_backup_r, guardian_backup_s = guardian_backup.sign(hash) - wrong_owner_r, wrong_owner_s = wrong_owner.sign(hash) - wrong_guardian_r, wrong_guardian_s = wrong_guardian.sign(hash) - - print(f""" - - const message_hash: felt252 = 0x{hash:x}; - - const owner_pubkey: felt252 = 0x{owner.public_key:x}; - const owner_r: felt252 = 0x{owner_r:x}; - const owner_s: felt252 = 0x{owner_s:x}; - - const guardian_pubkey: felt252 = 0x{guardian.public_key:x}; - const guardian_r: felt252 = 0x{guardian_r:x}; - const guardian_s: felt252 = 0x{guardian_s:x}; - - const guardian_backup_pubkey: felt252 = 0x{guardian_backup.public_key:x}; - const guardian_backup_r: felt252 = 0x{guardian_backup_r:x}; - const guardian_backup_s: felt252 = 0x{guardian_backup_s:x}; - - const wrong_owner_pubkey: felt252 = 0x{wrong_owner.public_key:x}; - const wrong_owner_r: felt252 = 0x{wrong_owner_r:x}; - const wrong_owner_s: felt252 = 0x{wrong_owner_s:x}; - - const wrong_guardian_pubkey: felt252 = 0x{wrong_guardian.public_key:x}; - const wrong_guardian_r: felt252 = 0x{wrong_guardian_r:x}; - const wrong_guardian_s: felt252 = 0x{wrong_guardian_s:x}; - - """) - - -def calculate_sig_change_owner(): - # message_hash = pedersen(0, (change_owner selector, chainid, contract address, old_owner)) - change_owner_selector = 658036363289841962501247229249022783727527757834043681434485756469236076608 - chain_id = 0 - contract_address = 1 - old_owner = 0x1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca - - message_hash = compute_hash_on_elements([change_owner_selector, chain_id, contract_address, old_owner]) - - new_owner_r, new_owner_s = new_owner.sign(message_hash) - - print(f""" - - const new_owner_pubkey: felt252 = 0x{new_owner.public_key:x}; - const new_owner_r: felt252 = 0x{new_owner_r:x}; - const new_owner_s: felt252 = 0x{new_owner_s:x}; - """) - - -calculate_sig_change_owner() diff --git a/scripts/check-contract-approvers.ts b/scripts/check-contract-approvers.ts deleted file mode 100644 index a77803c8..00000000 --- a/scripts/check-contract-approvers.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { exec } from "child_process"; - -const minApprovers = parseInt(process.argv[2]); -if (Number.isNaN(minApprovers)) { - console.error("Usage: yarn ts-node ./scripts/contract-approvals.ts "); - process.exit(1); -} - -exec("gh pr view --json files,reviews", (err, stdout, stderr) => { - if (stderr.includes("no pull requests found")) { - console.log("✨ Not in a pull request"); - return; - } - if (stderr) { - console.error(stderr); - } - if (err) { - throw err; - } - const { files, reviews } = JSON.parse(stdout) as Record>; - - const contractsChanged = files.map(({ path }) => path).filter((path: string) => path.match(/\.(sol|cairo)$/)); - if (contractsChanged.length === 0) { - console.log("✨ No smart contracts changes"); - return; - } - - const approvals = reviews.filter(({ state }) => state === "APPROVED").map(({ author }) => author.login); - const approvers = new Set(approvals).size; - if (approvers < minApprovers) { - console.error(`\nNeed at least ${minApprovers} approvers for smart contract changes, got ${approvers}\n`); - return process.exit(1); - } else { - console.log(`✨ ${approvers} approvers for smart contract changes`); - } -}); diff --git a/scripts/deploy-account.ts b/scripts/deploy-account.ts deleted file mode 100644 index d009534e..00000000 --- a/scripts/deploy-account.ts +++ /dev/null @@ -1,26 +0,0 @@ -import "dotenv/config"; -import { declareContract, deployAccount, deployer, loadContract, provider } from "../tests/lib"; - -const argentAccountClassHash = await declareContract("ArgentAccount", true); -console.log("ArgentAccount class hash:", argentAccountClassHash); -const testDappClassHash = await declareContract("TestDapp", true); -console.log("TestDapp class hash:", testDappClassHash); - -console.log("Deploying new account"); -const { account, owner, guardian } = await deployAccount(argentAccountClassHash); -console.log("Account address:", account.address); -console.log("Account owner private key:", owner.privateKey); -console.log("Account guardian private key:", guardian.privateKey); - -console.log("Deploying new test dapp"); -const { contract_address } = await deployer.deployContract({ classHash: testDappClassHash }); -console.log("TestDapp address:", contract_address); -const testDappContract = await loadContract(contract_address); - -console.log("Calling test dapp"); -testDappContract.connect(account); -const response = await testDappContract.set_number(42n); -await provider.waitForTransaction(response.transaction_hash); - -const number = await testDappContract.get_number(account.address); -console.log(number === 42n ? "Seems good!" : "Something went wrong :("); diff --git a/scripts/deploy-multisig.ts b/scripts/deploy-multisig.ts deleted file mode 100644 index c775532f..00000000 --- a/scripts/deploy-multisig.ts +++ /dev/null @@ -1,33 +0,0 @@ -import "dotenv/config"; -import { declareContract, deployer, deployMultisig, loadContract, provider } from "../tests/lib"; - -const multisigClassHash = await declareContract("ArgentMultisig", true); -console.log("ArgentMultisig class hash:", multisigClassHash); -const testDappClassHash = await declareContract("TestDapp", true); -console.log("TestDapp class hash:", testDappClassHash); - -console.log("Deploying new account"); - -const threshold = 1; -const signersLength = 2; -const { account, keys, signers } = await deployMultisig(multisigClassHash, threshold, signersLength); - -console.log("Account address:", account.address); -console.log("Account signers:", signers); -console.log( - "Account private keys:", - keys.map(({ privateKey }) => privateKey), -); - -console.log("Deploying new test dapp"); -const { contract_address } = await deployer.deployContract({ classHash: testDappClassHash }); -console.log("TestDapp address:", contract_address); -const testDappContract = await loadContract(contract_address); - -console.log("Calling test dapp"); -testDappContract.connect(account); -const response = await testDappContract.set_number(42n); -await provider.waitForTransaction(response.transaction_hash); - -const number = await testDappContract.get_number(account.address); -console.log(number === 42n ? "Seems good!" : "Something went wrong :("); diff --git a/scripts/format-title.py b/scripts/format-title.py deleted file mode 100755 index 86fb062a..00000000 --- a/scripts/format-title.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import math - -if len(sys.argv) < 2: - print("Usage: ./scripts/format-title.py ") - sys.exit(1) - -title = " ".join(sys.argv[1:]) -half_len = len(title) // 2 - -indent = 4 -style = "/" - -if style == "*": - width = 120 - stars_len = width - indent - 4 - first_stars = stars_len // 2 - math.ceil(half_len) - second_stars = first_stars if len(title) % 2 == 0 else first_stars - 1 - - header = f"{' ' * indent}/{'*' * first_stars} {title} {'*' * second_stars}/" -else: - width = 100 - spaces_len = width - indent - 6 - first_spaces = spaces_len // 2 - math.ceil(half_len) - second_spaces = first_spaces if len(title) % 2 == 0 else first_spaces - 1 - lines = [ - f"{' ' * indent}{'/' * (width - indent)}", - f"{' ' * indent}//{' ' * first_spaces} {title} {' ' * second_spaces}//", - f"{' ' * indent}{'/' * (width - indent)}", - ] - header = "\n".join(lines) - -print(header) diff --git a/scripts/profile-account.ts b/scripts/profile-account.ts deleted file mode 100644 index 88d5b533..00000000 --- a/scripts/profile-account.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { - declareContract, - deployAccount, - deployAccountWithoutGuardian, - deployer, - deployOldAccount, - loadContract, -} from "../tests/lib"; -import { profileGasUsage } from "../tests/lib/gas"; - -const argentAccountClassHash = await declareContract("ArgentAccount"); -const oldArgentAccountClassHash = await declareContract("OldArgentAccount"); -const proxyClassHash = await declareContract("Proxy"); -const testDappClassHash = await declareContract("TestDapp"); -const { contract_address } = await deployer.deployContract({ classHash: testDappClassHash }); -const testDappContract = await loadContract(contract_address); - -{ - console.log("Old Account"); - const { account } = await deployOldAccount(proxyClassHash, oldArgentAccountClassHash); - testDappContract.connect(account); - const receipt = await testDappContract.set_number(42); - await profileGasUsage(receipt); -} - -{ - console.log("New Account"); - const { account } = await deployAccount(argentAccountClassHash); - testDappContract.connect(account); - const receipt = await testDappContract.set_number(42); - await profileGasUsage(receipt); -} - -{ - console.log("New Account without guardian"); - const { account } = await deployAccountWithoutGuardian(argentAccountClassHash); - testDappContract.connect(account); - const receipt = await testDappContract.set_number(42); - await profileGasUsage(receipt); -} diff --git a/scripts/start-devnet.sh b/scripts/start-devnet.sh deleted file mode 100755 index b4354a4e..00000000 --- a/scripts/start-devnet.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -if ! command -v starknet-devnet >/dev/null; then - echo "starknet-devnet is not installed. Please install it and try again." >&2 - echo "Maybe activate your venv using 'source path-to-venv/bin/activate'" >&2 - exit 1 -fi - -if nc -z 127.0.0.1 5050; then - echo "Port is not free" - exit 1 -else - echo "About to spawn a devnet" - export STARKNET_DEVNET_CAIRO_VM=rust - starknet-devnet --cairo-compiler-manifest $INSTALLATION_FOLDER_CARGO --seed 42 --lite-mode --timeout 320 --compiler-args '--add-pythonic-hints --allowed-libfuncs-list-name all' -fi diff --git a/scripts/wait-devnet-ready.sh b/scripts/wait-devnet-ready.sh deleted file mode 100755 index 1bd3eb2e..00000000 --- a/scripts/wait-devnet-ready.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -# Waits for the local devnet to be ready. - -set -e # stop the script if any subprocess fails - -echo Waiting for devnet to be ready. - -result="" -until [[ "$result" == *"Alive"* ]]; do - result=$(curl http://127.0.0.1:5050/is_alive --silent || true) - sleep 1 -done - -echo Devnet ready. diff --git a/src/lib.cairo b/src/lib.cairo new file mode 100644 index 00000000..a3e6177d --- /dev/null +++ b/src/lib.cairo @@ -0,0 +1,19 @@ +mod multisig { + mod argent_multisig; + mod interface; + + // Structures + + mod signer_signature; +} + +mod library { + mod account; + mod array_ext; + mod asserts; + mod calls; + mod erc165; + mod outside_execution; + mod upgrade; + mod version; +} diff --git a/contracts/lib/README.md b/src/library/README.md similarity index 100% rename from contracts/lib/README.md rename to src/library/README.md diff --git a/contracts/lib/src/account.cairo b/src/library/account.cairo similarity index 52% rename from contracts/lib/src/account.cairo rename to src/library/account.cairo index 93993759..da0f359a 100644 --- a/contracts/lib/src/account.cairo +++ b/src/library/account.cairo @@ -4,12 +4,6 @@ const ERC165_ACCOUNT_INTERFACE_ID: felt252 = 0x2ceccef7f994940b3962a6c67e0ba4fcd37df7d131417c604f91e03caecc1cd; const ERC165_ACCOUNT_INTERFACE_ID_OLD_1: felt252 = 0xa66bd575; const ERC165_ACCOUNT_INTERFACE_ID_OLD_2: felt252 = 0x3943f10f; - // InterfaceID: 0x2ceccef7f994940b3962a6c67e0ba4fcd37df7d131417c604f91e03caecc1cd -trait IAccount<TContractState> { - fn __validate__(ref self: TContractState, calls: Array<Call>) -> felt252; - fn __execute__(ref self: TContractState, calls: Array<Call>) -> Array<Span<felt252>>; - fn is_valid_signature( - self: @TContractState, hash: felt252, signature: Array<felt252> - ) -> felt252; -} + + diff --git a/contracts/lib/src/array_ext.cairo b/src/library/array_ext.cairo similarity index 100% rename from contracts/lib/src/array_ext.cairo rename to src/library/array_ext.cairo diff --git a/contracts/lib/src/asserts.cairo b/src/library/asserts.cairo similarity index 100% rename from contracts/lib/src/asserts.cairo rename to src/library/asserts.cairo index d3b6ea85..e6737183 100644 --- a/contracts/lib/src/asserts.cairo +++ b/src/library/asserts.cairo @@ -1,7 +1,7 @@ use array::SpanTrait; -use zeroable::Zeroable; use starknet::{get_contract_address, get_caller_address, ContractAddress, account::Call}; +use zeroable::Zeroable; const TRANSACTION_VERSION: felt252 = 1; const QUERY_VERSION: felt252 = diff --git a/contracts/lib/cairo_project.toml b/src/library/cairo_project.toml similarity index 100% rename from contracts/lib/cairo_project.toml rename to src/library/cairo_project.toml diff --git a/contracts/lib/src/calls.cairo b/src/library/calls.cairo similarity index 95% rename from contracts/lib/src/calls.cairo rename to src/library/calls.cairo index efa18ca0..d86a9425 100644 --- a/contracts/lib/src/calls.cairo +++ b/src/library/calls.cairo @@ -1,8 +1,7 @@ +use argent::library::array_ext::ArrayExtTrait; use array::{ArrayTrait, SpanTrait}; use starknet::{call_contract_syscall, account::Call}; -use lib::ArrayExtTrait; - fn execute_multicall(mut calls: Span<Call>) -> Array<Span<felt252>> { let mut result: Array<Span<felt252>> = ArrayTrait::new(); let mut idx = 0; diff --git a/contracts/lib/src/erc165.cairo b/src/library/erc165.cairo similarity index 100% rename from contracts/lib/src/erc165.cairo rename to src/library/erc165.cairo diff --git a/contracts/lib/src/outside_execution.cairo b/src/library/outside_execution.cairo similarity index 100% rename from contracts/lib/src/outside_execution.cairo rename to src/library/outside_execution.cairo index 1fde2259..b9dc78de 100644 --- a/contracts/lib/src/outside_execution.cairo +++ b/src/library/outside_execution.cairo @@ -1,8 +1,8 @@ use array::{ArrayTrait, SpanTrait}; use box::BoxTrait; use hash::pedersen; -use traits::Into; use starknet::{ContractAddress, get_tx_info, get_contract_address, account::Call}; +use traits::Into; const ERC165_OUTSIDE_EXECUTION_INTERFACE_ID: felt252 = 0x68cfd18b92d1907b8ba3cc324900277f5a3622099431ea85dd8089255e4181; diff --git a/contracts/lib/src/upgrade.cairo b/src/library/upgrade.cairo similarity index 100% rename from contracts/lib/src/upgrade.cairo rename to src/library/upgrade.cairo diff --git a/contracts/lib/src/version.cairo b/src/library/version.cairo similarity index 100% rename from contracts/lib/src/version.cairo rename to src/library/version.cairo diff --git a/contracts/multisig/src/argent_multisig.cairo b/src/multisig/argent_multisig.cairo similarity index 87% rename from contracts/multisig/src/argent_multisig.cairo rename to src/multisig/argent_multisig.cairo index 10a36d35..e4a3bbdb 100644 --- a/contracts/multisig/src/argent_multisig.cairo +++ b/src/multisig/argent_multisig.cairo @@ -1,28 +1,50 @@ -use multisig::IArgentMultisig; // For some reason (fn colliding with same name) I have to import it here and use super - #[starknet::contract] mod ArgentMultisig { + trait IAccount<TContractState> { + fn __validate__(ref self: TContractState, calls: Array<Call>) -> felt252; + fn __execute__(ref self: TContractState, calls: Array<Call>) -> Array<Span<felt252>>; + fn is_valid_signature( + self: @TContractState, hash: felt252, signature: Array<felt252> + ) -> felt252; + } + + use argent::multisig::interface::IArgentMultisig; // For some reason (fn colliding with same name) I have to import it here and use super + + + use argent::library::account::{ + ERC165_ACCOUNT_INTERFACE_ID, ERC165_ACCOUNT_INTERFACE_ID_OLD_1, + ERC165_ACCOUNT_INTERFACE_ID_OLD_2 + }; + use argent::library::array_ext::ArrayExtTrait; + use argent::library::asserts::{ + assert_only_self, assert_no_self_call, assert_caller_is_null, assert_correct_tx_version, + assert_correct_declare_version + }; + use argent::library::calls::execute_multicall; + use argent::library::erc165::{ + IErc165, IErc165LibraryDispatcher, IErc165DispatcherTrait, ERC165_IERC165_INTERFACE_ID, + ERC165_IERC165_INTERFACE_ID_OLD + }; + use argent::library::outside_execution::{ + OutsideExecution, hash_outside_execution_message, IOutsideExecution, + ERC165_OUTSIDE_EXECUTION_INTERFACE_ID + }; + use argent::library::upgrade::{ + IUpgradeable, IUpgradeableLibraryDispatcher, IUpgradeableDispatcherTrait + }; + use argent::library::version::Version; + use argent::multisig::interface::IDeprecatedArgentMultisig; + use argent::multisig::signer_signature::deserialize_array_signer_signature; use array::{ArrayTrait, SpanTrait}; use box::BoxTrait; use ecdsa::check_ecdsa_signature; use option::OptionTrait; - use traits::Into; use starknet::{ get_contract_address, ContractAddressIntoFelt252, VALIDATED, - syscalls::replace_class_syscall, ClassHash, class_hash_const, get_block_timestamp, - get_caller_address, get_tx_info, account::Call - }; - - use lib::{ - IAccount, assert_only_self, assert_no_self_call, assert_correct_tx_version, - assert_caller_is_null, execute_multicall, Version, IErc165LibraryDispatcher, - IErc165DispatcherTrait, OutsideExecution, hash_outside_execution_message, - ERC165_IERC165_INTERFACE_ID, ERC165_ACCOUNT_INTERFACE_ID, ERC165_IERC165_INTERFACE_ID_OLD, - ERC165_ACCOUNT_INTERFACE_ID_OLD_1, ERC165_ACCOUNT_INTERFACE_ID_OLD_2, IUpgradeable, - IUpgradeableLibraryDispatcher, IUpgradeableDispatcherTrait, IOutsideExecution, - ERC165_OUTSIDE_EXECUTION_INTERFACE_ID, IErc165, + syscalls::{SyscallResult, replace_class_syscall}, ClassHash, class_hash_const, + get_block_timestamp, get_caller_address, get_tx_info, account::Call }; - use multisig::{deserialize_array_signer_signature, IDeprecatedArgentMultisig}; + use traits::Into; const EXECUTE_AFTER_UPGRADE_SELECTOR: felt252 = 738349667340360233096752603318170676063569407717437256101137432051386874767; // starknet_keccak('execute_after_upgrade') @@ -97,7 +119,7 @@ mod ArgentMultisig { let new_signers_count = signers.len(); assert_valid_threshold_and_signers_count(new_threshold, new_signers_count); - self.add_signers(signers.span(), last_signer: 0); + MultisigStorage::add_signers(ref self, signers.span(), last_signer: 0); self.threshold.write(new_threshold); self.emit(ThresholdUpdated { new_threshold }); @@ -231,7 +253,7 @@ mod ArgentMultisig { } #[external(v0)] - impl ArgentMultisigImpl of super::IArgentMultisig<ContractState> { + impl ArgentMultisigImpl of IArgentMultisig<ContractState> { fn __validate_declare__(self: @ContractState, class_hash: felt252) -> felt252 { panic_with_felt252('argent/declare-not-available') // Not implemented yet } @@ -251,13 +273,13 @@ mod ArgentMultisig { assert(parsed_signatures.len() == 1, 'argent/invalid-signature-length'); let signer_sig = *parsed_signatures.at(0); - let valid_signer_signature = self - .is_valid_signer_signature( - tx_info.transaction_hash, - signer_sig.signer, - signer_sig.signature_r, - signer_sig.signature_s - ); + let valid_signer_signature = ArgentMultisigImpl::is_valid_signer_signature( + self, + tx_info.transaction_hash, + signer_sig.signer, + signer_sig.signature_r, + signer_sig.signature_s + ); assert(valid_signer_signature, 'argent/invalid-signature'); VALIDATED } @@ -281,7 +303,7 @@ mod ArgentMultisig { let new_signers_count = signers_len + signers_to_add.len(); assert_valid_threshold_and_signers_count(new_threshold, new_signers_count); - self.add_signers(signers_to_add.span(), last_signer); + MultisigStorage::add_signers(ref self, signers_to_add.span(), last_signer); self.threshold.write(new_threshold); if previous_threshold != new_threshold { @@ -311,7 +333,7 @@ mod ArgentMultisig { let new_signers_count = signers_len - signers_to_remove.len(); assert_valid_threshold_and_signers_count(new_threshold, new_signers_count); - self.remove_signers(signers_to_remove.span(), last_signer); + MultisigStorage::remove_signers(ref self, signers_to_remove.span(), last_signer); self.threshold.write(new_threshold); if previous_threshold != new_threshold { @@ -337,7 +359,7 @@ mod ArgentMultisig { assert_only_self(); let (new_signers_count, last_signer) = self.load(); - self.replace_signer(signer_to_remove, signer_to_add, last_signer); + MultisigStorage::replace_signer(ref self, signer_to_remove, signer_to_add, last_signer); self.emit(OwnerRemoved { removed_owner_guid: signer_to_remove }); self.emit(OwnerAdded { new_owner_guid: signer_to_add }); @@ -357,11 +379,11 @@ mod ArgentMultisig { } fn get_signers(self: @ContractState) -> Array<felt252> { - self.get_signers() + MultisigStorage::get_signers(self) } fn is_signer(self: @ContractState, signer: felt252) -> bool { - self.is_signer(signer) + MultisigStorage::is_signer(self, signer) } fn is_valid_signer_signature( @@ -371,7 +393,7 @@ mod ArgentMultisig { signature_r: felt252, signature_s: felt252 ) -> bool { - self.is_valid_signer_signature(hash, signer, signature_r, signature_s) + Private::is_valid_signer_signature(self, hash, signer, signature_r, signature_s) } } @@ -398,7 +420,7 @@ mod ArgentMultisig { #[external(v0)] impl OldArgentMultisigImpl< - impl ArgentMultisig: super::IArgentMultisig<ContractState>, + impl ArgentMultisig: IArgentMultisig<ContractState>, impl Erc165: IErc165<ContractState>, impl Account: IAccount<ContractState>, > of IDeprecatedArgentMultisig<ContractState> { @@ -476,13 +498,13 @@ mod ArgentMultisig { let signer_sig = *signer_sig_ref; let signer_uint: u256 = signer_sig.signer.into(); assert(signer_uint > last_signer, 'argent/signatures-not-sorted'); - let is_valid = self - .is_valid_signer_signature( - hash, - signer: signer_sig.signer, - signature_r: signer_sig.signature_r, - signature_s: signer_sig.signature_s, - ); + let is_valid = Private::is_valid_signer_signature( + self, + hash, + signer: signer_sig.signer, + signature_r: signer_sig.signature_r, + signature_s: signer_sig.signature_s, + ); if !is_valid { break false; } @@ -502,7 +524,7 @@ mod ArgentMultisig { signature_r: felt252, signature_s: felt252 ) -> bool { - let is_signer = self.is_signer(signer); + let is_signer = MultisigStorage::is_signer(self, signer); assert(is_signer, 'argent/not-a-signer'); check_ecdsa_signature(hash, signer, signature_r, signature_s) } @@ -591,7 +613,7 @@ mod ArgentMultisig { // Signers are added at the end of the list self.signer_list.write(last_signer, signer); - self.add_signers(signers_to_add, last_signer: signer); + MultisigStorage::add_signers(ref self, signers_to_add, last_signer: signer); }, Option::None(()) => (), } @@ -615,11 +637,13 @@ mod ArgentMultisig { if next_signer == 0 { // Removing the last item - self.remove_signers(signers_to_remove, last_signer: previous_signer); + MultisigStorage::remove_signers( + ref self, signers_to_remove, last_signer: previous_signer + ); } else { // Removing an item in the middle self.signer_list.write(signer, 0); - self.remove_signers(signers_to_remove, last_signer); + MultisigStorage::remove_signers(ref self, signers_to_remove, last_signer); } }, Option::None(()) => (), diff --git a/contracts/multisig/src/interface.cairo b/src/multisig/interface.cairo similarity index 98% rename from contracts/multisig/src/interface.cairo rename to src/multisig/interface.cairo index 811ed70a..9e7e3e49 100644 --- a/contracts/multisig/src/interface.cairo +++ b/src/multisig/interface.cairo @@ -1,4 +1,4 @@ -use lib::Version; +use argent::library::version::Version; #[starknet::interface] trait IArgentMultisig<TContractState> { diff --git a/contracts/multisig/src/signer_signature.cairo b/src/multisig/signer_signature.cairo similarity index 81% rename from contracts/multisig/src/signer_signature.cairo rename to src/multisig/signer_signature.cairo index fd566f4c..55438079 100644 --- a/contracts/multisig/src/signer_signature.cairo +++ b/src/multisig/signer_signature.cairo @@ -1,3 +1,4 @@ +use core::option::OptionTrait; use array::{ArrayTrait, SpanTrait}; use serde::Serde; @@ -16,6 +17,6 @@ fn deserialize_array_signer_signature( if serialized.len() == 0 { break Option::Some(output.span()); } - output.append(Serde::deserialize(ref serialized)?); + output.append(Serde::deserialize(ref serialized).unwrap()); } }