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

feat: Solidity static analysis #38

Merged
merged 7 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 19 additions & 1 deletion .github/workflows/solidity-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,22 @@ jobs:
uses: actions/upload-artifact@v3
with:
name: gas-report
path: ${{ env.working-directory }}/previous_gas_report.txt
path: ${{ env.working-directory }}/previous_gas_report.txt

- name: Static analysis
uses: crytic/slither-action@v0.4.0
id: slither
with:
target: 'solidity/'
slither-config: 'solidity/slither.config.json'
slither-args: --exclude incorrect-exponentiation
sarif: results.sarif
fail-on: none
compile-command: |
forge build
ignore-compile: false

- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: ${{ steps.slither.outputs.sarif }}
6 changes: 6 additions & 0 deletions solidity/slither.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"filter_paths": "lib|node_modules|test|Mock*|Test*",
"compile_force_framework": "foundry",
"exclude_informational": true,
"no_fail": true
}
17 changes: 15 additions & 2 deletions solidity/src/Pragma.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import "./PragmaDecoder.sol";
import "./libraries/EventsLib.sol";
import "./libraries/ErrorsLib.sol";
import "./interfaces/PragmaStructs.sol";
import "./libraries/DataParser.sol";

/// @title Pragma
/// @author Pragma Labs
Expand All @@ -16,7 +17,6 @@ contract Pragma is IPragma, PragmaDecoder {
/* STORAGE */
uint256 public validTimePeriodSeconds;
uint256 public singleUpdateFeeInWei;
mapping(bytes32 => uint64) public latestDataInfoPublishTime;

constructor(
address _hyperlane,
Expand Down Expand Up @@ -111,7 +111,20 @@ contract Pragma is IPragma, PragmaDecoder {

/// @inheritdoc IPragma
function dataFeedExists(bytes32 id) external view returns (bool) {
return (latestDataInfoPublishTime[id] != 0);
FeedType feedType = DataParser.safeCastToFeedType(uint8(id[0]));
if (feedType == FeedType.SpotMedian) {
return (spotMedianFeeds[id].metadata.timestamp != 0);
} else if (feedType == FeedType.Twap) {
return (twapFeeds[id].metadata.timestamp != 0);
} else if (feedType == FeedType.RealizedVolatility) {
return (rvFeeds[id].metadata.timestamp != 0);
} else if (feedType == FeedType.Options) {
return (optionsFeeds[id].metadata.timestamp != 0);
} else if (feedType == FeedType.Perpetuals) {
return (perpFeeds[id].metadata.timestamp != 0);
} else {
revert ErrorsLib.InvalidDataFeedType();
}
}

function getValidTimePeriod() public view returns (uint256) {
Expand Down
2 changes: 1 addition & 1 deletion solidity/src/PragmaDecoder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ contract PragmaDecoder {
virtual
returns (bool valid, uint256 endOffset)
{
return MerkleTree.isProofValid(encodedProof, offset, root, leafData);
(valid, endOffset) = MerkleTree.isProofValid(encodedProof, offset, root, leafData);
}

function extractDataInfoFromUpdate(bytes calldata encoded, uint256 offset, bytes32 checkpointRoot)
Expand Down
71 changes: 69 additions & 2 deletions solidity/src/interfaces/PragmaStructs.sol
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ struct RealizedVolatility {
uint256 timePeriod;
uint256 startPrice;
uint256 endPrice;
uint256 high_price;
uint256 low_price;
uint256 highPrice;
uint256 lowPrice;
uint256 numberOfDataPoints;
}

Expand Down Expand Up @@ -109,3 +109,70 @@ enum FeedType {
Options,
Perpetuals
}

library StructsInitializers {
function initializeParsedData() internal pure returns (ParsedData memory) {
return ParsedData({
dataType: FeedType.SpotMedian,
spot: initializeSpotMedian(),
twap: initializeTwap(),
rv: initializeRV(),
options: initializeOptions(),
perp: initializePerpetuals()
});
}

function initializeMetadata() internal pure returns (Metadata memory) {
return Metadata({feedId: 0, timestamp: 0, numberOfSources: 0, decimals: 0});
}

function initializeSpotMedian() internal pure returns (SpotMedian memory) {
return SpotMedian({metadata: initializeMetadata(), price: 0, volume: 0});
}

function initializeTwap() internal pure returns (TWAP memory) {
return TWAP({
metadata: initializeMetadata(),
twapPrice: 0,
timePeriod: 0,
startPrice: 0,
endPrice: 0,
totalVolume: 0,
numberOfDataPoints: 0
});
}

function initializeRV() internal pure returns (RealizedVolatility memory) {
return RealizedVolatility({
metadata: initializeMetadata(),
volatility: 0,
timePeriod: 0,
startPrice: 0,
endPrice: 0,
highPrice: 0,
lowPrice: 0,
numberOfDataPoints: 0
});
}

function initializeOptions() internal pure returns (Options memory) {
return Options({
metadata: initializeMetadata(),
strikePrice: 0,
impliedVolatility: 0,
timeToExpiry: 0,
isCall: false,
underlyingPrice: 0,
optionPrice: 0,
delta: 0,
gamma: 0,
vega: 0,
theta: 0,
rho: 0
});
}

function initializePerpetuals() internal pure returns (Perp memory) {
return Perp({metadata: initializeMetadata(), markPrice: 0, fundingRate: 0, openInterest: 0, volume: 0});
}
}
24 changes: 12 additions & 12 deletions solidity/src/libraries/DataParser.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ library DataParser {

function parse(bytes memory data) internal pure returns (ParsedData memory) {
uint8 offset = 2; // type feed stored after asset class
uint16 rawDataType = data.toUint16(offset);
uint8 rawDataType = data.toUint8(offset);
FeedType dataType = safeCastToFeedType(rawDataType);

ParsedData memory parsedData;
ParsedData memory parsedData = StructsInitializers.initializeParsedData();
parsedData.dataType = dataType;
if (dataType == FeedType.SpotMedian) {
parsedData.spot = parseSpotData(data);
Expand All @@ -32,16 +32,16 @@ library DataParser {
return parsedData;
}

function safeCastToFeedType(uint16 rawDataType) internal pure returns (FeedType) {
if (rawDataType <= uint16(type(FeedType).max)) {
function safeCastToFeedType(uint8 rawDataType) internal pure returns (FeedType) {
if (rawDataType <= uint8(type(FeedType).max)) {
return FeedType(rawDataType);
} else {
revert ErrorsLib.InvalidDataFeedType();
}
}

function parseMetadata(bytes memory data, uint256 startIndex) internal pure returns (Metadata memory, uint256) {
Metadata memory metadata;
Metadata memory metadata = StructsInitializers.initializeMetadata();
uint256 index = startIndex;

metadata.feedId = bytes32(data.toUint256(index));
Expand All @@ -60,7 +60,7 @@ library DataParser {
}

function parseSpotData(bytes memory data) internal pure returns (SpotMedian memory) {
SpotMedian memory entry;
SpotMedian memory entry = StructsInitializers.initializeSpotMedian();
uint256 index = 0;

(entry.metadata, index) = parseMetadata(data, index);
Expand All @@ -74,7 +74,7 @@ library DataParser {
}

function parseTWAPData(bytes memory data) internal pure returns (TWAP memory) {
TWAP memory entry;
TWAP memory entry = StructsInitializers.initializeTwap();
uint256 index = 0;

(entry.metadata, index) = parseMetadata(data, index);
Expand All @@ -100,7 +100,7 @@ library DataParser {
}

function parseRealizedVolatilityData(bytes memory data) internal pure returns (RealizedVolatility memory) {
RealizedVolatility memory entry;
RealizedVolatility memory entry = StructsInitializers.initializeRV();
uint256 index = 0;

(entry.metadata, index) = parseMetadata(data, index);
Expand All @@ -117,10 +117,10 @@ library DataParser {
entry.endPrice = data.toUint256(index);
index += 32;

entry.high_price = data.toUint256(index);
entry.highPrice = data.toUint256(index);
index += 32;

entry.low_price = data.toUint256(index);
entry.lowPrice = data.toUint256(index);
index += 32;

entry.numberOfDataPoints = data.toUint256(index);
Expand All @@ -129,7 +129,7 @@ library DataParser {
}

function parseOptionsData(bytes memory data) internal pure returns (Options memory) {
Options memory entry;
Options memory entry = StructsInitializers.initializeOptions();
uint256 index = 0;

(entry.metadata, index) = parseMetadata(data, index);
Expand Down Expand Up @@ -170,7 +170,7 @@ library DataParser {
}

function parsePerpData(bytes memory data) internal pure returns (Perp memory) {
Perp memory entry;
Perp memory entry = StructsInitializers.initializePerpetuals();
uint256 index = 0;

(entry.metadata, index) = parseMetadata(data, index);
Expand Down
26 changes: 16 additions & 10 deletions solidity/test/DataParser.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ contract DataParserTest is Test {
abi.encodePacked(
uint16(0),
///CRYPTO
uint16(0), //SPOT
uint8(0), //SPOT
uint8(0), //VARIANT
bytes32("BTC/USD")
)
);
Expand Down Expand Up @@ -40,7 +41,8 @@ contract DataParserTest is Test {
abi.encodePacked(
uint16(0),
///CRYPTO
uint16(1), //TWAP
uint8(1), //TWAP
uint8(0), //VARIANT
bytes32("ETH/USD")
)
);
Expand Down Expand Up @@ -77,7 +79,8 @@ contract DataParserTest is Test {
abi.encodePacked(
uint16(0),
///CRYPTO
uint16(2), //RV
uint8(2), //RV
uint8(0), //VARIANT
bytes32("BTC/USD")
)
);
Expand All @@ -90,8 +93,8 @@ contract DataParserTest is Test {
uint256(86400), // timePeriod
uint256(34000 ether), // startPrice
uint256(36000 ether), // endPrice
uint256(37000 ether), // high_price
uint256(33000 ether), // low_price
uint256(37000 ether), // highPrice
uint256(33000 ether), // lowPrice
uint256(1440) // numberOfDataPoints
);

Expand All @@ -106,8 +109,8 @@ contract DataParserTest is Test {
assertEq(result.rv.timePeriod, 86400);
assertEq(result.rv.startPrice, 34000 ether);
assertEq(result.rv.endPrice, 36000 ether);
assertEq(result.rv.high_price, 37000 ether);
assertEq(result.rv.low_price, 33000 ether);
assertEq(result.rv.highPrice, 37000 ether);
assertEq(result.rv.lowPrice, 33000 ether);
assertEq(result.rv.numberOfDataPoints, 1440);
}

Expand All @@ -116,7 +119,8 @@ contract DataParserTest is Test {
abi.encodePacked(
uint16(0),
///CRYPTO
uint16(3), //Option
uint8(3), //Option
uint8(0), //VARIANT
bytes32("ETH/USD")
)
);
Expand Down Expand Up @@ -163,7 +167,8 @@ contract DataParserTest is Test {
abi.encodePacked(
uint16(0),
///CRYPTO
uint16(4), //PERP
uint8(4), //PERP
uint8(0), //VARIANT
bytes32("BTC/USD")
)
);
Expand Down Expand Up @@ -196,7 +201,8 @@ contract DataParserTest is Test {
abi.encodePacked(
uint16(0),
///CRYPTO
uint16(10), //Unkown data type
uint8(20), //Unkown data type
uint8(0),
bytes32("BTC/USD")
)
);
Expand Down
Loading
Loading