From 65fbf7ddc2697ec919b8df3c843d889d990036da Mon Sep 17 00:00:00 2001 From: Elias Tazartes <66871571+Eikix@users.noreply.github.com> Date: Tue, 17 Oct 2023 10:54:53 +0700 Subject: [PATCH] Feat/balance (#427) * gc -m "feat: implement eoa balance" * feat: add test * feat: update internal of exec_balance * refactor: refactoring balance to not call * refactor: delete kakarot_core_native_token * chore: fmt * refactor: put openzeppelin into its own crate * refactor: rename core contracts * refactor: clean up and implement comments from pr review * refactor: use contract_address_const more * Update crates/evm/src/instructions/environmental_information.cairo Co-authored-by: Mathieu <60658558+enitrat@users.noreply.github.com> * Update crates/evm/src/instructions/environmental_information.cairo Co-authored-by: Mathieu <60658558+enitrat@users.noreply.github.com> * fix: fix pr comment --------- Co-authored-by: Mathieu <60658558+enitrat@users.noreply.github.com> --- .trunk/trunk.yaml | 2 +- .../{core_contracts => contracts}/Scarb.toml | 3 +- .../src/components.cairo | 0 .../src/components/ownable.cairo | 0 .../src/components/upgradeable.cairo | 0 crates/contracts/src/kakarot_core.cairo | 4 + .../src/kakarot_core/interface.cairo | 137 ++++++++++ .../src/kakarot_core/kakarot.cairo} | 166 ++--------- .../src/lib.cairo | 0 .../src/tests.cairo | 0 .../src/tests/test_kakarot_core.cairo | 29 +- .../src/tests/test_ownable.cairo | 8 +- .../src/tests/test_upgradeable.cairo | 0 .../src/tests/utils.cairo | 35 ++- crates/evm/Scarb.toml | 3 +- .../environmental_information.cairo | 40 ++- crates/evm/src/storage.cairo | 15 +- .../test_environment_information.cairo | 78 +++++- crates/evm/src/tests/test_utils.cairo | 11 +- crates/openzeppelin/Scarb.toml | 23 ++ crates/openzeppelin/src/lib.cairo | 1 + crates/openzeppelin/src/token.cairo | 1 + crates/openzeppelin/src/token/erc20.cairo | 6 + .../openzeppelin/src/token/erc20/erc20.cairo | 258 ++++++++++++++++++ .../src/token/erc20/interface.cairo | 89 ++++++ crates/utils/src/constants.cairo | 1 + 26 files changed, 722 insertions(+), 188 deletions(-) rename crates/{core_contracts => contracts}/Scarb.toml (79%) rename crates/{core_contracts => contracts}/src/components.cairo (100%) rename crates/{core_contracts => contracts}/src/components/ownable.cairo (100%) rename crates/{core_contracts => contracts}/src/components/upgradeable.cairo (100%) create mode 100644 crates/contracts/src/kakarot_core.cairo create mode 100644 crates/contracts/src/kakarot_core/interface.cairo rename crates/{core_contracts/src/kakarot_core.cairo => contracts/src/kakarot_core/kakarot.cairo} (63%) rename crates/{core_contracts => contracts}/src/lib.cairo (100%) rename crates/{core_contracts => contracts}/src/tests.cairo (100%) rename crates/{core_contracts => contracts}/src/tests/test_kakarot_core.cairo (80%) rename crates/{core_contracts => contracts}/src/tests/test_ownable.cairo (95%) rename crates/{core_contracts => contracts}/src/tests/test_upgradeable.cairo (100%) rename crates/{core_contracts => contracts}/src/tests/utils.cairo (62%) create mode 100644 crates/openzeppelin/Scarb.toml create mode 100644 crates/openzeppelin/src/lib.cairo create mode 100644 crates/openzeppelin/src/token.cairo create mode 100644 crates/openzeppelin/src/token/erc20.cairo create mode 100644 crates/openzeppelin/src/token/erc20/erc20.cairo create mode 100644 crates/openzeppelin/src/token/erc20/interface.cairo diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index ba8edb79b..9c3d2d447 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -2,7 +2,7 @@ # To learn more about the format of this file, see https://docs.trunk.io/reference/trunk-yaml version: 0.1 cli: - version: 1.16.2 + version: 1.17.0 plugins: sources: - id: trunk diff --git a/crates/core_contracts/Scarb.toml b/crates/contracts/Scarb.toml similarity index 79% rename from crates/core_contracts/Scarb.toml rename to crates/contracts/Scarb.toml index 02f39828c..6653e8dc2 100644 --- a/crates/core_contracts/Scarb.toml +++ b/crates/contracts/Scarb.toml @@ -1,5 +1,5 @@ [package] -name = "core_contracts" +name = "contracts" version = "0.1.0" # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html @@ -7,6 +7,7 @@ version = "0.1.0" [dependencies] starknet.workspace = true evm = { path = "../evm" } +openzeppelin = { path = "../openzeppelin" } [tool.fmt] sort-module-level-items = true diff --git a/crates/core_contracts/src/components.cairo b/crates/contracts/src/components.cairo similarity index 100% rename from crates/core_contracts/src/components.cairo rename to crates/contracts/src/components.cairo diff --git a/crates/core_contracts/src/components/ownable.cairo b/crates/contracts/src/components/ownable.cairo similarity index 100% rename from crates/core_contracts/src/components/ownable.cairo rename to crates/contracts/src/components/ownable.cairo diff --git a/crates/core_contracts/src/components/upgradeable.cairo b/crates/contracts/src/components/upgradeable.cairo similarity index 100% rename from crates/core_contracts/src/components/upgradeable.cairo rename to crates/contracts/src/components/upgradeable.cairo diff --git a/crates/contracts/src/kakarot_core.cairo b/crates/contracts/src/kakarot_core.cairo new file mode 100644 index 000000000..875c99008 --- /dev/null +++ b/crates/contracts/src/kakarot_core.cairo @@ -0,0 +1,4 @@ +mod interface; +mod kakarot; + +use kakarot::{KakarotCore, ContractAccountStorage}; diff --git a/crates/contracts/src/kakarot_core/interface.cairo b/crates/contracts/src/kakarot_core/interface.cairo new file mode 100644 index 000000000..1975891d1 --- /dev/null +++ b/crates/contracts/src/kakarot_core/interface.cairo @@ -0,0 +1,137 @@ +use contracts::kakarot_core::ContractAccountStorage; +use starknet::{ContractAddress, EthAddress, ClassHash}; + +#[starknet::interface] +trait IKakarotCore { + /// Sets the native token, this token will be considered the native coin in the Ethereum sense + fn set_native_token(ref self: TContractState, native_token: ContractAddress); + + /// Gets the native token used by the Kakarot smart contract + fn native_token(self: @TContractState) -> ContractAddress; + + /// Sets the deploy fee for an EOA + /// Currently, the Kakarot RPC can trigger an EOA deployment, + /// and optimistically fund it. + /// Then, the KakarotCore smart contract is able to levy this fee retroactively from the EOA + /// And reimburse the RPC's smart wallet. + fn set_deploy_fee(ref self: TContractState, deploy_fee: u128); + + /// Get the deploy fee + fn deploy_fee(self: @TContractState) -> u128; + + /// Deterministically computes a Starknet address for an given EVM address + /// The address is computed as the Starknet address corresponding to the deployment of an EOA, + /// Using its EVM address as salt, and KakarotCore as deployer. + fn compute_starknet_address(self: @TContractState, evm_address: EthAddress) -> ContractAddress; + + /// Checks into KakarotCore storage if an EOA has been deployed for a + /// particular EVM address and if so, returns its corresponding Starknet Address. + /// Otherwise, returns 0 + fn eoa_starknet_address(self: @TContractState, evm_address: EthAddress) -> ContractAddress; + + /// Gets the storage associated to a contract account + fn contract_account_storage( + self: @TContractState, evm_address: EthAddress + ) -> ContractAccountStorage; + + /// Deploys an EOA for a particular EVM address + fn deploy_eoa(ref self: TContractState, evm_address: EthAddress) -> ContractAddress; + + /// View entrypoint into the EVM + /// Performs view calls into the blockchain + /// It cannot modify the state of the chain + fn eth_call( + self: @TContractState, + from: EthAddress, + to: EthAddress, + gas_limit: u128, + gas_price: u128, + value: u128, + data: Span + ) -> Span; + + /// Transaction entrypoint into the EVM + /// Executes an EVM transaction and possibly modifies the state + fn eth_send_transaction( + ref self: TContractState, + to: EthAddress, + gas_limit: u128, + gas_price: u128, + value: u128, + data: Span + ) -> Span; + + /// Upgrade the KakarotCore smart contract + /// Using replace_class_syscall + fn upgrade(ref self: TContractState, new_class_hash: ClassHash); +} + +#[starknet::interface] +trait IExtendedKakarotCore { + /// Sets the native token, this token will be considered the native coin in the Ethereum sense + fn set_native_token(ref self: TContractState, native_token: ContractAddress); + + /// Gets the native token used by the Kakarot smart contract + fn native_token(self: @TContractState) -> ContractAddress; + + /// Sets the deploy fee for an EOA + /// Currently, the Kakarot RPC can trigger an EOA deployment, + /// and optimistically fund it. + /// Then, the KakarotCore smart contract is able to levy this fee retroactively from the EOA + /// And reimburse the RPC's smart wallet. + fn set_deploy_fee(ref self: TContractState, deploy_fee: u128); + + /// Get the deploy fee + fn deploy_fee(self: @TContractState) -> u128; + + /// Deterministically computes a Starknet address for an given EVM address + /// The address is computed as the Starknet address corresponding to the deployment of an EOA, + /// Using its EVM address as salt, and KakarotCore as deployer. + fn compute_starknet_address(self: @TContractState, evm_address: EthAddress) -> ContractAddress; + + /// Checks into KakarotCore storage if an EOA has been deployed for a + /// particular EVM address and if so, returns its corresponding Starknet Address + fn eoa_starknet_address(self: @TContractState, evm_address: EthAddress) -> ContractAddress; + + + /// Gets the storage associated to a contract account + fn contract_account_storage( + self: @TContractState, evm_address: EthAddress + ) -> ContractAccountStorage; + + /// Deploys an EOA for a particular EVM address + fn deploy_eoa(ref self: TContractState, evm_address: EthAddress) -> ContractAddress; + + /// View entrypoint into the EVM + /// Performs view calls into the blockchain + /// It cannot modify the state of the chain + fn eth_call( + self: @TContractState, + from: EthAddress, + to: EthAddress, + gas_limit: u128, + gas_price: u128, + value: u128, + data: Span + ) -> Span; + + /// Transaction entrypoint into the EVM + /// Executes an EVM transaction and possibly modifies the state + fn eth_send_transaction( + ref self: TContractState, + to: EthAddress, + gas_limit: u128, + gas_price: u128, + value: u128, + data: Span + ) -> Span; + + /// Upgrade the KakarotCore smart contract + /// Using replace_class_syscall + fn upgrade(ref self: TContractState, new_class_hash: ClassHash); + + fn owner(self: @TContractState) -> ContractAddress; + fn transfer_ownership(ref self: TContractState, new_owner: ContractAddress); + fn renounce_ownership(ref self: TContractState); +} + diff --git a/crates/core_contracts/src/kakarot_core.cairo b/crates/contracts/src/kakarot_core/kakarot.cairo similarity index 63% rename from crates/core_contracts/src/kakarot_core.cairo rename to crates/contracts/src/kakarot_core/kakarot.cairo index 92fba5b19..a68d16d42 100644 --- a/crates/core_contracts/src/kakarot_core.cairo +++ b/crates/contracts/src/kakarot_core/kakarot.cairo @@ -2,80 +2,34 @@ use starknet::{ContractAddress, EthAddress, ClassHash}; const INVOKE_ETH_CALL_FORBIDDEN: felt252 = 'KKT: Cannot invoke eth_call'; -#[starknet::interface] -trait IKakarotCore { - /// Sets the native token, this token will be considered the native coin in the Ethereum sense - fn set_native_token(ref self: TContractState, native_token: ContractAddress); - - /// Gets the native token used by the Kakarot smart contract - fn native_token(self: @TContractState) -> ContractAddress; - - /// Sets the deploy fee for an EOA - /// Currently, the Kakarot RPC can trigger an EOA deployment, - /// and optimistically fund it. - /// Then, the KakarotCore smart contract is able to levy this fee retroactively from the EOA - /// And reimburse the RPC's smart wallet. - fn set_deploy_fee(ref self: TContractState, deploy_fee: u128); - - /// Get the deploy fee - fn deploy_fee(self: @TContractState) -> u128; - - /// Deterministically computes a Starknet address for an given EVM address - /// The address is computed as the Starknet address corresponding to the deployment of an EOA, - /// Using its EVM address as salt, and KakarotCore as deployer. - fn compute_starknet_address(self: @TContractState, evm_address: EthAddress) -> ContractAddress; - - /// Checks into KakarotCore storage if an EOA has been deployed for a - /// particular EVM address and if so, returns its corresponding Starknet Address. - /// Otherwise, returns 0 - fn get_eoa_starknet_address(self: @TContractState, evm_address: EthAddress) -> ContractAddress; - - /// Deploys an EOA for a particular EVM address - fn deploy_eoa(ref self: TContractState, evm_address: EthAddress) -> ContractAddress; - - /// View entrypoint into the EVM - /// Performs view calls into the blockchain - /// It cannot modify the state of the chain - fn eth_call( - self: @TContractState, - from: EthAddress, - to: EthAddress, - gas_limit: u128, - gas_price: u128, - value: u128, - data: Span - ) -> Span; - - /// Transaction entrypoint into the EVM - /// Executes an EVM transaction and possibly modifies the state - fn eth_send_transaction( - ref self: TContractState, - to: EthAddress, - gas_limit: u128, - gas_price: u128, - value: u128, - data: Span - ) -> Span; - - /// Upgrade the KakarotCore smart contract - /// Using replace_class_syscall - fn upgrade(ref self: TContractState, new_class_hash: ClassHash); + +#[derive(Copy, Drop, Serde, starknet::Store)] +struct ContractAccountStorage { + nonce: u64, + balance: u256, +// TODO: add bytecode as a field for ContractAccountStorage +// bytecode: List + +//TODO: add valid jumps as a field for ContractAccountStorage +// valid_jumps: LegacyMap } #[starknet::contract] mod KakarotCore { + use contracts::components::ownable::ownable_component::InternalTrait; + use contracts::components::ownable::{ownable_component}; + use contracts::kakarot_core::interface::IKakarotCore; + use contracts::kakarot_core::interface; use core::hash::{HashStateExTrait, HashStateTrait}; use core::pedersen::{HashState, PedersenTrait}; use core::starknet::SyscallResultTrait; use core::zeroable::Zeroable; - use core_contracts::components::ownable::ownable_component::InternalTrait; - use core_contracts::components::ownable::{ownable_component}; use evm::errors::EVMError; - use evm::storage::ContractAccountStorage; use starknet::{ EthAddress, ContractAddress, ClassHash, get_tx_info, get_contract_address, deploy_syscall }; + use super::ContractAccountStorage; use super::INVOKE_ETH_CALL_FORBIDDEN; use utils::constants::{CONTRACT_ADDRESS_PREFIX, MAX_ADDRESS}; use utils::traits::U256TryIntoContractAddress; @@ -90,20 +44,19 @@ mod KakarotCore { #[storage] struct Storage { /// Kakarot storage for accounts: Externally Owned Accounts (EOA) and Contract Accounts (CA) - /// EOAs: - /// Map their EVM address and their Starknet address - /// - starknet_address: the deterministic starknet address (31 bytes) computed given an EVM address (20 bytes) - /// /// CAs: /// Map EVM address of a CA and the corresponding Kakarot Core storage -> /// - nonce (note that this nonce is not the same as the Starknet protocol nonce) /// - current balance in native token (CAs can use this balance as an allowance to spend native Starknet token through Kakarot Core) /// - bytecode of the CA + /// Storage of CAs in EVM is defined as a mapping of key (bytes32) - value (bytes32) pairs + /// + /// EOAs: + /// Map their EVM address and their Starknet address + /// - starknet_address: the deterministic starknet address (31 bytes) computed given an EVM address (20 bytes) + contract_account_storage: LegacyMap::, eoa_address_registry: LegacyMap::, eoa_class_hash: ClassHash, - /// Storage of CAs in EVM is defined as a mapping of key (bytes32) - value (bytes32) pairs - contract_account_storage: LegacyMap<(EthAddress, u256), u256>, - contract_account_registry: LegacyMap::, // Utility storage native_token: ContractAddress, deploy_fee: u128, @@ -145,7 +98,7 @@ mod KakarotCore { } #[external(v0)] - impl KakarotCoreImpl of super::IKakarotCore { + impl KakarotCoreImpl of interface::IKakarotCore { fn set_native_token(ref self: ContractState, native_token: ContractAddress) { self.ownable.assert_only_owner(); self.native_token.write(native_token); @@ -213,12 +166,17 @@ mod KakarotCore { /// Checks into KakarotCore storage if an EOA has been deployed for a /// particular EVM address and if so, returns its corresponding Starknet Address /// Otherwise, returns 0 - fn get_eoa_starknet_address( - self: @ContractState, evm_address: EthAddress - ) -> ContractAddress { + fn eoa_starknet_address(self: @ContractState, evm_address: EthAddress) -> ContractAddress { self.eoa_address_registry.read(evm_address) } + /// Gets the storage associated to a contract account + fn contract_account_storage( + self: @ContractState, evm_address: EthAddress + ) -> ContractAccountStorage { + self.contract_account_storage.read(evm_address) + } + /// Deploys an EOA for a particular EVM address fn deploy_eoa(ref self: ContractState, evm_address: EthAddress) -> ContractAddress { // First let's check that the EOA is not already deployed @@ -312,67 +270,3 @@ mod KakarotCore { } } - -#[starknet::interface] -trait IExtendedKakarotCore { - /// Sets the native token, this token will be considered the native coin in the Ethereum sense - fn set_native_token(ref self: TContractState, native_token: ContractAddress); - - /// Gets the native token used by the Kakarot smart contract - fn native_token(self: @TContractState) -> ContractAddress; - - /// Sets the deploy fee for an EOA - /// Currently, the Kakarot RPC can trigger an EOA deployment, - /// and optimistically fund it. - /// Then, the KakarotCore smart contract is able to levy this fee retroactively from the EOA - /// And reimburse the RPC's smart wallet. - fn set_deploy_fee(ref self: TContractState, deploy_fee: u128); - - /// Get the deploy fee - fn deploy_fee(self: @TContractState) -> u128; - - /// Deterministically computes a Starknet address for an given EVM address - /// The address is computed as the Starknet address corresponding to the deployment of an EOA, - /// Using its EVM address as salt, and KakarotCore as deployer. - fn compute_starknet_address(self: @TContractState, evm_address: EthAddress) -> ContractAddress; - - /// Checks into KakarotCore storage if an EOA has been deployed for a - /// particular EVM address and if so, returns its corresponding Starknet Address - fn get_eoa_starknet_address(self: @TContractState, evm_address: EthAddress) -> ContractAddress; - - /// Deploys an EOA for a particular EVM address - fn deploy_eoa(ref self: TContractState, evm_address: EthAddress) -> ContractAddress; - - /// View entrypoint into the EVM - /// Performs view calls into the blockchain - /// It cannot modify the state of the chain - fn eth_call( - self: @TContractState, - from: EthAddress, - to: EthAddress, - gas_limit: u128, - gas_price: u128, - value: u128, - data: Span - ) -> Span; - - /// Transaction entrypoint into the EVM - /// Executes an EVM transaction and possibly modifies the state - fn eth_send_transaction( - ref self: TContractState, - to: EthAddress, - gas_limit: u128, - gas_price: u128, - value: u128, - data: Span - ) -> Span; - - /// Upgrade the KakarotCore smart contract - /// Using replace_class_syscall - fn upgrade(ref self: TContractState, new_class_hash: ClassHash); - - fn owner(self: @TContractState) -> ContractAddress; - fn transfer_ownership(ref self: TContractState, new_owner: ContractAddress); - fn renounce_ownership(ref self: TContractState); -} - diff --git a/crates/core_contracts/src/lib.cairo b/crates/contracts/src/lib.cairo similarity index 100% rename from crates/core_contracts/src/lib.cairo rename to crates/contracts/src/lib.cairo diff --git a/crates/core_contracts/src/tests.cairo b/crates/contracts/src/tests.cairo similarity index 100% rename from crates/core_contracts/src/tests.cairo rename to crates/contracts/src/tests.cairo diff --git a/crates/core_contracts/src/tests/test_kakarot_core.cairo b/crates/contracts/src/tests/test_kakarot_core.cairo similarity index 80% rename from crates/core_contracts/src/tests/test_kakarot_core.cairo rename to crates/contracts/src/tests/test_kakarot_core.cairo index 02fcba37c..d7f992d5e 100644 --- a/crates/core_contracts/src/tests/test_kakarot_core.cairo +++ b/crates/contracts/src/tests/test_kakarot_core.cairo @@ -1,6 +1,6 @@ -use core_contracts::components::ownable::ownable_component; -use core_contracts::kakarot_core::{IExtendedKakarotCoreDispatcherImpl, KakarotCore}; -use core_contracts::tests::utils; +use contracts::components::ownable::ownable_component; +use contracts::kakarot_core::{interface::IExtendedKakarotCoreDispatcherImpl, KakarotCore}; +use contracts::tests::utils; use debug::PrintTrait; use eoa::externally_owned_account::ExternallyOwnedAccount; use evm::tests::test_utils; @@ -9,7 +9,7 @@ use starknet::{get_caller_address, testing, contract_address_const, ContractAddr #[test] #[available_gas(20000000)] fn test_kakarot_core_owner() { - let kakarot_core = utils::deploy_kakarot_core(); + let kakarot_core = utils::deploy_kakarot_core(test_utils::native_token()); assert(kakarot_core.owner() == utils::other_starknet_address(), 'wrong owner') } @@ -17,7 +17,7 @@ fn test_kakarot_core_owner() { #[test] #[available_gas(20000000)] fn test_kakarot_core_transfer_ownership() { - let kakarot_core = utils::deploy_kakarot_core(); + let kakarot_core = utils::deploy_kakarot_core(test_utils::native_token()); assert(kakarot_core.owner() == utils::other_starknet_address(), 'wrong owner'); testing::set_contract_address(utils::other_starknet_address()); kakarot_core.transfer_ownership(test_utils::starknet_address()); @@ -27,7 +27,7 @@ fn test_kakarot_core_transfer_ownership() { #[test] #[available_gas(20000000)] fn test_kakarot_core_renounce_ownership() { - let kakarot_core = utils::deploy_kakarot_core(); + let kakarot_core = utils::deploy_kakarot_core(test_utils::native_token()); assert(kakarot_core.owner() == utils::other_starknet_address(), 'wrong owner'); testing::set_contract_address(utils::other_starknet_address()); kakarot_core.renounce_ownership(); @@ -38,14 +38,14 @@ fn test_kakarot_core_renounce_ownership() { #[test] #[available_gas(20000000)] fn test_kakarot_core_deploy_fee() { - let kakarot_core = utils::deploy_kakarot_core(); + let kakarot_core = utils::deploy_kakarot_core(test_utils::native_token()); assert(kakarot_core.deploy_fee() == utils::deploy_fee(), 'wrong deploy_fee'); } #[test] #[available_gas(20000000)] fn test_kakarot_core_set_deploy_fee() { - let kakarot_core = utils::deploy_kakarot_core(); + let kakarot_core = utils::deploy_kakarot_core(test_utils::native_token()); assert(kakarot_core.deploy_fee() == utils::deploy_fee(), 'wrong deploy_fee'); testing::set_contract_address(utils::other_starknet_address()); kakarot_core.set_deploy_fee(0x100); @@ -53,17 +53,10 @@ fn test_kakarot_core_set_deploy_fee() { } -#[test] -#[available_gas(20000000)] -fn test_kakarot_core_native_token() { - let kakarot_core = utils::deploy_kakarot_core(); - assert(kakarot_core.native_token() == test_utils::native_token(), 'wrong native_token'); -} - #[test] #[available_gas(20000000)] fn test_kakarot_core_set_native_token() { - let kakarot_core = utils::deploy_kakarot_core(); + let kakarot_core = utils::deploy_kakarot_core(test_utils::native_token()); assert(kakarot_core.native_token() == test_utils::native_token(), 'wrong native_token'); testing::set_contract_address(test_utils::other_starknet_address()); @@ -76,7 +69,7 @@ fn test_kakarot_core_set_native_token() { #[test] #[available_gas(20000000)] fn test_kakarot_core_deploy_eoa() { - let kakarot_core = utils::deploy_kakarot_core(); + let kakarot_core = utils::deploy_kakarot_core(test_utils::native_token()); let eoa_starknet_address = kakarot_core.deploy_eoa(test_utils::evm_address()); // We drop the first event of Kakarot Core, as it is the initializer from Ownable, // triggerred in the constructor @@ -90,7 +83,7 @@ fn test_kakarot_core_deploy_eoa() { #[available_gas(20000000)] fn test_kakarot_core_compute_starknet_address() { let evm_address = test_utils::evm_address(); - let kakarot_core = utils::deploy_kakarot_core(); + let kakarot_core = utils::deploy_kakarot_core(test_utils::native_token()); // Precomputed Starknet address with starknet-rs and starknetjs // With arguments: diff --git a/crates/core_contracts/src/tests/test_ownable.cairo b/crates/contracts/src/tests/test_ownable.cairo similarity index 95% rename from crates/core_contracts/src/tests/test_ownable.cairo rename to crates/contracts/src/tests/test_ownable.cairo index e395dd6ac..59d079b52 100644 --- a/crates/core_contracts/src/tests/test_ownable.cairo +++ b/crates/contracts/src/tests/test_ownable.cairo @@ -1,6 +1,6 @@ -use core_contracts::components::ownable::{ownable_component}; -use core_contracts::tests::utils::constants::{ZERO, OWNER, OTHER}; -use core_contracts::tests::utils; +use contracts::components::ownable::{ownable_component}; +use contracts::tests::utils::constants::{ZERO, OWNER, OTHER}; +use contracts::tests::utils; use ownable_component::{InternalImpl, OwnableImpl}; @@ -10,7 +10,7 @@ use starknet::testing; #[starknet::contract] mod MockContract { - use core_contracts::components::ownable::{ownable_component}; + use contracts::components::ownable::{ownable_component}; component!(path: ownable_component, storage: ownable, event: OwnableEvent); diff --git a/crates/core_contracts/src/tests/test_upgradeable.cairo b/crates/contracts/src/tests/test_upgradeable.cairo similarity index 100% rename from crates/core_contracts/src/tests/test_upgradeable.cairo rename to crates/contracts/src/tests/test_upgradeable.cairo diff --git a/crates/core_contracts/src/tests/utils.cairo b/crates/contracts/src/tests/utils.cairo similarity index 62% rename from crates/core_contracts/src/tests/utils.cairo rename to crates/contracts/src/tests/utils.cairo index 5ad989342..7d6209166 100644 --- a/crates/core_contracts/src/tests/utils.cairo +++ b/crates/contracts/src/tests/utils.cairo @@ -1,6 +1,8 @@ -use core_contracts::kakarot_core::{IExtendedKakarotCoreDispatcher, KakarotCore}; +use contracts::kakarot_core::{interface::IExtendedKakarotCoreDispatcher, KakarotCore}; use eoa::externally_owned_account::{ExternallyOwnedAccount}; -use evm::tests::test_utils::{native_token, deploy_fee, other_starknet_address, chain_id}; +use evm::tests::test_utils::{deploy_fee, other_starknet_address, chain_id}; +use openzeppelin::token::erc20::ERC20; +use openzeppelin::token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait}; use starknet::{testing, contract_address_const, ContractAddress, deploy_syscall}; @@ -46,12 +48,27 @@ mod constants { fn OTHER() -> ContractAddress { contract_address_const::<0xe1145>() } + + fn ETH_BANK() -> ContractAddress { + contract_address_const::<0x777>() + } +} + +fn deploy_native_token() -> IERC20CamelDispatcher { + let calldata: Array = array![ + 'STARKNET_ETH', 'ETH', 0x00, 0xfffffffffffffffffffffffffff, constants::ETH_BANK().into() + ]; + let (contract_address, _) = deploy_syscall( + ERC20::TEST_CLASS_HASH.try_into().unwrap(), 0, calldata.span(), false + ) + .unwrap(); + IERC20CamelDispatcher { contract_address } } -fn deploy_kakarot_core() -> IExtendedKakarotCoreDispatcher { - let mut calldata: Array = array![ - native_token().into(), +fn deploy_kakarot_core(native_token: ContractAddress) -> IExtendedKakarotCoreDispatcher { + let calldata: Array = array![ + native_token.into(), deploy_fee().into(), ExternallyOwnedAccount::TEST_CLASS_HASH.try_into().unwrap(), other_starknet_address().into(), @@ -65,3 +82,11 @@ fn deploy_kakarot_core() -> IExtendedKakarotCoreDispatcher { IExtendedKakarotCoreDispatcher { contract_address } } + +fn fund_account_with_native_token( + contract_address: ContractAddress, native_token: IERC20CamelDispatcher +) { + let amount: u256 = 0x01; + testing::set_contract_address(constants::ETH_BANK()); + native_token.transfer(contract_address, amount); +} diff --git a/crates/evm/Scarb.toml b/crates/evm/Scarb.toml index 11451328a..00f291181 100644 --- a/crates/evm/Scarb.toml +++ b/crates/evm/Scarb.toml @@ -7,8 +7,9 @@ version = "0.1.0" [dependencies] starknet.workspace = true utils = { path = "../utils" } -core_contracts = { path = "../core_contracts" } +contracts = { path = "../contracts" } eoa = { path = "../eoa" } +openzeppelin = { path = "../openzeppelin" } [tool.fmt] sort-module-level-items = true diff --git a/crates/evm/src/instructions/environmental_information.cairo b/crates/evm/src/instructions/environmental_information.cairo index 0e7e0ca95..912c62e39 100644 --- a/crates/evm/src/instructions/environmental_information.cairo +++ b/crates/evm/src/instructions/environmental_information.cairo @@ -1,10 +1,15 @@ -//! Environmental Information. +use contracts::kakarot_core::interface::{IKakarotCore}; +use contracts::kakarot_core::{ContractAccountStorage, KakarotCore}; +use core::hash::{HashStateExTrait, HashStateTrait}; use evm::context::ExecutionContextTrait; -use evm::errors::{EVMError, RETURNDATA_OUT_OF_BOUNDS_ERROR}; +use evm::errors::{EVMError, RETURNDATA_OUT_OF_BOUNDS_ERROR, READ_SYSCALL_FAILED}; use evm::machine::{Machine, MachineCurrentContextTrait}; use evm::memory::MemoryTrait; use evm::stack::StackTrait; use integer::u32_overflowing_add; +use openzeppelin::token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait}; +use pedersen::{PedersenTrait, HashState}; +use starknet::{Store, storage_base_address_from_felt252, ContractAddress, get_contract_address}; use utils::helpers::{load_word}; use utils::traits::{EthAddressIntoU256}; @@ -21,7 +26,36 @@ impl EnvironmentInformationImpl of EnvironmentInformationTrait { /// Get ETH balance of the specified address. /// # Specification: https://www.evm.codes/#31?fork=shanghai fn exec_balance(ref self: Machine) -> Result<(), EVMError> { - Result::Ok(()) + let evm_address = self.stack.pop_eth_address()?; + + // Get access to Kakarot State locally + let kakarot_state = KakarotCore::unsafe_new_contract_state(); + + let eoa_starknet_address = kakarot_state.eoa_starknet_address(evm_address); + + // Case 1: EOA is deployed + // BALANCE is the EOA's native_token.balanceOf(eoa_starknet_address) + if !eoa_starknet_address.is_zero() { + let native_token_address = kakarot_state.native_token(); + // TODO: make sure this part of the codebase is upgradable + // As native_token might become a snake_case implementation + // instead of camelCase + let native_token = IERC20CamelDispatcher { contract_address: native_token_address }; + return self.stack.push(native_token.balanceOf(eoa_starknet_address)); + } + + // Case 2: EOA is not deployed and CA is deployed + // We check if a contract account is initialized at evm_address + // A good condition to check is nonce > 0, as deploying a contract account + // will set its nonce to 1 + let ca_storage = kakarot_state.contract_account_storage(evm_address); + if ca_storage.nonce != 0 { + return self.stack.push(ca_storage.balance); + } + + // Case 3: No EOA nor CA are deployed at `evm_address` + // Return 0 + return self.stack.push(0); } /// 0x32 - ORIGIN diff --git a/crates/evm/src/storage.cairo b/crates/evm/src/storage.cairo index f749af51d..adf3caab4 100644 --- a/crates/evm/src/storage.cairo +++ b/crates/evm/src/storage.cairo @@ -1,17 +1,8 @@ use hash::{HashStateTrait, HashStateExTrait}; use poseidon::PoseidonTrait; -use starknet::{ContractAddress, EthAddress, StorageBaseAddress, storage_base_address_from_felt252}; - -#[derive(Copy, Drop, Serde, starknet::Store)] -struct ContractAccountStorage { - nonce: u64, - balance: u256, -// TODO: add bytecode as a field for ContractAccountStorage -// bytecode: List - -//TODO: add valid jumps as a field for ContractAccountStorage -// valid_jumps: LegacyMap -} +use starknet::{ + ContractAddress, EthAddress, StorageBaseAddress, storage_base_address_from_felt252, Store +}; /// Computes the storage address for a given EVM address and an EVM storage key. diff --git a/crates/evm/src/tests/test_instructions/test_environment_information.cairo b/crates/evm/src/tests/test_instructions/test_environment_information.cairo index 1090efa5f..50aba05b4 100644 --- a/crates/evm/src/tests/test_instructions/test_environment_information.cairo +++ b/crates/evm/src/tests/test_instructions/test_environment_information.cairo @@ -1,4 +1,9 @@ use array::{ArrayTrait}; +use contracts::kakarot_core::interface::IExtendedKakarotCoreDispatcherTrait; +use contracts::tests::utils::{ + deploy_kakarot_core, deploy_native_token, fund_account_with_native_token +}; +use debug::U256PrintImpl; use evm::errors::{EVMError, TYPE_CONVERSION_ERROR, RETURNDATA_OUT_OF_BOUNDS_ERROR}; use evm::instructions::EnvironmentInformationTrait; use evm::machine::{Machine, MachineCurrentContextTrait}; @@ -9,8 +14,9 @@ use evm::tests::test_utils::{ setup_machine_with_nested_execution_context, other_evm_address, return_from_subcontext }; use integer::u32_overflowing_add; +use openzeppelin::token::erc20::interface::IERC20CamelDispatcherTrait; -use starknet::EthAddressIntoFelt252; +use starknet::{EthAddressIntoFelt252, contract_address_const, testing::set_contract_address}; use utils::helpers::{ u256_to_bytes_array, load_word, ArrayExtension, ArrayExtensionTrait, SpanExtension, SpanExtensionTrait @@ -43,6 +49,76 @@ fn test_address_nested_call() { // A (EOA) -(calls)-> B (smart contract) -(calls // ref: https://github.com/kkrt-labs/kakarot-ssj/issues/183 } +// ************************************************************************* +// 0x31: BALANCE +// ************************************************************************* +#[test] +#[available_gas(5000000)] +fn test_balance_eoa() { + // Given + let native_token = deploy_native_token(); + let kakarot_core = deploy_kakarot_core(native_token.contract_address); + let eoa = kakarot_core.deploy_eoa(evm_address()); + + fund_account_with_native_token(eoa, native_token); + + // And + let mut machine = setup_machine(); + machine.stack.push(evm_address().into()).unwrap(); + + // When + set_contract_address(kakarot_core.contract_address); + machine.exec_balance(); + + // Then + machine.stack.peek().unwrap().print(); + assert(machine.stack.peek().unwrap() == native_token.balanceOf(eoa), 'wrong balance'); +} + +#[test] +#[available_gas(5000000)] +fn test_balance_zero() { + // Given + let native_token = deploy_native_token(); + let kakarot_core = deploy_kakarot_core(native_token.contract_address); + + // And + let mut machine = setup_machine(); + machine.stack.push(evm_address().into()).unwrap(); + + // When + set_contract_address(kakarot_core.contract_address); + machine.exec_balance(); + + // Then + machine.stack.peek().unwrap().print(); + assert(machine.stack.peek().unwrap() == 0x00, 'wrong balance'); +} + +// TODO: implement balance once contracts accounts can be deployed +#[ignore] +#[test] +#[available_gas(5000000)] +fn test_balance_contract_account() { + // Given + let native_token = deploy_native_token(); + let kakarot_core = deploy_kakarot_core(native_token.contract_address); + // TODO: deploy contract account + // and fund it + + // And + let mut machine = setup_machine(); + machine.stack.push(evm_address().into()).unwrap(); + + // When + set_contract_address(kakarot_core.contract_address); + machine.exec_balance(); + + // Then + machine.stack.peek().unwrap().print(); + panic_with_felt252('Not implemented yet'); +} + // ************************************************************************* // 0x33: CALLER diff --git a/crates/evm/src/tests/test_utils.cairo b/crates/evm/src/tests/test_utils.cairo index 8f4aa36bd..dfc87b1dc 100644 --- a/crates/evm/src/tests/test_utils.cairo +++ b/crates/evm/src/tests/test_utils.cairo @@ -10,7 +10,7 @@ use starknet::{ }; fn starknet_address() -> ContractAddress { - 'starknet_address'.try_into().unwrap() + contract_address_const::<'starknet_address'>() } fn evm_address() -> EthAddress { @@ -22,7 +22,7 @@ fn other_evm_address() -> EthAddress { } fn other_starknet_address() -> ContractAddress { - 'other_starknet_address'.try_into().unwrap() + contract_address_const::<'other_starknet_address'>() } fn storage_base_address() -> StorageBaseAddress { @@ -30,7 +30,7 @@ fn storage_base_address() -> StorageBaseAddress { } fn zero_address() -> ContractAddress { - 0.try_into().unwrap() + contract_address_const::<0x00>() } fn callvalue() -> u256 { @@ -43,7 +43,7 @@ fn deploy_fee() -> u128 { } fn native_token() -> ContractAddress { - 'native_token'.try_into().unwrap() + contract_address_const::<'native_token'>() } fn chain_id() -> u128 { @@ -51,8 +51,7 @@ fn chain_id() -> u128 { } fn kakarot_address() -> ContractAddress { - let test_kakarot_address: ContractAddress = contract_address_const::<0x777>(); - test_kakarot_address + contract_address_const::<'kakarot'>() } fn eoa_address() -> EthAddress { diff --git a/crates/openzeppelin/Scarb.toml b/crates/openzeppelin/Scarb.toml new file mode 100644 index 000000000..d328a25e5 --- /dev/null +++ b/crates/openzeppelin/Scarb.toml @@ -0,0 +1,23 @@ +# Due to the following error, we have to manually copy paste contracts from Open Zeppelin +# error: Version solving failed: +# - openzeppelin v0.7.0 (git+https:#github.com/OpenZeppelin/cairo-contracts.git?tag=v0.7.0#61a2505fe0c0f19b5de2b3f8dedf421ba2cff657) cannot use starknet v2.3.0-rc0 (std), because openzeppelin requires starknet >=2.2.0 + +# Scarb does not have real version solving algorithm yet. +# Perhaps in the future this conflict could be resolved, but currently, +# please upgrade your dependencies to use latest versions of their dependencies. +# +# +# Credits: "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.7.0" + + +[package] +name = "openzeppelin" +version = "0.1.0" + +# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html + +[dependencies] +starknet.workspace = true + +[tool.fmt] +sort-module-level-items = true diff --git a/crates/openzeppelin/src/lib.cairo b/crates/openzeppelin/src/lib.cairo new file mode 100644 index 000000000..40d3ff585 --- /dev/null +++ b/crates/openzeppelin/src/lib.cairo @@ -0,0 +1 @@ +mod token; diff --git a/crates/openzeppelin/src/token.cairo b/crates/openzeppelin/src/token.cairo new file mode 100644 index 000000000..bfe4665e0 --- /dev/null +++ b/crates/openzeppelin/src/token.cairo @@ -0,0 +1 @@ +mod erc20; diff --git a/crates/openzeppelin/src/token/erc20.cairo b/crates/openzeppelin/src/token/erc20.cairo new file mode 100644 index 000000000..33853d7f4 --- /dev/null +++ b/crates/openzeppelin/src/token/erc20.cairo @@ -0,0 +1,6 @@ +mod erc20; +mod interface; + +use erc20::ERC20; +use interface::ERC20ABIDispatcher; +use interface::ERC20ABIDispatcherTrait; diff --git a/crates/openzeppelin/src/token/erc20/erc20.cairo b/crates/openzeppelin/src/token/erc20/erc20.cairo new file mode 100644 index 000000000..01bf5dca9 --- /dev/null +++ b/crates/openzeppelin/src/token/erc20/erc20.cairo @@ -0,0 +1,258 @@ +// Due to the following error, we have to manually copy paste contracts from Open Zeppelin +// error: Version solving failed: +// - openzeppelin v0.7.0 (git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.7.0#61a2505fe0c0f19b5de2b3f8dedf421ba2cff657) cannot use starknet v2.3.0-rc0 (std), because openzeppelin requires starknet >=2.2.0 + +// Scarb does not have real version solving algorithm yet. +// Perhaps in the future this conflict could be resolved, but currently, +// please upgrade your dependencies to use latest versions of their dependencies. +// +// +// Credits: "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.7.0" + +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (token/erc20/erc20.cairo) + +#[starknet::contract] +mod ERC20 { + use integer::BoundedInt; + use openzeppelin::token::erc20::interface::{IERC20, IERC20CamelOnly}; + use starknet::ContractAddress; + use starknet::get_caller_address; + use zeroable::Zeroable; + + #[storage] + struct Storage { + _name: felt252, + _symbol: felt252, + _total_supply: u256, + _balances: LegacyMap, + _allowances: LegacyMap<(ContractAddress, ContractAddress), u256>, + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + Transfer: Transfer, + Approval: Approval, + } + + #[derive(Drop, starknet::Event)] + struct Transfer { + #[key] + from: ContractAddress, + #[key] + to: ContractAddress, + value: u256 + } + + #[derive(Drop, starknet::Event)] + struct Approval { + #[key] + owner: ContractAddress, + #[key] + spender: ContractAddress, + value: u256 + } + + mod Errors { + const APPROVE_FROM_ZERO: felt252 = 'ERC20: approve from 0'; + const APPROVE_TO_ZERO: felt252 = 'ERC20: approve to 0'; + const TRANSFER_FROM_ZERO: felt252 = 'ERC20: transfer from 0'; + const TRANSFER_TO_ZERO: felt252 = 'ERC20: transfer to 0'; + const BURN_FROM_ZERO: felt252 = 'ERC20: burn from 0'; + const MINT_TO_ZERO: felt252 = 'ERC20: mint to 0'; + } + + #[constructor] + fn constructor( + ref self: ContractState, + name: felt252, + symbol: felt252, + initial_supply: u256, + recipient: ContractAddress + ) { + self.initializer(name, symbol); + self._mint(recipient, initial_supply); + } + + // + // External + // + + #[external(v0)] + impl ERC20Impl of IERC20 { + fn name(self: @ContractState) -> felt252 { + self._name.read() + } + + fn symbol(self: @ContractState) -> felt252 { + self._symbol.read() + } + + fn decimals(self: @ContractState) -> u8 { + 18 + } + + fn total_supply(self: @ContractState) -> u256 { + self._total_supply.read() + } + + fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { + self._balances.read(account) + } + + fn allowance( + self: @ContractState, owner: ContractAddress, spender: ContractAddress + ) -> u256 { + self._allowances.read((owner, spender)) + } + + fn transfer(ref self: ContractState, recipient: ContractAddress, amount: u256) -> bool { + let sender = get_caller_address(); + self._transfer(sender, recipient, amount); + true + } + + fn transfer_from( + ref self: ContractState, + sender: ContractAddress, + recipient: ContractAddress, + amount: u256 + ) -> bool { + let caller = get_caller_address(); + self._spend_allowance(sender, caller, amount); + self._transfer(sender, recipient, amount); + true + } + + fn approve(ref self: ContractState, spender: ContractAddress, amount: u256) -> bool { + let caller = get_caller_address(); + self._approve(caller, spender, amount); + true + } + } + + #[external(v0)] + impl ERC20CamelOnlyImpl of IERC20CamelOnly { + fn totalSupply(self: @ContractState) -> u256 { + ERC20Impl::total_supply(self) + } + + fn balanceOf(self: @ContractState, account: ContractAddress) -> u256 { + ERC20Impl::balance_of(self, account) + } + + fn transferFrom( + ref self: ContractState, + sender: ContractAddress, + recipient: ContractAddress, + amount: u256 + ) -> bool { + ERC20Impl::transfer_from(ref self, sender, recipient, amount) + } + } + + #[external(v0)] + fn increase_allowance( + ref self: ContractState, spender: ContractAddress, added_value: u256 + ) -> bool { + self._increase_allowance(spender, added_value) + } + + #[external(v0)] + fn increaseAllowance( + ref self: ContractState, spender: ContractAddress, addedValue: u256 + ) -> bool { + increase_allowance(ref self, spender, addedValue) + } + + #[external(v0)] + fn decrease_allowance( + ref self: ContractState, spender: ContractAddress, subtracted_value: u256 + ) -> bool { + self._decrease_allowance(spender, subtracted_value) + } + + #[external(v0)] + fn decreaseAllowance( + ref self: ContractState, spender: ContractAddress, subtractedValue: u256 + ) -> bool { + decrease_allowance(ref self, spender, subtractedValue) + } + + // + // Internal + // + + #[generate_trait] + impl InternalImpl of InternalTrait { + fn initializer(ref self: ContractState, name: felt252, symbol: felt252) { + self._name.write(name); + self._symbol.write(symbol); + } + + fn _increase_allowance( + ref self: ContractState, spender: ContractAddress, added_value: u256 + ) -> bool { + let caller = get_caller_address(); + self._approve(caller, spender, self._allowances.read((caller, spender)) + added_value); + true + } + + fn _decrease_allowance( + ref self: ContractState, spender: ContractAddress, subtracted_value: u256 + ) -> bool { + let caller = get_caller_address(); + self + ._approve( + caller, spender, self._allowances.read((caller, spender)) - subtracted_value + ); + true + } + + fn _mint(ref self: ContractState, recipient: ContractAddress, amount: u256) { + assert(!recipient.is_zero(), Errors::MINT_TO_ZERO); + self._total_supply.write(self._total_supply.read() + amount); + self._balances.write(recipient, self._balances.read(recipient) + amount); + self.emit(Transfer { from: Zeroable::zero(), to: recipient, value: amount }); + } + + fn _burn(ref self: ContractState, account: ContractAddress, amount: u256) { + assert(!account.is_zero(), Errors::BURN_FROM_ZERO); + self._total_supply.write(self._total_supply.read() - amount); + self._balances.write(account, self._balances.read(account) - amount); + self.emit(Transfer { from: account, to: Zeroable::zero(), value: amount }); + } + + fn _approve( + ref self: ContractState, owner: ContractAddress, spender: ContractAddress, amount: u256 + ) { + assert(!owner.is_zero(), Errors::APPROVE_FROM_ZERO); + assert(!spender.is_zero(), Errors::APPROVE_TO_ZERO); + self._allowances.write((owner, spender), amount); + self.emit(Approval { owner, spender, value: amount }); + } + + fn _transfer( + ref self: ContractState, + sender: ContractAddress, + recipient: ContractAddress, + amount: u256 + ) { + assert(!sender.is_zero(), Errors::TRANSFER_FROM_ZERO); + assert(!recipient.is_zero(), Errors::TRANSFER_TO_ZERO); + self._balances.write(sender, self._balances.read(sender) - amount); + self._balances.write(recipient, self._balances.read(recipient) + amount); + self.emit(Transfer { from: sender, to: recipient, value: amount }); + } + + fn _spend_allowance( + ref self: ContractState, owner: ContractAddress, spender: ContractAddress, amount: u256 + ) { + let current_allowance = self._allowances.read((owner, spender)); + if current_allowance != BoundedInt::max() { + self._approve(owner, spender, current_allowance - amount); + } + } + } +} diff --git a/crates/openzeppelin/src/token/erc20/interface.cairo b/crates/openzeppelin/src/token/erc20/interface.cairo new file mode 100644 index 000000000..e5fe4e340 --- /dev/null +++ b/crates/openzeppelin/src/token/erc20/interface.cairo @@ -0,0 +1,89 @@ +// Due to the following error, we have to manually copy paste contracts from Open Zeppelin +// error: Version solving failed: +// - openzeppelin v0.7.0 (git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.7.0#61a2505fe0c0f19b5de2b3f8dedf421ba2cff657) cannot use starknet v2.3.0-rc0 (std), because openzeppelin requires starknet >=2.2.0 + +// Scarb does not have real version solving algorithm yet. +// Perhaps in the future this conflict could be resolved, but currently, +// please upgrade your dependencies to use latest versions of their dependencies. +// +// +// Credits: "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.7.0" + +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (token/erc20/interface.cairo) + +use starknet::ContractAddress; + +#[starknet::interface] +trait IERC20 { + fn name(self: @TState) -> felt252; + fn symbol(self: @TState) -> felt252; + fn decimals(self: @TState) -> u8; + fn total_supply(self: @TState) -> u256; + fn balance_of(self: @TState, account: ContractAddress) -> u256; + fn allowance(self: @TState, owner: ContractAddress, spender: ContractAddress) -> u256; + fn transfer(ref self: TState, recipient: ContractAddress, amount: u256) -> bool; + fn transfer_from( + ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256 + ) -> bool; + fn approve(ref self: TState, spender: ContractAddress, amount: u256) -> bool; +} + +#[starknet::interface] +trait IERC20Camel { + fn name(self: @TState) -> felt252; + fn symbol(self: @TState) -> felt252; + fn decimals(self: @TState) -> u8; + fn totalSupply(self: @TState) -> u256; + fn balanceOf(self: @TState, account: ContractAddress) -> u256; + fn allowance(self: @TState, owner: ContractAddress, spender: ContractAddress) -> u256; + fn transfer(ref self: TState, recipient: ContractAddress, amount: u256) -> bool; + fn transferFrom( + ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256 + ) -> bool; + fn approve(ref self: TState, spender: ContractAddress, amount: u256) -> bool; +} + +trait IERC20CamelOnly { + fn totalSupply(self: @TState) -> u256; + fn balanceOf(self: @TState, account: ContractAddress) -> u256; + fn transferFrom( + ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256 + ) -> bool; +} + +#[starknet::interface] +trait ERC20ABI { + fn name(self: @TState) -> felt252; + fn symbol(self: @TState) -> felt252; + fn decimals(self: @TState) -> u8; + fn total_supply(self: @TState) -> u256; + fn balance_of(self: @TState, account: ContractAddress) -> u256; + fn allowance(self: @TState, owner: ContractAddress, spender: ContractAddress) -> u256; + fn transfer(ref self: TState, recipient: ContractAddress, amount: u256) -> bool; + fn transfer_from( + ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256 + ) -> bool; + fn approve(ref self: TState, spender: ContractAddress, amount: u256) -> bool; + fn increase_allowance(ref self: TState, spender: ContractAddress, added_value: u256) -> bool; + fn decrease_allowance( + ref self: TState, spender: ContractAddress, subtracted_value: u256 + ) -> bool; +} + +#[starknet::interface] +trait ERC20CamelABI { + fn name(self: @TState) -> felt252; + fn symbol(self: @TState) -> felt252; + fn decimals(self: @TState) -> u8; + fn totalSupply(self: @TState) -> u256; + fn balanceOf(self: @TState, account: ContractAddress) -> u256; + fn allowance(self: @TState, owner: ContractAddress, spender: ContractAddress) -> u256; + fn transfer(ref self: TState, recipient: ContractAddress, amount: u256) -> bool; + fn transferFrom( + ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256 + ) -> bool; + fn approve(ref self: TState, spender: ContractAddress, amount: u256) -> bool; + fn increaseAllowance(ref self: TState, spender: ContractAddress, addedValue: u256) -> bool; + fn decreaseAllowance(ref self: TState, spender: ContractAddress, subtractedValue: u256) -> bool; +} diff --git a/crates/utils/src/constants.cairo b/crates/utils/src/constants.cairo index 8bc4ba80f..b6b2047ae 100644 --- a/crates/utils/src/constants.cairo +++ b/crates/utils/src/constants.cairo @@ -14,6 +14,7 @@ const CHAIN_ID: u256 = 1263227476; // STACK const STACK_MAX_DEPTH: usize = 1024; + // Numeric constants const POW_256_0: u128 = 0x1; const POW_256_1: u128 = 0x100;