Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spot Price detector #2448

Open
wants to merge 60 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
38cc41a
Update installation instrucitons
0xalpharush Oct 18, 2023
f0f1200
Update Dockerfile
0xalpharush Oct 18, 2023
a9e52aa
Bump actions/setup-node from 3 to 4
dependabot[bot] Oct 23, 2023
c316a81
Error when a missing contract is specified to read-storage. Previousl…
UsmannK Nov 16, 2023
4bfb61e
Bump cachix/install-nix-action from 23 to 24
dependabot[bot] Dec 4, 2023
72a8e4b
Bump actions/configure-pages from 3 to 4
dependabot[bot] Dec 4, 2023
a2c5206
Bump actions/deploy-pages from 2 to 3
dependabot[bot] Dec 4, 2023
6d5def6
Bump pypa/gh-action-pypi-publish from 1.8.10 to 1.8.11
dependabot[bot] Dec 4, 2023
12ac369
remove unused files
0xalpharush Oct 20, 2023
435871f
fix is_reentrant for internal vyper functions
0xalpharush Nov 1, 2023
c225727
Substituted the letter 'z' with 'x' in pre-declaration
ATREAY Dec 7, 2023
a3dd9b1
updated mutator
vishnuram1999 Jan 2, 2024
7984ad3
Updated replace string logic
vishnuram1999 Jan 3, 2024
4eba366
Added new mutant generators
vishnuram1999 Jan 6, 2024
40305bb
Added new mutators
vishnuram1999 Jan 9, 2024
107c42b
Updated mutators
vishnuram1999 Jan 15, 2024
f001bad
Updated run_test_cmd
vishnuram1999 Jan 16, 2024
dfed543
updated create patch
vishnuram1999 Jan 17, 2024
25ed527
Added contract_names arg
vishnuram1999 Jan 18, 2024
6ae8579
updated quick
vishnuram1999 Jan 20, 2024
9ce98f4
Added README
vishnuram1999 Jan 21, 2024
3db8da2
Updated arguments
vishnuram1999 Jan 24, 2024
a51fd70
Updated mutators
vishnuram1999 Jan 24, 2024
6f584ec
Updated files
vishnuram1999 Jan 25, 2024
89e90b7
Updated with formatting
vishnuram1999 Jan 26, 2024
5c88d6e
Formatted mutators
vishnuram1999 Jan 26, 2024
31ef3f1
Formatted test_patch
vishnuram1999 Jan 26, 2024
114e864
Bump cachix/cachix-action from 12 to 14 (#2275)
dependabot[bot] Jan 29, 2024
e944173
Bump actions/upload-artifact from 3 to 4 (#2265)
dependabot[bot] Jan 29, 2024
8e4e634
Bump actions/setup-python from 4 to 5 (#2259)
dependabot[bot] Jan 29, 2024
d0761a3
Detect also in modifiers (#2280)
smonicas Jan 29, 2024
04426e0
Fix/iterative update (#2206)
0xalpharush Jan 29, 2024
cda9a9d
fix: detect selfdestruct in internal calls (#2232)
0xalpharush Jan 29, 2024
5a8b636
Bump actions/deploy-pages from 3 to 4 (#2285)
dependabot[bot] Jan 30, 2024
9c2110e
Bump cachix/install-nix-action from 24 to 25 (#2286)
dependabot[bot] Jan 30, 2024
2e089b6
Bump sigstore/gh-action-sigstore-python from 2.1.0 to 2.1.1 (#2293)
dependabot[bot] Feb 6, 2024
565fa7c
Bump actions/upload-pages-artifact from 2 to 3 (#2294)
dependabot[bot] Feb 6, 2024
0ded68d
Fix using for when used with "this" (#2224)
smonicas Feb 6, 2024
6449529
fix: broken doc links (#2299)
mds1 Feb 8, 2024
2450fbc
feat: First version of Uniswap spot price detection
talfao Mar 12, 2024
20843df
feat: Test data with getReserves
talfao Mar 12, 2024
ee7055b
feat: balanceOF spot price detection
talfao Mar 12, 2024
fac7b33
feat: Add comments
talfao Mar 29, 2024
947e447
feat: making analysis faster
talfao Apr 8, 2024
2e5e4bc
fix: remove files which should be in different branch
talfao Apr 15, 2024
f640165
fix: balanceOf check
talfao Apr 15, 2024
1fa07aa
fix: balanceOf issue
talfao Apr 15, 2024
51a58bf
fix: pylint issues
talfao Apr 15, 2024
89a6d75
feat: improvement for detecting correct balanceOF scenario
talfao Apr 16, 2024
a21fc91
feat: getAmountOut add as problem + tainting vars
talfao Apr 16, 2024
784d0e4
feat: break loop earlier in tracking
talfao Apr 16, 2024
ead9998
feat: remove cumulativePrice and case when slot0 does not return price
talfao Apr 17, 2024
921f6cb
fix: slot0 must return price
talfao Apr 17, 2024
9e0d446
feat: get calculate functions
talfao Apr 21, 2024
f042e6c
fix: pure and view evaluating
talfao Apr 21, 2024
70c187e
fix: reformating
talfao Apr 21, 2024
0d1e276
Swap detection
talfao Apr 23, 2024
070b785
feat: add more comments before merge
talfao Apr 26, 2024
a8a7722
fix: rebase branch
talfao Apr 27, 2024
f2c9409
Merge dev to on-chain branch
talfao Apr 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions slither/detectors/all_detectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,4 @@
from .statements.return_bomb import ReturnBomb
from .functions.out_of_order_retryable import OutOfOrderRetryable
from .statements.unused_import import UnusedImport
from .oracles.spot_price import SpotPriceDetector
393 changes: 393 additions & 0 deletions slither/detectors/oracles/spot_price.py

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions slither/slithir/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -1584,6 +1584,18 @@ def convert_to_library_or_top_level(
if new_ir:
return new_ir

if (
isinstance(t, ElementaryType)
and t.name == "address"
and ir.destination.name == "this"
and UserDefinedType(node.function.contract) in using_for
):
new_ir = look_for_library_or_top_level(
contract, ir, using_for, UserDefinedType(node.function.contract)
)
if new_ir:
return new_ir
talfao marked this conversation as resolved.
Show resolved Hide resolved

return None


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Calculations are made with spot price data in tests/e2e/detectors/test_data/oracle-spot-price/0.6.12/spot_price_getReserves.sol#200
Method which could indicate usage of spot price was detected in Uniswap V2 at tests/e2e/detectors/test_data/oracle-spot-price/0.6.12/spot_price_getReserves.sol#197
EXPRESSION (_yaxReserves) = yaxisEthUniswapV2Pair.getReserves()

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Calculations are made with spot price data in tests/e2e/detectors/test_data/oracle-spot-price/0.8.20/spot_price_balanceOf.sol#41 but the function is not used anywhere in the contract.
Method which could indicate usage of spot price was detected at tests/e2e/detectors/test_data/oracle-spot-price/0.8.20/spot_price_balanceOf.sol#38 and tests/e2e/detectors/test_data/oracle-spot-price/0.8.20/spot_price_balanceOf.sol#39.
NEW VARIABLE usdcBalance = IERC20(USDCAddress).balanceOf(pool)
NEW VARIABLE ethBalance = IERC20(weth).balanceOf(pool)

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Calculations are made with spot price data in tests/e2e/detectors/test_data/oracle-spot-price/0.8.20/spot_price_getAmountOut.sol#66-71 but the function is not used anywhere in the contract.
Method which could indicate usage of spot price was detected in Uniswap V2 at tests/e2e/detectors/test_data/oracle-spot-price/0.8.20/spot_price_getAmountOut.sol#59
EXPRESSION (left,right) = pair.getReserves()

Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
// Resource: https://solodit.xyz/issues/m-10-yaxisvotepowerbalanceof-can-be-manipulated-code4rena-yaxis-yaxis-contest-git

// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
if (a == 0) {
return 0;
}
c = a * b;
assert(c / a == b);
return c;
}

/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
// uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return a / b;
}

/**
* @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}

/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
c = a + b;
assert(c >= a);
return c;
}
}

interface IRewards {
function balanceOf(address) external view returns (uint256);
function earned(address) external view returns (uint256);
function totalSupply() external view returns (uint256);
}
interface IUniswapV2Pair {
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);

function name() external pure returns (string memory);
function symbol() external pure returns (string memory);
function decimals() external pure returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(
address owner,
address spender
) external view returns (uint);

function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(
address from,
address to,
uint value
) external returns (bool);

function DOMAIN_SEPARATOR() external view returns (bytes32);
function PERMIT_TYPEHASH() external pure returns (bytes32);
function nonces(address owner) external view returns (uint);

function permit(
address owner,
address spender,
uint value,
uint deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;

event Mint(address indexed sender, uint amount0, uint amount1);
event Burn(
address indexed sender,
uint amount0,
uint amount1,
address indexed to
);
event Swap(
address indexed sender,
uint amount0In,
uint amount1In,
uint amount0Out,
uint amount1Out,
address indexed to
);
event Sync(uint112 reserve0, uint112 reserve1);

function MINIMUM_LIQUIDITY() external pure returns (uint);
function factory() external view returns (address);
function token0() external view returns (address);
function token1() external view returns (address);
function getReserves()
external
view
returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
function price0CumulativeLast() external view returns (uint);
function price1CumulativeLast() external view returns (uint);
function kLast() external view returns (uint);

function mint(address to) external returns (uint liquidity);
function burn(address to) external returns (uint amount0, uint amount1);
function swap(
uint amount0Out,
uint amount1Out,
address to,
bytes calldata data
) external;
function skim(address to) external;
function sync() external;

function initialize(address, address) external;
}

interface IVoteProxy {
function decimals() external pure returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address _voter) external view returns (uint256);
}

interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);

event Approval(
address indexed owner,
address indexed spender,
uint256 value
);

function totalSupply() external view returns (uint256);

function balanceOf(address account) external view returns (uint256);

function transfer(address to, uint256 value) external returns (bool);

function allowance(
address owner,
address spender
) external view returns (uint256);

function approve(address spender, uint256 value) external returns (bool);

function transferFrom(
address from,
address to,
uint256 value
) external returns (bool);
}

contract YaxisVotePower is IVoteProxy {
using SafeMath for uint256;

// solhint-disable-next-line const-name-snakecase
uint8 public constant override decimals = uint8(18);

IUniswapV2Pair public immutable yaxisEthUniswapV2Pair;
IERC20 public immutable yaxis;
IRewards public immutable rewardsYaxis;
IRewards public immutable rewardsYaxisEth;

constructor(
address _yaxis,
address _rewardsYaxis,
address _rewardsYaxisEth,
address _yaxisEthUniswapV2Pair
) public {
yaxis = IERC20(_yaxis);
rewardsYaxis = IRewards(_rewardsYaxis);
rewardsYaxisEth = IRewards(_rewardsYaxisEth);
yaxisEthUniswapV2Pair = IUniswapV2Pair(_yaxisEthUniswapV2Pair);
}

function totalSupply() external view override returns (uint256) {
return sqrt(yaxis.totalSupply());
}

function balanceOf(
address _voter
) external view override returns (uint256 _balance) {
uint256 _stakeAmount = rewardsYaxisEth.balanceOf(_voter);
(uint256 _yaxReserves, , ) = yaxisEthUniswapV2Pair.getReserves();
uint256 _supply = yaxisEthUniswapV2Pair.totalSupply();
_supply = _supply == 0 ? 1e18 : _supply;
uint256 _lpStakingYax = _yaxReserves.mul(_stakeAmount).div(_supply);
talfao marked this conversation as resolved.
Show resolved Hide resolved
uint256 _rewardsYaxisAmount = rewardsYaxis.balanceOf(_voter).add(
rewardsYaxis.earned(_voter)
);
_balance = sqrt(
yaxis.balanceOf(_voter).add(_lpStakingYax).add(_rewardsYaxisAmount)
);
}

function sqrt(uint256 x) private pure returns (uint256 y) {
uint256 z = (x + 1) / 2;
y = x;
while (z < y) {
y = z;
z = (x / z + z) / 2;
}
y = y * (10 ** 9);
}
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
pragma solidity 0.8.20;

interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);

event Approval(
address indexed owner,
address indexed spender,
uint256 value
);

function totalSupply() external view returns (uint256);

function balanceOf(address account) external view returns (uint256);

function transfer(address to, uint256 value) external returns (bool);

function allowance(
address owner,
address spender
) external view returns (uint256);

function approve(address spender, uint256 value) external returns (bool);

function transferFrom(
address from,
address to,
uint256 value
) external returns (bool);
}

contract BalanceOfData {
function getPriceV2(
address pool,
address USDCAddress,
address weth
) internal view returns (uint256 price) {
uint256 usdcBalance = IERC20(USDCAddress).balanceOf(pool);
uint256 ethBalance = IERC20(weth).balanceOf(pool);

price = (ethBalance * 10 ** 18) / usdcBalance;
talfao marked this conversation as resolved.
Show resolved Hide resolved
}
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
pragma solidity 0.8.20;

interface IERC20 {
function balanceOf(address account) external view returns (uint256);

/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);

function decimals() external view virtual returns (uint8);
}
interface IUniswapV2Pair {
function getReserves()
external
view
returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
}
interface IUniswapV2Factory {
function getPair(
address tokenA,
address tokenB
) external view returns (address pair);
}

interface IUniswapV2Router01 {
function quote(
uint amountA,
uint reserveA,
uint reserveB
) external pure returns (uint amountB);
function getAmountOut(
uint amountIn,
uint reserveIn,
uint reserveOut
) external pure returns (uint amountOut);
}

contract getAmountOut {
// Same address just for testing purposes
address UNISWAP_ROUTER =
address(0x96871914D0F4354A79B1E4651b464351e093b737);
address UNISWAP_FACTORY =
address(0x96871914D0F4354A79B1E4651b464351e093b737);
address USDC = address(0x96871914D0F4354A79B1E4651b464351e093b737);
address WETH = address(0x96871914D0F4354A79B1E4651b464351e093b737);

function getEthUsdPrice() public view returns (uint256) {
address pairAddress = IUniswapV2Factory(UNISWAP_FACTORY).getPair(
USDC,
WETH
);
require(pairAddress != address(0x00), "pair not found");
IUniswapV2Pair pair = IUniswapV2Pair(pairAddress);
(uint256 left, uint256 right, ) = pair.getReserves();
(uint256 usdcReserves, uint256 ethReserves) = (USDC < WETH)
? (left, right)
: (right, left);
uint8 ethDecimals = IERC20(WETH).decimals();
//uint8 usdcDecimals = ERC20(USDC).decimals();
//returns price in 6 decimals
return
IUniswapV2Router01(UNISWAP_ROUTER).getAmountOut(
10 ** ethDecimals,
ethReserves,
usdcReserves
);
talfao marked this conversation as resolved.
Show resolved Hide resolved
}
}
Binary file not shown.
3 changes: 3 additions & 0 deletions tests/e2e/detectors/test_detectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -1869,6 +1869,9 @@ def id_test(test_item: Test):
"C.sol",
"0.8.16",
),
Test(all_detectors.SpotPriceDetector, "spot_price_getReserves.sol", "0.6.12"),
Test(all_detectors.SpotPriceDetector, "spot_price_balanceOf.sol", "0.8.20"),
Test(all_detectors.SpotPriceDetector, "spot_price_getAmountOut.sol", "0.8.20"),
]

GENERIC_PATH = "/GENERIC_PATH"
Expand Down