Skip to content

Commit 534493f

Browse files
authored
[WORKFLOW/ETHEREUM-CONTRACTS] Fix Up Coverage (#1348)
* fix up coverage tests - Add some tests to be pedantic for BatchLiquidator/SuperAppBaseCFA foundry - Recover CFAv1Library and IDAv1Library tests - fix typo in SuperToken.sol - extract more foundry specific tests * ci.feature codecov * remove extraction step * fix up coverage - add tests for supertokenlibv1 coverage - rename idaV1Lib => superTokenLibIDA and cfaV1Lib => superTokenLibCFA * add comments for reason for skipping coverage * cleanup - add back extraction - move ignore list to codecov.yml - remove skipFiles from solcover.js - add test for SF_GOV_INVALID_LIQUIDATION_OR_PATRICIAN_PERIOD - add safegas prop test - try out separate upload to allow codecov to merge on their end * >= 63 the multiplication takes some gas * bring back extract n merge * it likes the skipFiles no extract step + add back skipFiles * extraction required * safe gas * out of gas consistency * remove safe gas lib test * cleanup extraction! * Revert "cleanup extraction!" This reverts commit 861240c. * cleaner extraction - extract out only packages/ethereum-contracts/contracts for coverage - remove files we don't care about for coverage (see .solcover.js) * remove mocks
1 parent 5c46728 commit 534493f

18 files changed

+2851
-321
lines changed

.github/workflows/ci.canary.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,9 @@ jobs:
140140
- name: Clean up and merge coverage artifacts
141141
run: |
142142
# extract coverage for NFT contracts from forge coverage
143-
lcov -e lcov.info -o lcov.info 'packages/ethereum-contracts/contracts/superfluid/*NFT*.sol'
143+
lcov -e lcov.info 'packages/ethereum-contracts/contracts/*' -o lcov.info
144+
# remove mocks, base super app, test and deployer contracts (see .solcover.js)
145+
lcov -r lcov.info 'packages/ethereum-contracts/contracts/mocks/*' 'packages/ethereum-contracts/contracts/apps/*Base*' 'packages/ethereum-contracts/contracts/utils/*Test*' 'packages/ethereum-contracts/contracts/utils/*Deployer*' -o lcov.info
144146
# merge hardhat and forge coverage files
145147
lcov -a lcov.info -a packages/ethereum-contracts/coverage/lcov.info -o packages/ethereum-contracts/coverage/lcov.info
146148

.github/workflows/ci.feature.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,26 @@ jobs:
118118
run: |
119119
yarn workspace @superfluid-finance/ethereum-contracts test-coverage
120120
121+
- name: Install lcov
122+
run: sudo apt-get -y install lcov
123+
124+
- name: Clean up and merge coverage artifacts
125+
run: |
126+
# extract coverage for NFT contracts from forge coverage
127+
lcov -e lcov.info 'packages/ethereum-contracts/contracts/*' -o lcov.info
128+
# remove mocks, base super app, test and deployer contracts (see .solcover.js)
129+
lcov -r lcov.info 'packages/ethereum-contracts/contracts/mocks/*' 'packages/ethereum-contracts/contracts/apps/*Base*' 'packages/ethereum-contracts/contracts/utils/*Test*' 'packages/ethereum-contracts/contracts/utils/*Deployer*' -o lcov.info
130+
# merge hardhat and forge coverage files
131+
lcov -a lcov.info -a packages/ethereum-contracts/coverage/lcov.info -o packages/ethereum-contracts/coverage/lcov.info
132+
133+
- name: Upload ethereum-contracts-coverage to codecov
134+
uses: codecov/codecov-action@v2
135+
with:
136+
files: lcov.info
137+
name: ethereum-contracts-coverage
138+
flags: ethereum-contracts
139+
fail_ci_if_error: true
140+
121141
test-hot-fuzz:
122142
uses: ./.github/workflows/call.test-hot-fuzz.yml
123143
needs: [ check ]

codecov.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ parsers:
1515
macro: no
1616

1717
comment:
18-
layout: "reach,diff,flags,files,footer"
18+
layout: "reach, diff, flags, files"
1919
behavior: default
20-
require_changes: no
20+
require_changes: false # if true: only post the comment if coverage changes
21+
require_base: no # [yes :: must have a base report to post]
22+
require_head: yes # [yes :: must have a head report to post]
2123

2224
flags:
2325
ethereum-contracts:

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,12 @@
5858
"husky": "^8.0.2",
5959
"lerna": "^6.0.3",
6060
"node-jq": "^2.3.4",
61-
"shellcheck": "^2.2.0",
6261
"nodemon": "^2.0.20",
6362
"npm-run-all": "^4.1.5",
6463
"nyc": "^15.1.0",
6564
"prettier": "^2.7.1",
6665
"prettier-eslint": "^15.0.1",
66+
"shellcheck": "^2.2.0",
6767
"syncpack": "^8.3.9",
6868
"truffle": "^5.6.6",
6969
"ts-node": "^10.9.1",

packages/ethereum-contracts/.solcover.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
1+
// This file is used by solidity-coverage to configure the coverage report.
2+
13
module.exports = {
24
providerOptions: {
35
network_id: 8555,
46
},
57
skipFiles: [
68
"mocks/",
9+
// we skip the coverage for the SuperAppBase contracts because
10+
// we override the functions in child contracts
711
"apps/SuperAppBase.sol",
12+
"apps/SuperAppBaseCFA.sol",
13+
14+
// we skip the coverage for these contracts because they are
15+
// only used for testing
16+
"utils/SuperfluidFrameworkDeployer",
17+
"utils/SuperTokenDeployer",
18+
"utils/TestToken",
19+
"utils/TestResolver",
20+
"utils/TestGovernance",
821
],
922
mocha: {
1023
grep: "@skip-on-coverage", // Find everything with this tag

packages/ethereum-contracts/contracts/libs/SafeGasLibrary.sol

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,19 @@ pragma solidity 0.8.19;
55
/// @author Superfluid
66
/// @notice An internal library used to handle out of gas errors
77
library SafeGasLibrary {
8-
error OUT_OF_GAS();
8+
error OUT_OF_GAS(); // 0x20afada5
9+
10+
function _isOutOfGas(uint256 gasLeftBefore) internal view returns (bool) {
11+
return gasleft() <= gasLeftBefore / 63;
12+
}
913

1014
/// @dev A function used in the catch block to handle true out of gas errors
1115
/// @param gasLeftBefore the gas left before the try/catch block
12-
function _revertWhenOutOfGas(uint256 gasLeftBefore) internal {
16+
function _revertWhenOutOfGas(uint256 gasLeftBefore) internal view {
1317
// If the function actually runs out of gas, not just hitting the safety gas limit, we revert the whole transaction.
1418
// This solves an issue where the gas estimaton didn't provide enough gas by default for the function to succeed.
1519
// See https://medium.com/@wighawag/ethereum-the-concept-of-gas-and-its-dangers-28d0eb809bb2
16-
if (gasleft() <= gasLeftBefore / 63) {
20+
if (_isOutOfGas(gasLeftBefore)) {
1721
revert OUT_OF_GAS();
1822
}
1923
}

packages/ethereum-contracts/contracts/libs/SuperTokenDeployerLibrary.sol

Lines changed: 0 additions & 23 deletions
This file was deleted.

packages/ethereum-contracts/contracts/mocks/SuperTokenLibraryV1Mock.sol

Lines changed: 90 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,28 +74,112 @@ contract SuperTokenLibraryCFAMock {
7474
* CFA Operations
7575
*************************************************************************/
7676

77-
function createFlowTest(ISuperToken token, address receiver, int96 flowRate) public {
77+
function createFlowTest(
78+
ISuperToken token,
79+
address receiver,
80+
int96 flowRate
81+
) public {
7882
token.createFlow(receiver, flowRate);
7983
}
80-
function deleteFlowTest(ISuperToken token, address sender, address receiver) public {
84+
85+
function createFlowWithUserDataTest(
86+
ISuperToken token,
87+
address receiver,
88+
int96 flowRate,
89+
bytes memory userData
90+
) public {
91+
token.createFlow(receiver, flowRate, userData);
92+
}
93+
94+
function deleteFlowTest(
95+
ISuperToken token,
96+
address sender,
97+
address receiver
98+
) public {
8199
token.deleteFlow(sender, receiver);
82100
}
83101

84-
function updateFlowTest(ISuperToken token, address receiver, int96 flowRate) public {
102+
function deleteFlowWithUserDataTest(
103+
ISuperToken token,
104+
address sender,
105+
address receiver,
106+
bytes memory userData
107+
) public {
108+
token.deleteFlow(sender, receiver, userData);
109+
}
110+
111+
function updateFlowWithUserDataTest(
112+
ISuperToken token,
113+
address receiver,
114+
int96 flowRate,
115+
bytes memory userData
116+
) public {
117+
token.updateFlow(receiver, flowRate, userData);
118+
}
119+
120+
function updateFlowTest(
121+
ISuperToken token,
122+
address receiver,
123+
int96 flowRate
124+
) public {
85125
token.updateFlow(receiver, flowRate);
86126
}
87127

88-
function createFlowFromTest(ISuperToken token, address sender, address receiver, int96 flowRate) public {
128+
function createFlowFromTest(
129+
ISuperToken token,
130+
address sender,
131+
address receiver,
132+
int96 flowRate
133+
) public {
89134
token.createFlowFrom(sender, receiver, flowRate);
90135
}
91-
function deleteFlowFromTest(ISuperToken token, address sender, address receiver) public {
136+
137+
function createFlowFromWithUserDataTest(
138+
ISuperToken token,
139+
address sender,
140+
address receiver,
141+
int96 flowRate,
142+
bytes memory userData
143+
) public {
144+
token.createFlowFrom(sender, receiver, flowRate, userData);
145+
}
146+
147+
function deleteFlowFromTest(
148+
ISuperToken token,
149+
address sender,
150+
address receiver
151+
) public {
92152
token.deleteFlowFrom(sender, receiver);
93153
}
94154

95-
function updateFlowFromTest(ISuperToken token, address sender, address receiver, int96 flowRate) public {
155+
function deleteFlowFromWithUserDataTest(
156+
ISuperToken token,
157+
address sender,
158+
address receiver,
159+
bytes memory userData
160+
) public {
161+
token.deleteFlowFrom(sender, receiver, userData);
162+
}
163+
164+
function updateFlowFromTest(
165+
ISuperToken token,
166+
address sender,
167+
address receiver,
168+
int96 flowRate
169+
) public {
96170
token.updateFlowFrom(sender, receiver, flowRate);
97171
}
98172

173+
function updateFlowFromWithUserDataTest(
174+
ISuperToken token,
175+
address sender,
176+
address receiver,
177+
int96 flowRate,
178+
bytes memory userData
179+
) public {
180+
token.updateFlowFrom(sender, receiver, flowRate, userData);
181+
}
182+
99183
function setFlowPermissionsTest(
100184
ISuperToken token,
101185
address flowOperator,

packages/ethereum-contracts/contracts/superfluid/SuperToken.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
pragma solidity 0.8.19;
33

44
// solhint-disable max-states-count
5-
// Notes: SueperToken is rich with states, disable this default rule here.
5+
// Notes: SuperToken is rich with states, disable this default rule here.
66

77
import { UUPSProxiable } from "../upgradability/UUPSProxiable.sol";
88
import { IConstantFlowAgreementV1 } from "../interfaces/agreements/IConstantFlowAgreementV1.sol";

packages/ethereum-contracts/contracts/superfluid/Superfluid.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
pragma solidity 0.8.19;
33

44
import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol";
5-
65
import { UUPSProxiable } from "../upgradability/UUPSProxiable.sol";
76
import { UUPSProxy } from "../upgradability/UUPSProxy.sol";
7+
import { SafeGasLibrary } from "../libs/SafeGasLibrary.sol";
88

99
import {
1010
ISuperfluid,
@@ -1022,7 +1022,7 @@ contract Superfluid is
10221022
// "/ 63" is a magic to avoid out of gas attack.
10231023
// See https://medium.com/@wighawag/ethereum-the-concept-of-gas-and-its-dangers-28d0eb809bb2.
10241024
// A callback may use this to block the APP_RULE_NO_REVERT_ON_TERMINATION_CALLBACK jail rule.
1025-
if (gasleft() > gasLeftBefore / 63) {
1025+
if (!SafeGasLibrary._isOutOfGas(gasLeftBefore)) {
10261026
if (!isTermination) {
10271027
CallUtils.revertFromReturnedData(returnedData);
10281028
} else {

0 commit comments

Comments
 (0)