From cce287058e8bbe3581e091f5d2eae02d7d86c7c4 Mon Sep 17 00:00:00 2001 From: lourens linde <65844331+lokithe5th@users.noreply.github.com> Date: Wed, 13 Mar 2024 15:30:43 +0200 Subject: [PATCH 01/11] Feat: add custom config files for medusa --- .../medusa_EchidnaCollateralTokenTester.json | 88 +++++++++++++++++++ .../medusa_EchidnaDoomsdayTester.json | 88 +++++++++++++++++++ .../medusa_EchidnaEbtcFeedTester.json | 88 +++++++++++++++++++ .../medusa_EchidnaPriceFeedTester.json | 88 +++++++++++++++++++ ...{medusa.json => medusa_EchidnaTester.json} | 0 5 files changed, 352 insertions(+) create mode 100644 packages/contracts/medusa_EchidnaCollateralTokenTester.json create mode 100644 packages/contracts/medusa_EchidnaDoomsdayTester.json create mode 100644 packages/contracts/medusa_EchidnaEbtcFeedTester.json create mode 100644 packages/contracts/medusa_EchidnaPriceFeedTester.json rename packages/contracts/{medusa.json => medusa_EchidnaTester.json} (100%) diff --git a/packages/contracts/medusa_EchidnaCollateralTokenTester.json b/packages/contracts/medusa_EchidnaCollateralTokenTester.json new file mode 100644 index 000000000..9cd2c8966 --- /dev/null +++ b/packages/contracts/medusa_EchidnaCollateralTokenTester.json @@ -0,0 +1,88 @@ +{ + "fuzzing": { + "workers": 10, + "workerResetLimit": 50, + "timeout": 0, + "testLimit": 0, + "callSequenceLength": 100, + "corpusDirectory": "medusa", + "coverageEnabled": true, + "deploymentOrder": [ + "EchidnaCollateralTokenTester" + ], + "targetContracts": [ + "EchidnaCollateralTokenTester" + ], + "targetContractsBalances": [ + "0x4F68CA6D8CD91C6000000" + ], + "constructorArgs": {}, + "deployerAddress": "0x30000", + "senderAddresses": [ + "0x10000", + "0x20000", + "0x30000" + ], + "blockNumberDelayMax": 60480, + "blockTimestampDelayMax": 604800, + "blockGasLimit": 125000000, + "transactionGasLimit": 12500000, + "testing": { + "stopOnFailedTest": false, + "stopOnFailedContractMatching": false, + "stopOnNoTests": true, + "testAllContracts": false, + "traceAll": false, + "assertionTesting": { + "enabled": true, + "testViewMethods": true, + "panicCodeConfig": { + "failOnCompilerInsertedPanic": false, + "failOnAssertion": true, + "failOnArithmeticUnderflow": false, + "failOnDivideByZero": false, + "failOnEnumTypeConversionOutOfBounds": false, + "failOnIncorrectStorageAccess": false, + "failOnPopEmptyArray": false, + "failOnOutOfBoundsArrayAccess": false, + "failOnAllocateTooMuchMemory": false, + "failOnCallUninitializedVariable": false + } + }, + "propertyTesting": { + "enabled": true, + "testPrefixes": [ + "echidna_" + ] + }, + "optimizationTesting": { + "enabled": false, + "testPrefixes": [ + "optimize_" + ] + } + }, + "chainConfig": { + "codeSizeCheckDisabled": true, + "cheatCodes": { + "cheatCodesEnabled": true, + "enableFFI": false + } + } + }, + "compilation": { + "platform": "crytic-compile", + "platformConfig": { + "target": "contracts/TestContracts/invariants/echidna/EchidnaCollateralTokenTester.sol", + "solcVersion": "", + "exportDirectory": "", + "args": [ + "--solc-remaps", "@crytic/properties/=lib/properties/" + ] + } + }, + "logging": { + "level": "info", + "logDirectory": "" + } +} \ No newline at end of file diff --git a/packages/contracts/medusa_EchidnaDoomsdayTester.json b/packages/contracts/medusa_EchidnaDoomsdayTester.json new file mode 100644 index 000000000..9619a3dc7 --- /dev/null +++ b/packages/contracts/medusa_EchidnaDoomsdayTester.json @@ -0,0 +1,88 @@ +{ + "fuzzing": { + "workers": 10, + "workerResetLimit": 50, + "timeout": 0, + "testLimit": 0, + "callSequenceLength": 100, + "corpusDirectory": "medusa", + "coverageEnabled": true, + "deploymentOrder": [ + "EchidnaDoomsdayTester" + ], + "targetContracts": [ + "EchidnaDoomsdayTester" + ], + "targetContractsBalances": [ + "0x4F68CA6D8CD91C6000000" + ], + "constructorArgs": {}, + "deployerAddress": "0x30000", + "senderAddresses": [ + "0x10000", + "0x20000", + "0x30000" + ], + "blockNumberDelayMax": 60480, + "blockTimestampDelayMax": 604800, + "blockGasLimit": 125000000, + "transactionGasLimit": 12500000, + "testing": { + "stopOnFailedTest": false, + "stopOnFailedContractMatching": false, + "stopOnNoTests": true, + "testAllContracts": false, + "traceAll": false, + "assertionTesting": { + "enabled": true, + "testViewMethods": true, + "panicCodeConfig": { + "failOnCompilerInsertedPanic": false, + "failOnAssertion": true, + "failOnArithmeticUnderflow": false, + "failOnDivideByZero": false, + "failOnEnumTypeConversionOutOfBounds": false, + "failOnIncorrectStorageAccess": false, + "failOnPopEmptyArray": false, + "failOnOutOfBoundsArrayAccess": false, + "failOnAllocateTooMuchMemory": false, + "failOnCallUninitializedVariable": false + } + }, + "propertyTesting": { + "enabled": true, + "testPrefixes": [ + "echidna_" + ] + }, + "optimizationTesting": { + "enabled": false, + "testPrefixes": [ + "optimize_" + ] + } + }, + "chainConfig": { + "codeSizeCheckDisabled": true, + "cheatCodes": { + "cheatCodesEnabled": true, + "enableFFI": false + } + } + }, + "compilation": { + "platform": "crytic-compile", + "platformConfig": { + "target": "contracts/TestContracts/invariants/echidna/EchidnaDoomsdayTester.sol", + "solcVersion": "", + "exportDirectory": "", + "args": [ + "--solc-remaps", "@crytic/properties/=lib/properties/" + ] + } + }, + "logging": { + "level": "info", + "logDirectory": "" + } +} \ No newline at end of file diff --git a/packages/contracts/medusa_EchidnaEbtcFeedTester.json b/packages/contracts/medusa_EchidnaEbtcFeedTester.json new file mode 100644 index 000000000..b9639ec08 --- /dev/null +++ b/packages/contracts/medusa_EchidnaEbtcFeedTester.json @@ -0,0 +1,88 @@ +{ + "fuzzing": { + "workers": 10, + "workerResetLimit": 50, + "timeout": 0, + "testLimit": 0, + "callSequenceLength": 100, + "corpusDirectory": "medusa", + "coverageEnabled": true, + "deploymentOrder": [ + "EchidnaEbtcFeedTester" + ], + "targetContracts": [ + "EchidnaEbtcFeedTester" + ], + "targetContractsBalances": [ + "0x4F68CA6D8CD91C6000000" + ], + "constructorArgs": {}, + "deployerAddress": "0x30000", + "senderAddresses": [ + "0x10000", + "0x20000", + "0x30000" + ], + "blockNumberDelayMax": 60480, + "blockTimestampDelayMax": 604800, + "blockGasLimit": 125000000, + "transactionGasLimit": 12500000, + "testing": { + "stopOnFailedTest": false, + "stopOnFailedContractMatching": false, + "stopOnNoTests": true, + "testAllContracts": false, + "traceAll": false, + "assertionTesting": { + "enabled": true, + "testViewMethods": true, + "panicCodeConfig": { + "failOnCompilerInsertedPanic": false, + "failOnAssertion": true, + "failOnArithmeticUnderflow": false, + "failOnDivideByZero": false, + "failOnEnumTypeConversionOutOfBounds": false, + "failOnIncorrectStorageAccess": false, + "failOnPopEmptyArray": false, + "failOnOutOfBoundsArrayAccess": false, + "failOnAllocateTooMuchMemory": false, + "failOnCallUninitializedVariable": false + } + }, + "propertyTesting": { + "enabled": true, + "testPrefixes": [ + "echidna_" + ] + }, + "optimizationTesting": { + "enabled": false, + "testPrefixes": [ + "optimize_" + ] + } + }, + "chainConfig": { + "codeSizeCheckDisabled": true, + "cheatCodes": { + "cheatCodesEnabled": true, + "enableFFI": false + } + } + }, + "compilation": { + "platform": "crytic-compile", + "platformConfig": { + "target": "contracts/TestContracts/invariants/echidna/EchidnaEbtcFeedTester.sol", + "solcVersion": "", + "exportDirectory": "", + "args": [ + "--solc-remaps", "@crytic/properties/=lib/properties/" + ] + } + }, + "logging": { + "level": "info", + "logDirectory": "" + } +} \ No newline at end of file diff --git a/packages/contracts/medusa_EchidnaPriceFeedTester.json b/packages/contracts/medusa_EchidnaPriceFeedTester.json new file mode 100644 index 000000000..590e694c3 --- /dev/null +++ b/packages/contracts/medusa_EchidnaPriceFeedTester.json @@ -0,0 +1,88 @@ +{ + "fuzzing": { + "workers": 10, + "workerResetLimit": 50, + "timeout": 0, + "testLimit": 0, + "callSequenceLength": 100, + "corpusDirectory": "medusa", + "coverageEnabled": true, + "deploymentOrder": [ + "EchidnaPriceFeedTester" + ], + "targetContracts": [ + "EchidnaPriceFeedTester" + ], + "targetContractsBalances": [ + "0x4F68CA6D8CD91C6000000" + ], + "constructorArgs": {}, + "deployerAddress": "0x30000", + "senderAddresses": [ + "0x10000", + "0x20000", + "0x30000" + ], + "blockNumberDelayMax": 60480, + "blockTimestampDelayMax": 604800, + "blockGasLimit": 125000000, + "transactionGasLimit": 12500000, + "testing": { + "stopOnFailedTest": false, + "stopOnFailedContractMatching": false, + "stopOnNoTests": true, + "testAllContracts": false, + "traceAll": false, + "assertionTesting": { + "enabled": true, + "testViewMethods": true, + "panicCodeConfig": { + "failOnCompilerInsertedPanic": false, + "failOnAssertion": true, + "failOnArithmeticUnderflow": false, + "failOnDivideByZero": false, + "failOnEnumTypeConversionOutOfBounds": false, + "failOnIncorrectStorageAccess": false, + "failOnPopEmptyArray": false, + "failOnOutOfBoundsArrayAccess": false, + "failOnAllocateTooMuchMemory": false, + "failOnCallUninitializedVariable": false + } + }, + "propertyTesting": { + "enabled": true, + "testPrefixes": [ + "echidna_" + ] + }, + "optimizationTesting": { + "enabled": false, + "testPrefixes": [ + "optimize_" + ] + } + }, + "chainConfig": { + "codeSizeCheckDisabled": true, + "cheatCodes": { + "cheatCodesEnabled": true, + "enableFFI": false + } + } + }, + "compilation": { + "platform": "crytic-compile", + "platformConfig": { + "target": "contracts/TestContracts/invariants/echidna/EchidnaPriceFeedTester.sol", + "solcVersion": "", + "exportDirectory": "", + "args": [ + "--solc-remaps", "@crytic/properties/=lib/properties/" + ] + } + }, + "logging": { + "level": "info", + "logDirectory": "" + } +} \ No newline at end of file diff --git a/packages/contracts/medusa.json b/packages/contracts/medusa_EchidnaTester.json similarity index 100% rename from packages/contracts/medusa.json rename to packages/contracts/medusa_EchidnaTester.json From bf3ba34275f725da06d3042f289e4a11cb3a8880 Mon Sep 17 00:00:00 2001 From: wtj2021 Date: Thu, 13 Jun 2024 09:55:02 -0700 Subject: [PATCH 02/11] making internal handler functions virtual --- packages/contracts/contracts/LeverageMacroBase.sol | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/contracts/contracts/LeverageMacroBase.sol b/packages/contracts/contracts/LeverageMacroBase.sol index 656391b7d..55931a279 100644 --- a/packages/contracts/contracts/LeverageMacroBase.sol +++ b/packages/contracts/contracts/LeverageMacroBase.sol @@ -507,7 +507,7 @@ abstract contract LeverageMacroBase { } /// @dev Must be memory since we had to decode it - function _openCdpCallback(bytes memory data) internal { + function _openCdpCallback(bytes memory data) internal virtual { OpenCdpOperation memory flData = abi.decode(data, (OpenCdpOperation)); /** @@ -521,7 +521,7 @@ abstract contract LeverageMacroBase { ); } - function _openCdpForCallback(bytes memory data) internal { + function _openCdpForCallback(bytes memory data) internal virtual { OpenCdpForOperation memory flData = abi.decode(data, (OpenCdpForOperation)); /** @@ -537,7 +537,7 @@ abstract contract LeverageMacroBase { } /// @dev Must be memory since we had to decode it - function _closeCdpCallback(bytes memory data) internal { + function _closeCdpCallback(bytes memory data) internal virtual { CloseCdpOperation memory flData = abi.decode(data, (CloseCdpOperation)); // Initiator must be added by this contract, else it's not trusted @@ -545,7 +545,7 @@ abstract contract LeverageMacroBase { } /// @dev Must be memory since we had to decode it - function _adjustCdpCallback(bytes memory data) internal { + function _adjustCdpCallback(bytes memory data) internal virtual { AdjustCdpOperation memory flData = abi.decode(data, (AdjustCdpOperation)); borrowerOperations.adjustCdpWithColl( @@ -559,7 +559,7 @@ abstract contract LeverageMacroBase { ); } - function _claimSurplusCallback() internal { + function _claimSurplusCallback() internal virtual { borrowerOperations.claimSurplusCollShares(); } From 2da05f2d1354190eacf64ec256d2c5c5358e5fa2 Mon Sep 17 00:00:00 2001 From: wtj2021 Date: Tue, 18 Jun 2024 12:29:09 -0700 Subject: [PATCH 03/11] making doOperation virtual --- packages/contracts/contracts/LeverageMacroBase.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contracts/contracts/LeverageMacroBase.sol b/packages/contracts/contracts/LeverageMacroBase.sol index 55931a279..4581718fe 100644 --- a/packages/contracts/contracts/LeverageMacroBase.sol +++ b/packages/contracts/contracts/LeverageMacroBase.sol @@ -122,7 +122,7 @@ abstract contract LeverageMacroBase { LeverageMacroOperation calldata operation, PostOperationCheck postCheckType, PostCheckParams calldata checkParams - ) external { + ) external virtual { _assertOwner(); // Figure out the expected CDP ID using sortedCdps.toCdpId From 27c1d5b97856912cb9929be7f32d3ef879764a3c Mon Sep 17 00:00:00 2001 From: wtj2021 Date: Thu, 20 Jun 2024 11:51:33 -0700 Subject: [PATCH 04/11] removing fuzz workflow --- .github/workflows/invariant-test.yml | 38 ---------------------------- 1 file changed, 38 deletions(-) delete mode 100644 .github/workflows/invariant-test.yml diff --git a/.github/workflows/invariant-test.yml b/.github/workflows/invariant-test.yml deleted file mode 100644 index 6042a4529..000000000 --- a/.github/workflows/invariant-test.yml +++ /dev/null @@ -1,38 +0,0 @@ -on: - push: - branches: - - main - pull_request: - paths: - - "packages/contracts/**" - -name: invariant-test - -jobs: - check: - name: Invariant Test - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - invariant-test: - name: Run invariant test - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Create Recon Job - run: | - curl -XPOST \ - -H 'Content-Type: application/json' \ - -H 'x-api-key: ${{ env.apiKey }}' \ - --data '{"cmd":"${{ env.cmd }}","instanceType":"${{ env.instanceType }}","projectId":"${{ env.projectId }}","ref":"${{ env.ref }}","pullRequestNumber":"${{ env.pullRequestNumber }}"}' \ - https://app.fuzzy.fyi/api/job - env: - cmd: yarn && git submodule init && git submodule update && solc-select use 0.8.17 && cd packages/contracts/ && yarn echidna --test-mode assertion --test-limit 300000 - instanceType: c5.2xlarge - ref: ${{ github.head_ref || github.ref_name }} - pullRequestNumber: ${{ github.event.number }} - projectId: ${{ secrets.FUZZY_PROJECT_ID }} - apiKey: ${{ secrets.FUZZY_API_KEY }} From cc7c5a98d0b7277f6351a5f6babad57e85c02752 Mon Sep 17 00:00:00 2001 From: wtj2021 Date: Wed, 10 Jul 2024 16:52:45 -0700 Subject: [PATCH 05/11] c4 audit fixes --- .../contracts/contracts/LeverageMacroBase.sol | 6 +++--- .../SimplifiedDiamondLikeLeverage.t.sol | 20 +++++++++++-------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/packages/contracts/contracts/LeverageMacroBase.sol b/packages/contracts/contracts/LeverageMacroBase.sol index 4581718fe..fb864f68a 100644 --- a/packages/contracts/contracts/LeverageMacroBase.sol +++ b/packages/contracts/contracts/LeverageMacroBase.sol @@ -279,11 +279,11 @@ abstract contract LeverageMacroBase { // Early return return; } else if (check.operator == Operator.gte) { - require(check.value >= valueToCheck, "!LeverageMacroReference: gte post check"); + require(valueToCheck >= check.value, "!LeverageMacroBase: gte post check"); } else if (check.operator == Operator.lte) { - require(check.value <= valueToCheck, "!LeverageMacroReference: let post check"); + require(valueToCheck <= check.value, "!LeverageMacroBase: lte post check"); } else if (check.operator == Operator.equal) { - require(check.value == valueToCheck, "!LeverageMacroReference: equal post check"); + require(check.value == valueToCheck, "!LeverageMacroBase: equal post check"); } else { revert("Operator not found"); } diff --git a/packages/contracts/foundry_test/SimplifiedDiamondLikeLeverage.t.sol b/packages/contracts/foundry_test/SimplifiedDiamondLikeLeverage.t.sol index 355a77c3c..33d42ec89 100644 --- a/packages/contracts/foundry_test/SimplifiedDiamondLikeLeverage.t.sol +++ b/packages/contracts/foundry_test/SimplifiedDiamondLikeLeverage.t.sol @@ -453,7 +453,8 @@ contract SimplifiedDiamondLikeLeverageTests is eBTCBaseInvariants { uint256 debt = 1e18; uint256 margin = 5 ether; uint256 flAmount = _debtToCollateral(debt); - uint256 totalCollateral = ((flAmount + margin) * 9995) / 1e4; + // remove 3 basis points from flAmount to account for flash loan fee + uint256 totalCollateral = (flAmount * 9997 / 10000) + margin; LeverageMacroBase.OpenCdpForOperation memory cdp; @@ -477,6 +478,13 @@ contract SimplifiedDiamondLikeLeverageTests is eBTCBaseInvariants { address(eBTCToken), address(collateral), debt + ), + _postCheckParams: _getPostCheckParams( + bytes32(0), + debt, + // expected collateral should exclude LIQUIDATOR_REWARD and be converted to shares + collateral.getSharesByPooledEth(totalCollateral - borrowerOperations.LIQUIDATOR_REWARD()), + ICdpManagerData.Status.active ) }); } @@ -505,7 +513,8 @@ contract SimplifiedDiamondLikeLeverageTests is eBTCBaseInvariants { LeverageMacroBase.OpenCdpForOperation memory _cdp, uint256 _flAmount, uint256 _stEthBalance, - bytes memory _exchangeData + bytes memory _exchangeData, + LeverageMacroBase.PostCheckParams memory _postCheckParams ) internal { LeverageMacroBase.LeverageMacroOperation memory op; @@ -521,12 +530,7 @@ contract SimplifiedDiamondLikeLeverageTests is eBTCBaseInvariants { _flAmount, op, LeverageMacroBase.PostOperationCheck.openCdp, - _getPostCheckParams( - bytes32(0), - _cdp.eBTCToMint, - _cdp.stETHToDeposit, - ICdpManagerData.Status.active - ) + _postCheckParams ); } From eff15eb4842ee07bd7dc5edf57aef8786beb901d Mon Sep 17 00:00:00 2001 From: wtj2021 Date: Wed, 10 Jul 2024 17:06:58 -0700 Subject: [PATCH 06/11] lint --- .../foundry_test/SimplifiedDiamondLikeLeverage.t.sol | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/contracts/foundry_test/SimplifiedDiamondLikeLeverage.t.sol b/packages/contracts/foundry_test/SimplifiedDiamondLikeLeverage.t.sol index 33d42ec89..7e7c2526d 100644 --- a/packages/contracts/foundry_test/SimplifiedDiamondLikeLeverage.t.sol +++ b/packages/contracts/foundry_test/SimplifiedDiamondLikeLeverage.t.sol @@ -454,7 +454,7 @@ contract SimplifiedDiamondLikeLeverageTests is eBTCBaseInvariants { uint256 margin = 5 ether; uint256 flAmount = _debtToCollateral(debt); // remove 3 basis points from flAmount to account for flash loan fee - uint256 totalCollateral = (flAmount * 9997 / 10000) + margin; + uint256 totalCollateral = ((flAmount * 9997) / 10000) + margin; LeverageMacroBase.OpenCdpForOperation memory cdp; @@ -483,7 +483,9 @@ contract SimplifiedDiamondLikeLeverageTests is eBTCBaseInvariants { bytes32(0), debt, // expected collateral should exclude LIQUIDATOR_REWARD and be converted to shares - collateral.getSharesByPooledEth(totalCollateral - borrowerOperations.LIQUIDATOR_REWARD()), + collateral.getSharesByPooledEth( + totalCollateral - borrowerOperations.LIQUIDATOR_REWARD() + ), ICdpManagerData.Status.active ) }); From 8796a9503b8a580393c002ef8efc1013f761777c Mon Sep 17 00:00:00 2001 From: wtj2021 Date: Wed, 10 Jul 2024 21:28:58 -0700 Subject: [PATCH 07/11] adding submit to stETH mock --- .../TestContracts/CollateralTokenTester.sol | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/contracts/contracts/TestContracts/CollateralTokenTester.sol b/packages/contracts/contracts/TestContracts/CollateralTokenTester.sol index 40544e5c7..32b11278b 100644 --- a/packages/contracts/contracts/TestContracts/CollateralTokenTester.sol +++ b/packages/contracts/contracts/TestContracts/CollateralTokenTester.sol @@ -40,6 +40,8 @@ contract CollateralTokenTester is ICollateralToken, ICollateralTokenOracle, Owna uint256 private slotsPerEpoch = 32; uint256 private secondsPerSlot = 12; + bool public submitShouldRevert; + receive() external payable { deposit(); } @@ -53,6 +55,18 @@ contract CollateralTokenTester is ICollateralToken, ICollateralTokenOracle, Owna emit Deposit(msg.sender, msg.value, _share); } + function submit(address _referral) public payable returns (uint256) { + if (submitShouldRevert) { + revert(); + } + + deposit(); + } + + function setSubmitShouldRevert(bool _submitShouldRevert) public { + submitShouldRevert = _submitShouldRevert; + } + /// @dev Deposit collateral without ether for testing purposes function forceDeposit(uint256 ethToDeposit) external { if (!isUncappedMinter[msg.sender]) { From 3ff87f4033122145d41be8765c9d813a651012cd Mon Sep 17 00:00:00 2001 From: wtj2021 Date: Thu, 11 Jul 2024 07:10:07 -0700 Subject: [PATCH 08/11] reduce foundry logging --- .github/workflows/foundry-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/foundry-test.yml b/.github/workflows/foundry-test.yml index 5320e16ad..f4a882a36 100644 --- a/.github/workflows/foundry-test.yml +++ b/.github/workflows/foundry-test.yml @@ -44,4 +44,4 @@ jobs: - name: Foundry Test working-directory: ./packages/contracts/ - run: forge test -vvv ${{ github.base_ref == 'main' && '--fuzz-runs 200' || '' }} \ No newline at end of file + run: forge test -vv ${{ github.base_ref == 'main' && '--fuzz-runs 200' || '' }} \ No newline at end of file From c2395917587efcff13f6423ad476edc73ecfa6ec Mon Sep 17 00:00:00 2001 From: wtj2021 Date: Thu, 11 Jul 2024 07:38:57 -0700 Subject: [PATCH 09/11] reducing test gas usage --- packages/contracts/foundry_test/CDPManager.redemptions.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contracts/foundry_test/CDPManager.redemptions.t.sol b/packages/contracts/foundry_test/CDPManager.redemptions.t.sol index 831daa59c..63dcd690c 100644 --- a/packages/contracts/foundry_test/CDPManager.redemptions.t.sol +++ b/packages/contracts/foundry_test/CDPManager.redemptions.t.sol @@ -87,7 +87,7 @@ contract CDPManagerRedemptionsTest is eBTCBaseInvariants { } function testMultipleRedemption(uint256 _cdpNumber, uint256 _collAmt) public { - _cdpNumber = bound(_cdpNumber, 2, 1000); + _cdpNumber = bound(_cdpNumber, 2, 500); _collAmt = bound(_collAmt, 22e17 + 1, 10000e18); uint256 _price = priceFeedMock.getPrice(); From 94f86a22897437bc217b65c420ea8ddb60887cd0 Mon Sep 17 00:00:00 2001 From: wtj2021 Date: Mon, 15 Jul 2024 20:52:35 -0700 Subject: [PATCH 10/11] working on secondary oracle adapter --- .../contracts/contracts/ChronicleAdapter.sol | 30 +++++++++++++ .../contracts/Interfaces/IChronicle.sol | 17 +++++++ .../TestContracts/MockAggregator.sol | 4 ++ .../foundry_test/ChronicleAdapter.t.sol | 44 +++++++++++++++++++ 4 files changed, 95 insertions(+) create mode 100644 packages/contracts/contracts/ChronicleAdapter.sol create mode 100644 packages/contracts/contracts/Interfaces/IChronicle.sol create mode 100644 packages/contracts/foundry_test/ChronicleAdapter.t.sol diff --git a/packages/contracts/contracts/ChronicleAdapter.sol b/packages/contracts/contracts/ChronicleAdapter.sol new file mode 100644 index 000000000..4179e108d --- /dev/null +++ b/packages/contracts/contracts/ChronicleAdapter.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +import {IPriceFetcher} from "./Interfaces/IOracleCaller.sol"; +import {IChronicle} from "./Interfaces/IChronicle.sol"; + +contract ChronicleAdapter is IPriceFetcher { + uint256 public constant MAX_STALENESS = 24 hours; + uint256 public constant ADAPTER_PRECISION = 1e18; + + address public immutable BTC_STETH_FEED; + uint256 public immutable FEED_PRECISION; + + constructor(address _btcStEthFeed) { + BTC_STETH_FEED = _btcStEthFeed; + + uint256 feedDecimals = IChronicle(BTC_STETH_FEED).decimals(); + require(feedDecimals > 0 && feedDecimals <= 18); + + FEED_PRECISION = 10 ** feedDecimals; + } + + function fetchPrice() external returns (uint256) { + (uint256 price, uint256 age) = IChronicle(BTC_STETH_FEED).readWithAge(); + uint256 staleness = block.timestamp - age; + if (staleness > MAX_STALENESS) revert("ChronicleAdapter: stale price"); + + return (price * ADAPTER_PRECISION) / FEED_PRECISION; + } +} diff --git a/packages/contracts/contracts/Interfaces/IChronicle.sol b/packages/contracts/contracts/Interfaces/IChronicle.sol new file mode 100644 index 000000000..dca6a6e79 --- /dev/null +++ b/packages/contracts/contracts/Interfaces/IChronicle.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +/// @title IChronicle +/// @author chronicleprotocol (https://github.com/chronicleprotocol/chronicle-std/blob/ea9afe78a1d33245afcdbcc3f530ee9cbd7cde28/src/IChronicle.sol) +/// @notice Partial interface for Chronicle Protocol's oracle products. +interface IChronicle { + /// @notice Returns the oracle's current value and its age. + /// @dev Reverts if no value set. + /// @return value The oracle's current value. + /// @return age The value's age. + function readWithAge() external view returns (uint256 value, uint256 age); + + /// @notice Returns the oracle's decimals. + /// @return The decimals of the oracle. + function decimals() external view returns (uint8); +} diff --git a/packages/contracts/contracts/TestContracts/MockAggregator.sol b/packages/contracts/contracts/TestContracts/MockAggregator.sol index 946d2a677..c0eb81dfb 100644 --- a/packages/contracts/contracts/TestContracts/MockAggregator.sol +++ b/packages/contracts/contracts/TestContracts/MockAggregator.sol @@ -131,6 +131,10 @@ contract MockAggregator is AggregatorV3Interface { return (prevRoundId, prevPrice, 0, updateTime, 0); } + function readWithAge() external view returns (uint256 value, uint256 age) { + return (uint256(price), updateTime); + } + function description() external pure override returns (string memory) { return ""; } diff --git a/packages/contracts/foundry_test/ChronicleAdapter.t.sol b/packages/contracts/foundry_test/ChronicleAdapter.t.sol new file mode 100644 index 000000000..da145f2ab --- /dev/null +++ b/packages/contracts/foundry_test/ChronicleAdapter.t.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.17; + +import "forge-std/Test.sol"; +import {MockAggregator} from "../contracts/TestContracts/MockAggregator.sol"; +import {ChronicleAdapter} from "../contracts/ChronicleAdapter.sol"; + +contract ChainlinkAdapterTest is Test { + MockAggregator internal stEthBtcAggregator; + ChronicleAdapter internal chronicleAdapter; + + constructor() {} + + function testSuccessPrecision8() public { + stEthBtcAggregator = new MockAggregator(8); + chronicleAdapter = new ChronicleAdapter(address(stEthBtcAggregator)); + + stEthBtcAggregator.setUpdateTime(block.timestamp); + stEthBtcAggregator.setPrice(5341495); + + assertEq(chronicleAdapter.fetchPrice(), 53414950000000000); + } + + function testSuccessPrecision18() public { + stEthBtcAggregator = new MockAggregator(18); + chronicleAdapter = new ChronicleAdapter(address(stEthBtcAggregator)); + + stEthBtcAggregator.setUpdateTime(block.timestamp); + stEthBtcAggregator.setPrice(53414952714851023); + + assertEq(chronicleAdapter.fetchPrice(), 53414952714851023); + } + + function testFailureFreshness() public { + stEthBtcAggregator = new MockAggregator(18); + chronicleAdapter = new ChronicleAdapter(address(stEthBtcAggregator)); + + stEthBtcAggregator.setUpdateTime(block.timestamp - 24 hours - 1); + stEthBtcAggregator.setPrice(100e18); + + vm.expectRevert("ChronicleAdapter: stale price"); + chronicleAdapter.fetchPrice(); + } +} From 1a17876b00e291345382e45c02818e3ea99c127f Mon Sep 17 00:00:00 2001 From: wtj2021 Date: Mon, 15 Jul 2024 20:56:03 -0700 Subject: [PATCH 11/11] comments --- packages/contracts/contracts/ChronicleAdapter.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/contracts/contracts/ChronicleAdapter.sol b/packages/contracts/contracts/ChronicleAdapter.sol index 4179e108d..954356580 100644 --- a/packages/contracts/contracts/ChronicleAdapter.sol +++ b/packages/contracts/contracts/ChronicleAdapter.sol @@ -4,6 +4,8 @@ pragma solidity 0.8.17; import {IPriceFetcher} from "./Interfaces/IOracleCaller.sol"; import {IChronicle} from "./Interfaces/IChronicle.sol"; +/// @notice Chronicle oracle adapter for EbtcFeed +/// @notice https://etherscan.io/address/0x02238bb0085395ae52cd4755456891fc2fd5934d contract ChronicleAdapter is IPriceFetcher { uint256 public constant MAX_STALENESS = 24 hours; uint256 public constant ADAPTER_PRECISION = 1e18;