Skip to content

Commit

Permalink
Fix address filter from comments (#43)
Browse files Browse the repository at this point in the history
* Fix address filter from comments

* Fix tests

* Add test

* Add test

* Fix test

* Fix print

* Fix testcase

* Fix tests
  • Loading branch information
nivcertora authored Dec 29, 2024
1 parent cf820a2 commit bb97d09
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 6 deletions.
2 changes: 1 addition & 1 deletion Quorum/apis/block_explorers/chains_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def __init__(self, chain: Chain) -> None:
chain_id = self.CHAIN_ID_MAP[chain]
api_key = os.getenv("ETHSCAN_API_KEY")
if not api_key:
raise ValueError(f"{chain}SCAN_API_KEY environment variable is not set.")
raise ValueError("ETHSCAN_API_KEY environment variable is not set.")

self.base_url = self.BASE_URL.format(chain_id=chain_id, api_key=api_key)

Expand Down
39 changes: 36 additions & 3 deletions Quorum/checks/price_feed.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,31 @@
import Quorum.utils.pretty_printer as pp


def remove_solidity_comments(source_code: str) -> str:
"""
Removes single-line and multi-line comments from Solidity source code.
Args:
source_code (str): The Solidity source code as a single string.
Returns:
str: The source code with comments removed.
"""
# Regex pattern to match single-line comments (//...)
single_line_comment_pattern = r'//.*?$'

# Regex pattern to match multi-line comments (/*...*/)
multi_line_comment_pattern = r'/\*.*?\*/'

# First, remove multi-line comments
source_code = re.sub(multi_line_comment_pattern, '', source_code, flags=re.DOTALL)

# Then, remove single-line comments
source_code = re.sub(single_line_comment_pattern, '', source_code, flags=re.MULTILINE)

return source_code


class PriceFeedCheck(Check):
"""
The PriceFeedCheck class is responsible for verifying the price feed addresses in the source code
Expand Down Expand Up @@ -37,12 +62,13 @@ def __init__(
self.address_pattern = r'0x[a-fA-F0-9]{40}'
self.providers = providers

def __check_price_feed_address(self, address: str) -> dict | None:
def __check_price_feed_address(self, address: str, file_name: str) -> dict | None:
"""
Check if the given address is present in the price feed providers.
Args:
address (str): The address to be checked.
file_name (str): The name of the source code file where the address was found.
Returns:
dict | None: The price feed data if the address is found, otherwise None.
Expand Down Expand Up @@ -75,10 +101,17 @@ def verify_price_feed(self) -> None:
verified_sources_path = f"{Path(source_code.file_name).stem.removesuffix('.sol')}/verified_sources.json"
verified_variables = []

# Combine all lines into a single string
contract_text = '\n'.join(source_code.file_content)
addresses = set(re.findall(self.address_pattern, contract_text))

# Remove comments from the source code
clean_text = remove_solidity_comments(contract_text)

# Extract unique addresses using regex
addresses = set(re.findall(self.address_pattern, clean_text))

for address in addresses:
if feed := self.__check_price_feed_address(address):
if feed := self.__check_price_feed_address(address, source_code.file_name):
verified_variables.append(feed)

if verified_variables:
Expand Down
5 changes: 3 additions & 2 deletions Quorum/tests/test_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ def test_global_variables(source_codes: list[SourceCode], tmp_output_path: Path)


@pytest.mark.parametrize('source_codes', ['ETH/0xAD6c03BF78A3Ee799b86De5aCE32Bb116eD24637'], indirect=True)
def test_price_feed(source_codes: list[SourceCode], tmp_output_path: Path):
price_feed_check = Checks.PriceFeedCheck('Aave', Chain.ETH, '', source_codes, [ChainLinkAPI()])
def test_price_feed_check(source_codes: list[SourceCode], tmp_output_path: Path):
price_feed_check = Checks.PriceFeedCheck('Aave', Chain.ETH, '', source_codes, [
ChainLinkAPI()])
price_feed_check.verify_price_feed()

assert sorted([p.name for p in price_feed_check.check_folder.iterdir()]) == ['AaveV2Ethereum']
Expand Down
84 changes: 84 additions & 0 deletions Quorum/tests/test_price_feed.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import pytest


from Quorum.apis.block_explorers.source_code import SourceCode
import Quorum.checks as Checks
from Quorum.utils.chain_enum import Chain
from Quorum.apis.price_feeds import ChainLinkAPI

from pathlib import Path


@pytest.mark.parametrize('source_codes', ['ETH/0xAD6c03BF78A3Ee799b86De5aCE32Bb116eD24637'], indirect=True)
def test_price_feed(source_codes: list[SourceCode], tmp_output_path: Path):
price_feed_check = Checks.PriceFeedCheck('Aave', Chain.ETH, '', source_codes, [
ChainLinkAPI()])
price_feed_check.verify_price_feed()

assert sorted([p.name for p in price_feed_check.check_folder.iterdir()]) == ['AaveV2Ethereum']


def test_source_code_clean():
code = """
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IProposalGenericExecutor} from 'aave-helpers/interfaces/IProposalGenericExecutor.sol';
import {AaveV2Ethereum, AaveV2EthereumAssets, ILendingPoolConfigurator} from 'aave-address-book/AaveV2Ethereum.sol';
/**
* @title Reserve Factor Updates Mid July
* @author karpatkey_TokenLogic
* - Snapshot: https://snapshot.org/#/aave.eth/proposal/0x9cc7906f04f45cebeaa48a05ed281f49da00d89c4dd988a968272fa179f14d06
* - Discussion: https://governance.aave.com/t/arfc-increase-bridged-usdc-reserve-factor-across-all-deployments/17787
*/
contract AaveV2Ethereum_ReserveFactorUpdatesMidJuly_20240711 is IProposalGenericExecutor {
ILendingPoolConfigurator public constant POOL_CONFIGURATOR =
ILendingPoolConfigurator(AaveV2Ethereum.POOL_CONFIGURATOR);
uint256 public constant DAI_RF = 65_00;
uint256 public constant LINK_RF = 70_00;
uint256 public constant USDC_RF = 65_00;
uint256 public constant USDT_RF = 65_00;
uint256 public constant WBTC_RF = 70_00;
uint256 public constant WETH_RF = 65_00;
function execute() external {
POOL_CONFIGURATOR.setReserveFactor(AaveV2EthereumAssets.DAI_UNDERLYING, DAI_RF);
POOL_CONFIGURATOR.setReserveFactor(AaveV2EthereumAssets.LINK_UNDERLYING, LINK_RF);
POOL_CONFIGURATOR.setReserveFactor(AaveV2EthereumAssets.USDC_UNDERLYING, USDC_RF);
POOL_CONFIGURATOR.setReserveFactor(AaveV2EthereumAssets.USDT_UNDERLYING, USDT_RF);
POOL_CONFIGURATOR.setReserveFactor(AaveV2EthereumAssets.WBTC_UNDERLYING, WBTC_RF);
POOL_CONFIGURATOR.setReserveFactor(AaveV2EthereumAssets.WETH_UNDERLYING, WETH_RF);
}
}
"""
cleaned = Checks.price_feed.remove_solidity_comments(code)
expected = """pragma solidity ^0.8.0;
import {IProposalGenericExecutor} from 'aave-helpers/interfaces/IProposalGenericExecutor.sol';
import {AaveV2Ethereum, AaveV2EthereumAssets, ILendingPoolConfigurator} from 'aave-address-book/AaveV2Ethereum.sol';
contract AaveV2Ethereum_ReserveFactorUpdatesMidJuly_20240711 is IProposalGenericExecutor {
ILendingPoolConfigurator public constant POOL_CONFIGURATOR =
ILendingPoolConfigurator(AaveV2Ethereum.POOL_CONFIGURATOR);
uint256 public constant DAI_RF = 65_00;
uint256 public constant LINK_RF = 70_00;
uint256 public constant USDC_RF = 65_00;
uint256 public constant USDT_RF = 65_00;
uint256 public constant WBTC_RF = 70_00;
uint256 public constant WETH_RF = 65_00;
function execute() external {
POOL_CONFIGURATOR.setReserveFactor(AaveV2EthereumAssets.DAI_UNDERLYING, DAI_RF);
POOL_CONFIGURATOR.setReserveFactor(AaveV2EthereumAssets.LINK_UNDERLYING, LINK_RF);
POOL_CONFIGURATOR.setReserveFactor(AaveV2EthereumAssets.USDC_UNDERLYING, USDC_RF);
POOL_CONFIGURATOR.setReserveFactor(AaveV2EthereumAssets.USDT_UNDERLYING, USDT_RF);
POOL_CONFIGURATOR.setReserveFactor(AaveV2EthereumAssets.WBTC_UNDERLYING, WBTC_RF);
POOL_CONFIGURATOR.setReserveFactor(AaveV2EthereumAssets.WETH_UNDERLYING, WETH_RF);
}
}"""
# Strip leading/trailing whitespace for accurate comparison
assert cleaned.strip() == expected.strip()

0 comments on commit bb97d09

Please sign in to comment.