diff --git a/aderyn_core/src/detect/detector.rs b/aderyn_core/src/detect/detector.rs index 3005acdb..dfdf55f6 100644 --- a/aderyn_core/src/detect/detector.rs +++ b/aderyn_core/src/detect/detector.rs @@ -1,3 +1,4 @@ +use incorrect_erc20_interface::IncorrectERC20InterfaceDetector; use serde::{Deserialize, Serialize}; use strum::{Display, EnumCount, EnumIter, EnumString}; @@ -80,6 +81,7 @@ pub fn get_all_issue_detectors() -> Vec> { Box::::default(), Box::::default(), Box::::default(), + Box::::default(), Box::::default(), ] } @@ -157,6 +159,7 @@ pub(crate) enum IssueDetectorNamePool { TxOriginUsedForAuth, MsgValueInLoop, ContractLocksEther, + IncorrectERC20Interface, ReturnBomb, // NOTE: `Undecided` will be the default name (for new bots). // If it's accepted, a new variant will be added to this enum before normalizing it in aderyn @@ -167,6 +170,9 @@ pub fn request_issue_detector_by_name(detector_name: &str) -> Option { + Some(Box::::default()) + } IssueDetectorNamePool::ReturnBomb => Some(Box::::default()), IssueDetectorNamePool::UnusedStateVariable => { Some(Box::::default()) diff --git a/aderyn_core/src/detect/high/incorrect_erc20_interface.rs b/aderyn_core/src/detect/high/incorrect_erc20_interface.rs new file mode 100644 index 00000000..0919f59a --- /dev/null +++ b/aderyn_core/src/detect/high/incorrect_erc20_interface.rs @@ -0,0 +1,252 @@ +use std::collections::BTreeMap; +use std::convert::identity; +use std::error::Error; + +use crate::ast::{ASTNode, NodeID, Visibility}; + +use crate::capture; +use crate::context::browser::ExtractFunctionDefinitions; +use crate::detect::detector::IssueDetectorNamePool; +use crate::{ + context::workspace_context::WorkspaceContext, + detect::detector::{IssueDetector, IssueSeverity}, +}; +use eyre::Result; + +#[derive(Default)] +pub struct IncorrectERC20InterfaceDetector { + // Keys are: [0] source file name, [1] line number, [2] character location of node. + // Do not add items manually, use `capture!` to add nodes to this BTreeMap. + found_instances: BTreeMap<(String, usize, String), NodeID>, +} + +impl IssueDetector for IncorrectERC20InterfaceDetector { + fn detect(&mut self, context: &WorkspaceContext) -> Result> { + // Analyze each contract in context + for current_contract in context.contract_definitions() { + // Look through it's inheritance heirarchy to determine if it's an ERC20 + if let Some(contract_ids) = current_contract.linearized_base_contracts.as_ref() { + let current_contract_is_erc20 = contract_ids.iter().any(|i| { + context.nodes.get(i).is_some_and(|c| { + if let ASTNode::ContractDefinition(contract) = c { + if contract.name.contains("ERC20") { + return true; + } + } + false + }) + }); + + if !current_contract_is_erc20 { + continue; + } + + // Now we know that current contract is an ERC20 + + for contract_id in contract_ids { + if let Some(ASTNode::ContractDefinition(contract)) = + context.nodes.get(contract_id) + { + let functions = ExtractFunctionDefinitions::from(contract).extracted; + + for func in functions { + if (func.visibility != Visibility::Public + && func.visibility != Visibility::External) + || !func.implemented + { + continue; + } + + if (func.represents_erc20_transfer().is_some_and(identity) + || func.represents_erc20_transfer_from().is_some_and(identity) + || func.represents_erc20_approve().is_some_and(identity)) + && !func.returns_bool() + { + capture!(self, context, func); + } + + if (func.represents_erc20_allowance().is_some_and(identity) + || func.represents_erc20_balance_of().is_some_and(identity) + || func.represents_erc20_total_supply().is_some_and(identity)) + && !func.returns_uint256() + { + capture!(self, context, func) + } + } + } + } + } + } + + Ok(!self.found_instances.is_empty()) + } + + fn severity(&self) -> IssueSeverity { + IssueSeverity::High + } + + fn title(&self) -> String { + String::from("Incorrect ERC20 interface.") + } + + fn description(&self) -> String { + String::from("Incorrect return values for ERC20 functions. A contract compiled with Solidity > 0.4.22 \ + interacting with these functions will fail to execute them, as the return value is missing. Set the \ + appropriate return values and types for the defined ERC20 functions." + ) + } + + fn instances(&self) -> BTreeMap<(String, usize, String), NodeID> { + self.found_instances.clone() + } + + fn name(&self) -> String { + format!("{}", IssueDetectorNamePool::IncorrectERC20Interface) + } +} + +mod erc_matching_function_signature_helper { + //! This module matches function signature only (name + parameters) + //! This means, that the return value could be different. + + use crate::ast::FunctionDefinition; + + struct SignatureMatcher<'a> { + name: &'a str, + paramter_types: Vec<&'a str>, + } + + // Helps with checking if a function definition satisifed a signature matcher + impl<'a> SignatureMatcher<'a> { + fn satisfies(&self, func: &FunctionDefinition) -> Option { + if func.name != self.name { + return Some(false); + } + let params = &func.parameters.parameters; + if params.len() != self.paramter_types.len() { + return Some(false); + } + #[allow(clippy::needless_range_loop)] + for idx in 0..params.len() { + if let Some(func_param_type) = params[idx].type_descriptions.type_string.as_ref() { + let target = &self.paramter_types[idx]; + if *target == "address" { + if func_param_type == "address" || func_param_type == "address payable" { + continue; + } else { + return Some(false); + } + } else if func_param_type != target { + return Some(false); + } + } else { + return None; + } + } + Some(true) + } + } + + // ERC20 function signature matching + impl FunctionDefinition { + pub fn represents_erc20_transfer(&self) -> Option { + let satisifer = SignatureMatcher { + name: "transfer", + paramter_types: vec!["address", "uint256"], + }; + satisifer.satisfies(self) + } + + pub fn represents_erc20_transfer_from(&self) -> Option { + let satisifer = SignatureMatcher { + name: "transferFrom", + paramter_types: vec!["address", "address", "uint256"], + }; + satisifer.satisfies(self) + } + + pub fn represents_erc20_approve(&self) -> Option { + let satisifer = SignatureMatcher { + name: "approve", + paramter_types: vec!["address", "uint256"], + }; + satisifer.satisfies(self) + } + + pub fn represents_erc20_allowance(&self) -> Option { + let satisifer = SignatureMatcher { + name: "allowance", + paramter_types: vec!["address", "address"], + }; + satisifer.satisfies(self) + } + + pub fn represents_erc20_balance_of(&self) -> Option { + let satisifer = SignatureMatcher { + name: "balanceOf", + paramter_types: vec!["address"], + }; + satisifer.satisfies(self) + } + + pub fn represents_erc20_total_supply(&self) -> Option { + let satisifer = SignatureMatcher { + name: "totalSupply", + paramter_types: vec![], + }; + satisifer.satisfies(self) + } + } + + // Helpers to match return types (bool & uint256) + impl FunctionDefinition { + pub fn returns_bool(&self) -> bool { + let params = &self.return_parameters.parameters; + params.len() == 1 + && params[0] + .type_descriptions + .type_string + .as_ref() + .is_some_and(|type_string| type_string == "bool") + } + + pub fn returns_uint256(&self) -> bool { + let params = &self.return_parameters.parameters; + params.len() == 1 + && params[0] + .type_descriptions + .type_string + .as_ref() + .is_some_and(|type_string| type_string == "uint256") + } + } +} + +#[cfg(test)] +mod incorrect_erc20_tests { + use serial_test::serial; + + use crate::detect::{ + detector::IssueDetector, high::incorrect_erc20_interface::IncorrectERC20InterfaceDetector, + }; + + #[test] + #[serial] + fn test_incorrect_erc20_functions() { + let context = crate::detect::test_utils::load_solidity_source_unit( + "../tests/contract-playground/src/IncorrectERC20.sol", + ); + + let mut detector = IncorrectERC20InterfaceDetector::default(); + let found = detector.detect(&context).unwrap(); + // assert that the detector found an issue + assert!(found); + // assert that the detector found the correct number of instances + assert_eq!(detector.instances().len(), 5); + // assert the severity is high + assert_eq!( + detector.severity(), + crate::detect::detector::IssueSeverity::High + ); + } +} diff --git a/aderyn_core/src/detect/high/mod.rs b/aderyn_core/src/detect/high/mod.rs index 78270750..d6ce00ea 100644 --- a/aderyn_core/src/detect/high/mod.rs +++ b/aderyn_core/src/detect/high/mod.rs @@ -11,6 +11,7 @@ pub(crate) mod dynamic_array_length_assignment; pub(crate) mod enumerable_loop_removal; pub(crate) mod experimental_encoder; pub(crate) mod incorrect_caret_operator; +pub(crate) mod incorrect_erc20_interface; pub(crate) mod incorrect_shift_order; pub(crate) mod misused_boolean; pub(crate) mod msg_value_in_loops; @@ -48,6 +49,7 @@ pub use dynamic_array_length_assignment::DynamicArrayLengthAssignmentDetector; pub use enumerable_loop_removal::EnumerableLoopRemovalDetector; pub use experimental_encoder::ExperimentalEncoderDetector; pub use incorrect_caret_operator::IncorrectUseOfCaretOperatorDetector; +pub use incorrect_erc20_interface::IncorrectERC20InterfaceDetector; pub use incorrect_shift_order::IncorrectShiftOrderDetector; pub use misused_boolean::MisusedBooleanDetector; pub use msg_value_in_loops::MsgValueUsedInLoopDetector; diff --git a/reports/adhoc-sol-files-highs-only-report.json b/reports/adhoc-sol-files-highs-only-report.json index ad6e9c15..593df15a 100644 --- a/reports/adhoc-sol-files-highs-only-report.json +++ b/reports/adhoc-sol-files-highs-only-report.json @@ -197,6 +197,7 @@ "delete-nested-mapping", "tx-origin-used-for-auth", "msg-value-in-loop", - "contract-locks-ether" + "contract-locks-ether", + "incorrect-erc20-interface" ] } \ No newline at end of file diff --git a/reports/report.json b/reports/report.json index 7708d11f..fa835389 100644 --- a/reports/report.json +++ b/reports/report.json @@ -1,7 +1,7 @@ { "files_summary": { - "total_source_units": 81, - "total_sloc": 2334 + "total_source_units": 82, + "total_sloc": 2431 }, "files_details": { "files_details": [ @@ -117,6 +117,10 @@ "file_path": "src/IncorrectCaretOperator.sol", "n_sloc": 16 }, + { + "file_path": "src/IncorrectERC20.sol", + "n_sloc": 97 + }, { "file_path": "src/IncorrectShift.sol", "n_sloc": 17 @@ -332,7 +336,7 @@ ] }, "issue_count": { - "high": 36, + "high": 37, "low": 29 }, "high_issues": { @@ -2040,6 +2044,43 @@ "src_char": "3059:15" } ] + }, + { + "title": "Incorrect ERC20 interface.", + "description": "Incorrect return values for ERC20 functions. A contract compiled with Solidity > 0.4.22 interacting with these functions will fail to execute them, as the return value is missing. Set the appropriate return values and types for the defined ERC20 functions.", + "detector_name": "incorrect-erc20-interface", + "instances": [ + { + "contract_path": "src/IncorrectERC20.sol", + "line_no": 17, + "src": "483:8", + "src_char": "483:8" + }, + { + "contract_path": "src/IncorrectERC20.sol", + "line_no": 23, + "src": "690:7", + "src_char": "690:7" + }, + { + "contract_path": "src/IncorrectERC20.sol", + "line_no": 27, + "src": "808:12", + "src_char": "808:12" + }, + { + "contract_path": "src/IncorrectERC20.sol", + "line_no": 35, + "src": "1146:9", + "src_char": "1146:9" + }, + { + "contract_path": "src/IncorrectERC20.sol", + "line_no": 39, + "src": "1249:9", + "src_char": "1249:9" + } + ] } ] }, @@ -2325,6 +2366,12 @@ "src": "0:24", "src_char": "0:24" }, + { + "contract_path": "src/IncorrectERC20.sol", + "line_no": 2, + "src": "32:23", + "src_char": "32:23" + }, { "contract_path": "src/MsgValueInLoop.sol", "line_no": 2, @@ -2519,6 +2566,36 @@ "src": "129:9", "src_char": "129:9" }, + { + "contract_path": "src/IncorrectERC20.sol", + "line_no": 17, + "src": "483:8", + "src_char": "483:8" + }, + { + "contract_path": "src/IncorrectERC20.sol", + "line_no": 23, + "src": "690:7", + "src_char": "690:7" + }, + { + "contract_path": "src/IncorrectERC20.sol", + "line_no": 27, + "src": "808:12", + "src_char": "808:12" + }, + { + "contract_path": "src/IncorrectERC20.sol", + "line_no": 35, + "src": "1146:9", + "src_char": "1146:9" + }, + { + "contract_path": "src/IncorrectERC20.sol", + "line_no": 39, + "src": "1249:9", + "src_char": "1249:9" + }, { "contract_path": "src/ReturnBomb.sol", "line_no": 16, @@ -3263,6 +3340,12 @@ "src": "0:24", "src_char": "0:24" }, + { + "contract_path": "src/IncorrectERC20.sol", + "line_no": 2, + "src": "32:23", + "src_char": "32:23" + }, { "contract_path": "src/KeccakContract.sol", "line_no": 2, @@ -3787,6 +3870,18 @@ "line_no": 32, "src": "1673:6", "src_char": "1673:6" + }, + { + "contract_path": "src/IncorrectERC20.sol", + "line_no": 8, + "src": "225:7", + "src_char": "225:7" + }, + { + "contract_path": "src/IncorrectERC20.sol", + "line_no": 51, + "src": "1550:7", + "src_char": "1550:7" } ] }, @@ -4487,6 +4582,7 @@ "tx-origin-used-for-auth", "msg-value-in-loop", "contract-locks-ether", + "incorrect-erc20-interface", "return-bomb" ] } \ No newline at end of file diff --git a/reports/report.md b/reports/report.md index f335c32f..c03f1741 100644 --- a/reports/report.md +++ b/reports/report.md @@ -44,6 +44,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati - [H-34: Potential use of `tx.origin` for authentication.](#h-34-potential-use-of-txorigin-for-authentication) - [H-35: Loop contains `msg.value`.](#h-35-loop-contains-msgvalue) - [H-36: Contract locks Ether without a withdraw function.](#h-36-contract-locks-ether-without-a-withdraw-function) + - [H-37: Incorrect ERC20 interface.](#h-37-incorrect-erc20-interface) - [Low Issues](#low-issues) - [L-1: Centralization Risk for trusted owners](#l-1-centralization-risk-for-trusted-owners) - [L-2: Solmate's SafeTransferLib does not check for token contract's existence](#l-2-solmates-safetransferlib-does-not-check-for-token-contracts-existence) @@ -82,8 +83,8 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Key | Value | | --- | --- | -| .sol Files | 81 | -| Total nSLOC | 2334 | +| .sol Files | 82 | +| Total nSLOC | 2431 | ## Files Details @@ -118,6 +119,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | src/HugeConstants.sol | 36 | | src/InconsistentUints.sol | 17 | | src/IncorrectCaretOperator.sol | 16 | +| src/IncorrectERC20.sol | 97 | | src/IncorrectShift.sol | 17 | | src/InternalFunctions.sol | 22 | | src/KeccakContract.sol | 21 | @@ -171,14 +173,14 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | src/reused_contract_name/ContractB.sol | 7 | | src/uniswap/UniswapV2Swapper.sol | 50 | | src/uniswap/UniswapV3Swapper.sol | 150 | -| **Total** | **2334** | +| **Total** | **2431** | ## Issue Summary | Category | No. of Issues | | --- | --- | -| High | 36 | +| High | 37 | | Low | 29 | @@ -2034,6 +2036,47 @@ It appears that the contract includes a payable function to accept Ether but lac +## H-37: Incorrect ERC20 interface. + +Incorrect return values for ERC20 functions. A contract compiled with Solidity > 0.4.22 interacting with these functions will fail to execute them, as the return value is missing. Set the appropriate return values and types for the defined ERC20 functions. + +
5 Found Instances + + +- Found in src/IncorrectERC20.sol [Line: 17](../tests/contract-playground/src/IncorrectERC20.sol#L17) + + ```solidity + function transfer(address to, uint256 value) public { + ``` + +- Found in src/IncorrectERC20.sol [Line: 23](../tests/contract-playground/src/IncorrectERC20.sol#L23) + + ```solidity + function approve(address spender, uint256 value) public { + ``` + +- Found in src/IncorrectERC20.sol [Line: 27](../tests/contract-playground/src/IncorrectERC20.sol#L27) + + ```solidity + function transferFrom(address from, address to, uint256 value) public { + ``` + +- Found in src/IncorrectERC20.sol [Line: 35](../tests/contract-playground/src/IncorrectERC20.sol#L35) + + ```solidity + function balanceOf(address account) public pure returns (address) { + ``` + +- Found in src/IncorrectERC20.sol [Line: 39](../tests/contract-playground/src/IncorrectERC20.sol#L39) + + ```solidity + function allowance( + ``` + +
+ + + # Low Issues ## L-1: Centralization Risk for trusted owners @@ -2272,7 +2315,7 @@ ERC20 functions may not behave as expected. For example: return values are not a Consider using a specific version of Solidity in your contracts instead of a wide version. For example, instead of `pragma solidity ^0.8.0;`, use `pragma solidity 0.8.0;` -
25 Found Instances +
26 Found Instances - Found in src/CompilerBugStorageSignedIntegerArray.sol [Line: 2](../tests/contract-playground/src/CompilerBugStorageSignedIntegerArray.sol#L2) @@ -2341,6 +2384,12 @@ Consider using a specific version of Solidity in your contracts instead of a wid pragma solidity ^0.8.24; ``` +- Found in src/IncorrectERC20.sol [Line: 2](../tests/contract-playground/src/IncorrectERC20.sol#L2) + + ```solidity + pragma solidity ^0.8.0; + ``` + - Found in src/MsgValueInLoop.sol [Line: 2](../tests/contract-playground/src/MsgValueInLoop.sol#L2) ```solidity @@ -2480,7 +2529,7 @@ Check for `address(0)` when assigning values to address state variables. Instead of marking a function as `public`, consider marking it as `external` if it is not used internally. -
33 Found Instances +
38 Found Instances - Found in src/ArbitraryTransferFrom.sol [Line: 28](../tests/contract-playground/src/ArbitraryTransferFrom.sol#L28) @@ -2543,6 +2592,36 @@ Instead of marking a function as `public`, consider marking it as `external` if function setNumber(uint256 newNumber) public { ``` +- Found in src/IncorrectERC20.sol [Line: 17](../tests/contract-playground/src/IncorrectERC20.sol#L17) + + ```solidity + function transfer(address to, uint256 value) public { + ``` + +- Found in src/IncorrectERC20.sol [Line: 23](../tests/contract-playground/src/IncorrectERC20.sol#L23) + + ```solidity + function approve(address spender, uint256 value) public { + ``` + +- Found in src/IncorrectERC20.sol [Line: 27](../tests/contract-playground/src/IncorrectERC20.sol#L27) + + ```solidity + function transferFrom(address from, address to, uint256 value) public { + ``` + +- Found in src/IncorrectERC20.sol [Line: 35](../tests/contract-playground/src/IncorrectERC20.sol#L35) + + ```solidity + function balanceOf(address account) public pure returns (address) { + ``` + +- Found in src/IncorrectERC20.sol [Line: 39](../tests/contract-playground/src/IncorrectERC20.sol#L39) + + ```solidity + function allowance( + ``` + - Found in src/ReturnBomb.sol [Line: 16](../tests/contract-playground/src/ReturnBomb.sol#L16) ```solidity @@ -3236,7 +3315,7 @@ Using `ERC721::_mint()` can mint ERC721 tokens to addresses which don't support Solc compiler version 0.8.20 switches the default target EVM version to Shanghai, which means that the generated bytecode will include PUSH0 opcodes. Be sure to select the appropriate EVM version in case you intend to deploy on a chain other than mainnet like L2 chains that may not support PUSH0, otherwise deployment of your contracts will fail. -
33 Found Instances +
34 Found Instances - Found in src/AdminContract.sol [Line: 2](../tests/contract-playground/src/AdminContract.sol#L2) @@ -3299,6 +3378,12 @@ Solc compiler version 0.8.20 switches the default target EVM version to Shanghai pragma solidity ^0.8.24; ``` +- Found in src/IncorrectERC20.sol [Line: 2](../tests/contract-playground/src/IncorrectERC20.sol#L2) + + ```solidity + pragma solidity ^0.8.0; + ``` + - Found in src/KeccakContract.sol [Line: 2](../tests/contract-playground/src/KeccakContract.sol#L2) ```solidity @@ -3701,7 +3786,7 @@ Consider removing empty blocks. Use `e` notation, for example: `1e18`, instead of its full numeric value. -
22 Found Instances +
24 Found Instances - Found in src/HugeConstants.sol [Line: 6](../tests/contract-playground/src/HugeConstants.sol#L6) @@ -3836,6 +3921,18 @@ Use `e` notation, for example: `1e18`, instead of its full numeric value. uint256 constant public HUGE_NUMBER_25_UNDERSCORES = 10_000; ``` +- Found in src/IncorrectERC20.sol [Line: 8](../tests/contract-playground/src/IncorrectERC20.sol#L8) + + ```solidity + uint256 public totalSupply = 1000000; + ``` + +- Found in src/IncorrectERC20.sol [Line: 51](../tests/contract-playground/src/IncorrectERC20.sol#L51) + + ```solidity + uint256 private _totalSupply = 1000000 * 10 ** uint(decimals); + ``` +
diff --git a/reports/report.sarif b/reports/report.sarif index 8138e38a..423fe532 100644 --- a/reports/report.sarif +++ b/reports/report.sarif @@ -2990,6 +2990,70 @@ }, "ruleId": "contract-locks-ether" }, + { + "level": "warning", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/IncorrectERC20.sol" + }, + "region": { + "byteLength": 8, + "byteOffset": 483 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/IncorrectERC20.sol" + }, + "region": { + "byteLength": 7, + "byteOffset": 690 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/IncorrectERC20.sol" + }, + "region": { + "byteLength": 12, + "byteOffset": 808 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/IncorrectERC20.sol" + }, + "region": { + "byteLength": 9, + "byteOffset": 1146 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/IncorrectERC20.sol" + }, + "region": { + "byteLength": 9, + "byteOffset": 1249 + } + } + } + ], + "message": { + "text": "Incorrect return values for ERC20 functions. A contract compiled with Solidity > 0.4.22 interacting with these functions will fail to execute them, as the return value is missing. Set the appropriate return values and types for the defined ERC20 functions." + }, + "ruleId": "incorrect-erc20-interface" + }, { "level": "note", "locations": [ @@ -3478,6 +3542,17 @@ } } }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/IncorrectERC20.sol" + }, + "region": { + "byteLength": 23, + "byteOffset": 32 + } + } + }, { "physicalLocation": { "artifactLocation": { @@ -3826,6 +3901,61 @@ } } }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/IncorrectERC20.sol" + }, + "region": { + "byteLength": 8, + "byteOffset": 483 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/IncorrectERC20.sol" + }, + "region": { + "byteLength": 7, + "byteOffset": 690 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/IncorrectERC20.sol" + }, + "region": { + "byteLength": 12, + "byteOffset": 808 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/IncorrectERC20.sol" + }, + "region": { + "byteLength": 9, + "byteOffset": 1146 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/IncorrectERC20.sol" + }, + "region": { + "byteLength": 9, + "byteOffset": 1249 + } + } + }, { "physicalLocation": { "artifactLocation": { @@ -5167,6 +5297,17 @@ } } }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/IncorrectERC20.sol" + }, + "region": { + "byteLength": 23, + "byteOffset": 32 + } + } + }, { "physicalLocation": { "artifactLocation": { @@ -6117,6 +6258,28 @@ "byteOffset": 1673 } } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/IncorrectERC20.sol" + }, + "region": { + "byteLength": 7, + "byteOffset": 225 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/IncorrectERC20.sol" + }, + "region": { + "byteLength": 7, + "byteOffset": 1550 + } + } } ], "message": { diff --git a/tests/contract-playground/src/IncorrectERC20.sol b/tests/contract-playground/src/IncorrectERC20.sol new file mode 100644 index 00000000..a8ab83c0 --- /dev/null +++ b/tests/contract-playground/src/IncorrectERC20.sol @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract IncorrectERC20 { + string public name = "IncorrectToken"; + string public symbol = "ICT"; + uint8 public decimals = 18; + uint256 public totalSupply = 1000000; + + mapping(address => uint256) public balances; + mapping(address => mapping(address => uint256)) public allowances; + + constructor() { + balances[msg.sender] = totalSupply; // Assign total supply to contract creator + } + + function transfer(address to, uint256 value) public { + require(balances[msg.sender] >= value, "Insufficient balance"); + balances[msg.sender] -= value; + balances[to] += value; + } + + function approve(address spender, uint256 value) public { + allowances[msg.sender][spender] = value; + } + + function transferFrom(address from, address to, uint256 value) public { + require(balances[from] >= value, "Insufficient balance"); + require(allowances[from][msg.sender] >= value, "Allowance exceeded"); + balances[from] -= value; + balances[to] += value; + allowances[from][msg.sender] -= value; + } + + function balanceOf(address account) public pure returns (address) { + return account; + } + + function allowance( + address owner, + address spender + ) public pure returns (bool) { + return owner == spender; + } +} + +contract CorrectERC20 { + string public name = "CorrectToken"; + string public symbol = "CRT"; + uint8 public decimals = 18; + uint256 private _totalSupply = 1000000 * 10 ** uint(decimals); + + mapping(address => uint256) private _balances; + mapping(address => mapping(address => uint256)) private _allowances; + + constructor() { + _balances[msg.sender] = _totalSupply; + } + + function totalSupply() external view returns (uint256) { + return _totalSupply; + } + + function balanceOf(address account) external view returns (uint256) { + return _balances[account]; + } + + function transfer( + address recipient, + uint256 amount + ) external returns (bool) { + _transfer(msg.sender, recipient, amount); + return true; + } + + function allowance( + address owner, + address spender + ) external view returns (uint256) { + return _allowances[owner][spender]; + } + + function approve(address spender, uint256 amount) external returns (bool) { + _approve(msg.sender, spender, amount); + return true; + } + + function transferFrom( + address sender, + address recipient, + uint256 amount + ) external returns (bool) { + _transfer(sender, recipient, amount); + _approve(sender, msg.sender, _allowances[sender][msg.sender] - amount); + return true; + } + + function _transfer( + address sender, + address recipient, + uint256 amount + ) internal { + require(sender != address(0), "ERC20: transfer from the zero address"); + require(recipient != address(0), "ERC20: transfer to the zero address"); + require( + _balances[sender] >= amount, + "ERC20: transfer amount exceeds balance" + ); + + _balances[sender] -= amount; + _balances[recipient] += amount; + } + + function _approve(address owner, address spender, uint256 amount) internal { + require(owner != address(0), "ERC20: approve from the zero address"); + require(spender != address(0), "ERC20: approve to the zero address"); + + _allowances[owner][spender] = amount; + } +}